TripleDES: указанный ключ является известным слабым ключом для TripleDES и не может использоваться
Я использую класс.NET 3.0 System.Security.Cryptography.MACTripleDES
класс для генерации значения MAC. К сожалению, я работаю с аппаратным устройством, которое использует1111111111111111
" (в шестнадцатеричном виде) как ключ DES одинарной длины. System.Security.Cryptography
библиотека выполняет некоторую проверку работоспособности ключа и возвращает исключение, если вы пытаетесь использовать криптографически слабый ключ.
Например:
byte[] key = new byte[24];
for (int i = 0; i < key.Length; i++)
key[i] = 0x11;
byte[] data = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] computedMac = null;
using (MACTripleDES mac = new MACTripleDES(key))
{
computedMac = mac.ComputeHash(data);
}
бросает исключение
System.Security.Cryptography.CryptographicException : Specified key is a known weak key for 'TripleDES' and cannot be used.
Я знаю, что это не безопасный ключ. В производстве устройство будет мигать с новым, безопасным ключом. В то же время, есть ли способ запретить это исключение? Возможно app.config
или настройки реестра?
Изменить: Ключ на самом деле будет 101010... из-за алгоритма форсирования нечетной четности. Я не уверен, является ли это универсальным для алгоритма DES или просто требованием в работе по обработке платежей, которую я выполняю.
Редактировать 2: ответ Даниэля ниже содержит очень хорошую информацию о взломе.NET. К сожалению, я не смог решить свою проблему, используя эту технику, но там все еще есть некоторые интересные чтения.
8 ответов
Вместо использования MACTripleDES с повторяющейся клавишей DES для подделки одного DES-CBC-MAC, вы можете просто реализовать CBC-MAC самостоятельно поверх DESCryptoServiceProvider.
<1111111111111111> не является слабым ключом DES.
Это рассчитает DES CBC-MAC:
public static byte[] CalcDesMac(byte[] key, byte[] data){
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Key = key;
des.IV = new byte[8];
des.Padding = PaddingMode.Zeros;
MemoryStream ms = new MemoryStream();
using(CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write)){
cs.Write(data, 0, data.Length);
}
byte[] encryption = ms.ToArray();
byte[] mac = new byte[8];
Array.Copy(encryption, encryption.Length-8, mac, 0, 8);
PrintByteArray(encryption);
return mac;
}
Я бы не стал этого рекомендовать, но вы должны иметь возможность изменять IL-код, который проверяет наличие слабых ключей, используя Reflector и надстройку ReflexIL.
редактировать:
Извините, мне потребовалось некоторое время, чтобы загрузить все это на мою виртуальную машину (под управлением Ubuntu), и я не хотел связываться с Mono.
- Установите надстройку ReflexIL: Просмотр -> Надстройки -> Добавить
- Откройте ReflexIL: Инструменты -> ReflexIL v0.9
- Найдите функцию IsWeakKey(). (Вы можете использовать Поиск: F3)
- Появятся две функции, дважды щелкните ту, которая найдена в System.Security.Cryptography.TripleDES
- ReflexIL тоже должен был подойти. На вкладке "Инструкции" выполните прокрутку до строки 29 (смещение 63).
- Измените ldc.i4.1 на ldc.i4.0, это означает, что функция всегда будет возвращать false.
На панели сборок (слева) вы можете прокрутить вверх и щелкнуть "Common Language Runtime Library", панель ReflexIL даст вам возможность сохранить ее.
Важные заметки:
- Сначала создайте резервную копию вашей оригинальной сборки! (Mscorlib.dll)
- mscorlib.dll является подписанной сборкой, и вам потребуется.NET SDK (инструмент sn.exe) для ReflexIL, чтобы пропустить проверку. Я только что проверил это сам, у вас уже должно быть это с установленным Visual C#. Просто нажмите "Зарегистрировать для пропуска проверки (на этом компьютере)", когда будет предложено.
- Я не думаю, что я должен сказать вам, чтобы использовать это только на вашей машине разработки:)
Удачи! Если вам нужны дополнительные инструкции, пожалуйста, не стесняйтесь использовать поле для комментариев.
edit2:
Я не совсем понимаю!
http://i44.tinypic.com/2r6fwbo.png
Я полностью удалил проверку IsWeakKey из функции set_Key в сборке mscorlib. Я абсолютно уверен, что я изменил правильную функцию, и что я сделал это правильно. Дизассемблер отражателя больше не показывает чек. Забавно, однако, что Visual C# по-прежнему выдает то же исключение.
Это наводит меня на мысль, что mscorlib каким-то образом все еще нужно где-то кэшировать. Однако переименование mscorlib.dll в mscorlib.dll_ приводит к сбою MSVC#, поэтому он все равно должен зависеть от исходного dll.
Это довольно интересная вещь, но я думаю, что я дошел до того, что не имею понятия, что происходит, просто это не имеет никакого смысла! Смотрите прикрепленное изображение.:(
Edit3:
Я заметил у Олли, что в отличие от таких сборок, как mscoree, mscorsec и mscorwks; mscorlib.dll фактически не находится в: c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
Но вместо этого в том, что кажется несуществующим местоположением: C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\6d667f19d687361886990f3ca0f49816\mscorlib.ni.dll
Я думаю, что я что-то здесь упускаю:) Исследую это еще немного.
edit4:
Даже после того, как я пропатчил ВСЕ в IsWeakKey и поиграл с удалением и генерацией новых собственных образов (x.Ni.dll) mscorlib.dll с помощью "ngen.exe", я получаю то же исключение. Я должен отметить, что даже после удаления собственных образов mscorlib, он все еще использует mscorlib.ni.dll... Мех.
Я сдаюсь. Я надеюсь, что кто-то сможет ответить, что, черт возьми, происходит, потому что я точно не знаю.:)
Я узнал, что тебе нужно сделать. К счастью, есть метод, который создает ICryptoTranforms, который не проверяет наличие слабых ключей. Вам также нужно следить за базовым классом, так как он также проверяет работоспособность. Через рефлексию просто вызовите метод _NewEncryptor (вам нужно сделать немного больше рефлексии, но это идея).
К счастью, у MACTripleDES есть поле типа TripleDES, поэтому наследуем его от MACTripleDES и заменяем его посредством отражения в конструкторах. Я сделал всю работу за вас.
Я не могу проверить, что сгенерирован правильный MAC-адрес, но исключений нет. Кроме того, вы можете захотеть прокомментировать код и выполнить обработку исключений (ошибки отражения - например, если нет полей / методов) - но это SO; так что я не беспокоился.
using System;
using System.Reflection;
using System.Security.Cryptography;
using System.IO;
namespace DesHack
{
class Program
{
static void Main(string[] args)
{
byte[] key = new byte[24];
for (int i = 0; i < key.Length; i++)
key[i] = 0x11;
byte[] data = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] computedMac = null;
using (MACTripleDES mac = new MACTripleDESHack(key))
{
computedMac = mac.ComputeHash(data);
}
}
}
class MACTripleDESHack : MACTripleDES
{
TripleDES _desHack = new DesHack();
static FieldInfo _cspField = typeof(MACTripleDES).GetField("des", BindingFlags.Instance | BindingFlags.NonPublic);
public MACTripleDESHack()
: base()
{
RewireDes();
}
public MACTripleDESHack(byte[] rgbKey)
: base(rgbKey)
{
RewireDes();
}
private void RewireDes()
{
_cspField.SetValue(this, _desHack);
}
}
class DesHack : TripleDES
{
TripleDESCryptoServiceProvider _backing = new TripleDESCryptoServiceProvider();
static MethodInfo _newEncryptor;
static object _encrypt;
static object _decrypt;
public override int BlockSize
{
get
{
return _backing.BlockSize;
}
set
{
_backing.BlockSize = value;
}
}
public override int FeedbackSize
{
get
{
return _backing.FeedbackSize;
}
set
{
_backing.FeedbackSize = value;
}
}
// For these two we ALSO need to avoid
// the base class - it also checks
// for weak keys.
private byte[] _iv;
public override byte[] IV
{
get
{
return _iv;
}
set
{
_iv = value;
}
}
private byte[] _key;
public override byte[] Key
{
get
{
return _key;
}
set
{
_key = value;
}
}
public override int KeySize
{
get
{
return _backing.KeySize;
}
set
{
_backing.KeySize = value;
}
}
public override KeySizes[] LegalBlockSizes
{
get
{
return _backing.LegalBlockSizes;
}
}
public override KeySizes[] LegalKeySizes
{
get
{
return _backing.LegalKeySizes;
}
}
public override CipherMode Mode
{
get
{
return _backing.Mode;
}
set
{
_backing.Mode = value;
}
}
public override PaddingMode Padding
{
get
{
return _backing.Padding;
}
set
{
_backing.Padding = value;
}
}
static DesHack()
{
_encrypt = typeof(object).Assembly.GetType("System.Security.Cryptography.CryptoAPITransformMode").GetField("Encrypt").GetValue(null);
_decrypt = typeof(object).Assembly.GetType("System.Security.Cryptography.CryptoAPITransformMode").GetField("Decrypt").GetValue(null);
_newEncryptor = typeof(TripleDESCryptoServiceProvider).GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
}
public DesHack()
{
}
public override ICryptoTransform CreateDecryptor()
{
return CreateDecryptor(_key, _iv);
}
public override ICryptoTransform CreateEncryptor()
{
return CreateEncryptor(_key, _iv);
}
public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV)
{
// return this._NewEncryptor(rgbKey, base.ModeValue, rgbIV, base.FeedbackSizeValue, CryptoAPITransformMode.Decrypt);
return (ICryptoTransform) _newEncryptor.Invoke(_backing,
new object[] { rgbKey, ModeValue, rgbIV, FeedbackSizeValue, _decrypt });
}
public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV)
{
// return this._NewEncryptor(rgbKey, base.ModeValue, rgbIV, base.FeedbackSizeValue, CryptoAPITransformMode.Encrypt);
return (ICryptoTransform) _newEncryptor.Invoke(_backing,
new object[] { rgbKey, ModeValue, rgbIV, FeedbackSizeValue, _encrypt });
}
public override void GenerateIV()
{
_backing.GenerateIV();
}
public override void GenerateKey()
{
_backing.GenerateKey();
}
protected override void Dispose(bool disposing)
{
if (disposing)
((IDisposable) _backing).Dispose();
base.Dispose(disposing);
}
}
}
Есть отличное предложение использовать отражение на форумах MSDN
Я не эксперт по безопасности, но разве XOR вашего ключа с другим значением не будет достаточно для проверки работоспособности? Вы можете сделать это для своей отладочной версии (с надлежащим IFDEF), чтобы вы могли выполнить надлежащую проверку и удалить ее для своей версии или рабочей версии, где ключ будет достаточно сильным.
Решения, основанные на отражении, помогут вам решить проблему, но они грязные и злые. Никто еще не упомянул очень полезный метод: TripleDES.IsWeakKey
У меня была эта проблема, и я решил ее с помощью очень простой утилиты, которую я использую непосредственно перед тем, как установить ключ на свой CryptoServiceProvider:
private void MakeSecureKey(byte[] key)
{
while(TripleDES.IsWeakKey(key))
{
var sha = SHA256Managed.Create().ComputeHash(key);
Array.Copy(sha,key,key.Length);
}
}
Если вы вызываете его каждый раз, когда создаете шифратор или дешифровщик, это должно предотвратить сбой и всегда дает вам безопасный ключ.
Все очень просто (после просмотра кода из GitHub)
static bool TripleDES.IsWeakKey (Byte [] rgbKey)
Поскольку он статичен - легко проверить свой ключ
- Размер должен быть 16 или 24 байта (???). Почему они не могут поместить это в документацию
- Проверка кода на несколько простых повторений. Просто создайте случайные значения
См. Код по адресу: https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Security.Cryptography/TripleDES.cs
Dekel