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

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


person David Chappelle    schedule 13.04.2009    source источник
comment
(Re: ваше редактирование) DES и 3DES используют только верхние 7 бит каждого байта в ключе. Младший значащий бит обычно используется как бит четности.   -  person Rasmus Faber    schedule 14.04.2009
comment
Я бы дома так. Кажется странным, что что-то подобное будет применяться на уровне библиотеки. Люди, использующие шифрование, должны уметь использовать трудно угадываемые ключи, и библиотека не должна навязывать эти вещи.   -  person Kibbee    schedule 15.04.2009
comment
@Kibbee, слабый ключ в этом контексте не означает, что его трудно угадать. Это означает ключ, для которого указанный алгоритм демонстрирует некоторое вырожденное поведение, которое ослабляет его.   -  person caf    schedule 30.07.2009
comment
О четности: последний бит каждого байта фактически не используется алгоритмом, поэтому это не имеет большого значения. Обычно его используют как бит четности, но каждая реализация может делать то, что хочет.   -  person Paŭlo Ebermann    schedule 30.11.2011


Ответы (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;
    }
person Rasmus Faber    schedule 14.04.2009
comment
Это прекрасно работает. Я думаю, что я просто сосредоточился на классе MACTripleDES, который, если бы я подумал, действительно используется только для Triple DES, а не для одного DES. Жаль, что мне не понадобился год, чтобы прочитать ваш ответ! В итоге я реализовал CBC-MAC вручную. - person David Chappelle; 26.03.2010

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

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

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

Я смущен!

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

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

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

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

Я заметил в Олли, что в отличие от таких сборок, как 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

Я думаю, что я что-то здесь упускаю :) Буду исследовать это еще немного.

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

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

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

person Community    schedule 13.04.2009
comment
Довольно интересное решение! Я пропатчил свою mscorlib.dll, но поведение функции не изменилось. Эта dll где-то кешируется? Все, что я меняю, это ....\Microsoft.NET\Framework\v2.0.50737\mscorlib.dll. - person David Chappelle; 13.04.2009
comment
Я только что проверил код, вы правы. Буду разбираться и отчитываться :) - person Daniel Sloof; 13.04.2009
comment
Это странно, если не сказать больше. Теперь я изменил фактическую проверку IsWeakKey в set_Key(), и она делает то же самое. Он должен быть где-то закэширован. Буду исследовать дальше :) - person Daniel Sloof; 13.04.2009
comment
Ага. Он использует собственный образ mscorlib.dll, который можно создать с помощью инструмента ngen.exe (ngen.exe устанавливает mscorlib.dll). При первом запуске он сообщит вам, что собственный образ уже обновлен. (что не так). Даже если вы удалите собственный образ, он загрузит mscorlib.ni.dll. - person Daniel Sloof; 14.04.2009
comment
И удаление, а затем попытка установить mscorlib.dll (который был изменен рефлектором) приводит к тем же результатам. Выдается исключение, хотя эта проверка была полностью удалена. - person Daniel Sloof; 14.04.2009
comment
Я вполне уверен, что базовая реализация CryptoAPI также применяет ограничение слабого ключа. Таким образом, вам также придется исправлять advapi32.dll (что я бы не рекомендовал). - person Rasmus Faber; 14.04.2009
comment
@Rasmus, согласно отладчику, исключение генерируется из System.Security.Cryptography.TripleDES.set_Key... Я сам не знаком с внутренностями .NET (черт возьми, я никогда не касался C#/VB/ASP), так что извините меня за вопрос, почему, учитывая вышеизложенное, исключение по-прежнему выдается из этого места? - person Daniel Sloof; 14.04.2009
comment
Если исключение генерируется из set_Key, то вы, вероятно, правы в том, что проблема по-прежнему вызвана проверкой .NET IsWeakKey(). Мой комментарий был больше о том, что даже если вы получили код для использования вашей пропатченной сборки, код все равно потерпит неудачу, когда достигнет вызова CryptoAPI. - person Rasmus Faber; 14.04.2009
comment
О да. Я бы не знал об этом, если честно. :) Я думаю, это все еще было довольно интересно, к сожалению, я не мог полностью ответить на вопрос Дэвида. - person Daniel Sloof; 14.04.2009
comment
Я ценю усилия, которые вы приложили к этому. Я думаю, что просто воспользуюсь одной из неуправляемых криптографических библиотек с открытым исходным кодом и взаимодействую с ней. Инструмент Reflector довольно интересен (и страшен? Может быть, мне все-таки стоит запустить обфускатор...) - person David Chappelle; 14.04.2009
comment
@David, я должен еще раз отметить, что я не очень хорошо знаком с платформой .NET, но «dotfuscator» (я полагаю, поставляется по умолчанию в MSVS?) только запутывает имена классов, имена функций, имена переменных и так далее. Тем не менее, даже со сторонними обфускаторами это вопрос времени, когда ваш код будет изменен. - person Daniel Sloof; 14.04.2009
comment
Я посмотрел на это, прежде чем опубликовать, что это невозможно (думаю, я должен был указать без изменения системных сборок). Исключение выдается в классе TripleDESCryptoServiceProvider непосредственно перед вызовом его частного метода _NewEncryptor (или что-то в этом роде) - следовательно, set_Key не работает - person Dave Cluderay; 14.04.2009

Я узнал, что тебе нужно сделать. К счастью, есть доступный метод, который создает 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);
        }
    }
}
person Jonathan C Dickinson    schedule 14.04.2009

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

person Dave Cluderay    schedule 13.04.2009

Есть отличное предложение по использованию отражение на форумах MSDN

person user156397    schedule 14.08.2009

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

person Robert Kozak    schedule 13.04.2009
comment
Это действительно удовлетворило бы проверку работоспособности, но аппаратное устройство, с которым он работает, по-прежнему будет использовать исходный ключ. - person Daniel Sloof; 13.04.2009
comment
Ага. Я только что подумал об этом. Я думал об этом только с точки зрения кода и даже не рассматривал аппаратное обеспечение. Моя вина :) - person Robert Kozak; 13.04.2009

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

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

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

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

person captncraig    schedule 29.11.2011
comment
Это не очень помогает, если нужно взаимодействовать с устройством, для которого требуется определенный ключ. - person Paŭlo Ebermann; 30.11.2011
comment
Справедливо. Просто подумал, что добавлю его сюда, так как это ошибка номер один в Google, и она может быть полезна кому-то в немного другой ситуации. - person captncraig; 30.11.2011

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

статическое логическое значение TripleDES.IsWeakKey (Byte [] rgbKey)

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

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

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

Декель

person user2699139    schedule 20.08.2013