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)

Поскольку он статичен - легко проверить свой ключ

  1. Размер должен быть 16 или 24 байта (???). Почему они не могут поместить это в документацию
  2. Проверка кода на несколько простых повторений. Просто создайте случайные значения

См. Код по адресу: https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Security.Cryptography/TripleDES.cs

Dekel

Другие вопросы по тегам