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

StackOverflow https://stackoverflow.com/questions/744530

Вопрос

Я использую класс .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:Приведенный ниже ответ Дэниела содержит очень хорошую информацию о hacking .NET.К сожалению, я не смог решить свою проблему с помощью этой техники, но там все еще есть кое-что интересное для чтения.

Это было полезно?

Решение

Вместо использования 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-код, который проверяет наличие слабых ключей, используя Отражатель и надстройка РефлексИЛ

редактировать:

Извините, мне потребовалось некоторое время, чтобы загрузить все это на мою виртуальную машину (под управлением Ubuntu), и я не хотел связываться с Mono.

  • Установите надстройку ReflexIL:Вид -> Надстройки -> Добавить
  • Откройте ReflexIL:Инструменты -> ReflexIL v0.9
  • Найдите функцию IsWeakKey().(Вы можете использовать поиск:Ф3)
  • Появятся две функции, дважды щелкните ту, которая находится в System.Security.Cryptography.TripleDES.
  • ReflexIL тоже должен был появиться.На вкладке «Инструкции» прокрутите страницу до строки 29 (смещение 63).
  • Измените ldc.i4.1 на ldc.i4.0, это означает, что функция всегда будет возвращать false.

На панели сборок (слева) теперь вы можете прокрутить вверх и нажать «Библиотека времени выполнения общего языка», панель ReflexIL предоставит вам возможность сохранить ее.

Важные заметки:

  • Сначала сделайте резервную копию исходной сборки!(mscorlib.dll)
  • mscorlib.dll — это подписанная сборка, и вам понадобится .NET SDK (инструмент sn.exe) для ReflexIL, чтобы он пропустил проверку.Я только что проверил это сам, это уже должно быть у вас с установленным Visual C#.Просто нажмите «Зарегистрировать его для пропуска проверки (на этом компьютере)», когда вас об этом попросят.
  • Я не думаю, что мне нужно советовать вам использовать это только на вашей машине разработки :)

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

редактировать2:

Я в замешательстве!

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

Это наводит меня на мысль, что mscorlib каким-то образом все еще где-то кэшируется.Однако переименование mscorlib.dll в mscorlib.dll_ приводит к сбою MSVC#, поэтому он все равно должен зависеть от исходной dll.

Это довольно интересная вещь, но я думаю, что дошел до того, что понятия не имею, что происходит, это просто не имеет никакого смысла!См. прикрепленное изображение.:(

редактировать3:

Я заметил в Olly, что в отличие от таких сборок, как mscoree, mscorsec и mscorwks;mscorlib.dll на самом деле не находится в:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\

Но вместо этого в несуществующем месте:C:\WINDOWS\assembly ativeImages_v2.0.50727_32\mscorlib\6d667f19d687361886990f3ca0f49816\mscorlib.ni.dll

Кажется, я что-то упускаю :) Буду исследовать этот вопрос еще раз.

редактировать4:

Даже после того, как ВСЕ исправили в IsWeakKey и поигрались с удалением и созданием новых собственных образов (x.нет.dll) из mscorlib.dll с помощью «ngen.exe», я получаю то же исключение.Следует отметить, что даже после удаления собственных образов mscorlib он все еще использует mscorlib.ni.dll...Мех.

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

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

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

Я не могу проверить, что сгенерирован правильный MAC-адрес, но исключений не возникает.Кроме того, вы можете захотеть прокомментировать код и выполнить обработку исключений (ошибки отражения - например.если полей/методов нет) - но это ТАК;так что я не заморачивался.

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)

статический bool-файл утраивается.IsWeakKey(Байт[] rgbKey)

Поскольку он статичен, легко протестировать ваш ключ на нем

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

Смотрите код по адресу: https://github.com/mono/mono/blob/master/mcs/class/corlib/System .Безопасность.Криптография/TripleDES.cs

Декель

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top