Подтвердить что ты не робот

TripleDES: Указанный ключ является известным слабым ключом для "TripleDES" и не может использоваться

Я использую класс класса .NET 3.0 System.Security.Cryptography.MACTripleDES для генерации значения MAC. К сожалению, я работаю с аппаратным устройством, которое использует "1111111111111111" (как hex) как одноразмерный 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. К сожалению, я не смог решить свою проблему, используя эту технику, но там есть еще кое-что интересное.

4b9b3361

Ответ 1

Вместо использования 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;
    }

Ответ 2

Я бы не стал этого рекомендовать, но вы сможете изменить 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 С#. Просто нажмите "Зарегистрировать для пропуска проверки (на этом компьютере)", когда будет предложено.
  • Я не думаю, что я должен сказать вам, чтобы использовать это только на вашей машине разработки :)

Удачи! Если вам нужны дополнительные инструкции, пожалуйста, не стесняйтесь использовать поле для комментариев.

edit2:

Я не совсем понимаю!

Я полностью удалил проверку IsWeakKey из функции set_Key в сборке mscorlib. Я абсолютно уверен, что я изменил правильную функцию, и что я сделал это правильно. Демонтажер отражателя больше не показывает чек. Забавно, однако, что Visual С# по-прежнему выдает то же исключение.

Это наводит меня на мысль, что mscorlib каким-то образом все еще нужно где-то кэшировать. Однако переименование mscorlib.dll в mscorlib.dll_ приводит к сбою MSVС#, поэтому он все равно должен зависеть от исходного 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... Мех.

Я сдаюсь. Я надеюсь, что кто-то сможет ответить, что, черт возьми, происходит, потому что я точно не знаю. :)

Ответ 3

Я выяснил, что вам нужно сделать. К счастью, существует доступный метод, который создает ICryptoTranforms, который не проверяет наличие слабых клавиш. Вы также должны следить за базовым классом, так как он также выполняет проверки здравомыслия. Через отражение просто вызывают метод _NewEncryptor (вам нужно сделать немного больше отражения, но эта идея).

К счастью, MACTripleDES имеет поле типа TripleDES, поэтому выведите его из MACTripleDES и замените его через отражение в конструкторах. Я сделал для вас всю работу.

Я не могу проверить, что генерируется правильный MAC, но исключений не выбрано. Кроме того, вы можете захотеть doc прокомментировать код и обработать исключения (ошибки отражения - например, если полей/методов там нет), но это 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);
        }
    }
}

Ответ 4

К сожалению, поведение нельзя переопределить.

Ответ 5

Существует большое предложение с использованием отражение на форумах MSDN

Ответ 6

Я не эксперт по безопасности, но не мог бы XORing ваш ключ с другим значением быть достаточно, чтобы удовлетворить проверку здравомыслия? Вы можете сделать это для своей отладочной версии (с надлежащим IFDEF), чтобы вы могли сделать правильную проверку и удалить ее для своей версии или производственной версии, где ключ был бы достаточно сильным.

Ответ 7

Решения, основанные на отражении, помогут вам решить проблему, но они грязные и злые. Никто еще не упомянул очень полезный метод: TripleDES.IsWeakKey

У меня была эта проблема и она была решена с помощью очень простой утилиты, которую я использую непосредственно перед тем, как установить ключ на свой CryptoServiceProvider:

private void MakeSecureKey(byte[] key)
{
    while(TripleDES.IsWeakKey(key))
    {
        var sha = SHA256Managed.Create().ComputeHash(key);
        Array.Copy(sha,key,key.Length);
    }
}

Если вы вызываете его в любое время, когда вы делаете шифр или дешифратор, он должен предотвращать крах и всегда предоставлять вам безопасный ключ.

Ответ 8

Довольно просто (посмотрев код от GitHub)

static bool TripleDES.IsWeakKey(Byte [] rgbKey)

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

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

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

Декел