Triple DES:La clave especificada es una clave débil conocida para 'TripleDES' y no se puede utilizar

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

Pregunta

Estoy usando la clase .NET 3.0 System.Security.Cryptography.MACTripleDES clase para generar un valor MAC.Desafortunadamente, estoy trabajando con un dispositivo de hardware que usa "1111111111111111" (como hexadecimal) como clave DES de longitud única.El System.Security.Cryptography La biblioteca realiza algunas comprobaciones de cordura en la clave y devuelve una excepción si intenta utilizar una clave criptográficamente débil.

Por ejemplo:

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);
}

lanza una excepción

System.Security.Cryptography.CryptographicException : Specified key is a known weak key for 'TripleDES' and cannot be used.

Sé que esta no es una clave segura.En producción, el dispositivo se actualizará con una clave nueva y segura.Mientras tanto, ¿hay alguna forma de impedir que se lance esta excepción?Quizás un app.config o configuración del registro?

Editar:La clave en realidad sería 101010...debido al algoritmo que fuerza la paridad impar.No estoy seguro de si esto es universal para el algoritmo DES o simplemente un requisito en el trabajo de procesamiento de pagos que hago.

Edición 2:La respuesta de Daniel a continuación tiene muy buena información sobre cómo piratear .NET.Desafortunadamente, no pude resolver mi problema usando esta técnica, pero todavía hay algunas lecturas interesantes allí.

¿Fue útil?

Solución

En lugar de utilizar MACTripleDES con la clave DES repetida de falsificar un solo DES CBC-MAC, sólo podría aplicar CBC-MAC a sí mismo en la parte superior de DESCryptoServiceProvider .

<1111111111111111> no es una clave débil DES.

Esta calculará una 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;
    }

Otros consejos

Realmente no lo recomendaría, pero deberías poder modificar el código IL que busca claves débiles usando Reflector y el complemento ReflexIL

editar:

Lo siento, me tomó un tiempo cargarlo todo en mi máquina virtual (con Ubuntu) y no quería meterme con Mono.

  • Instale el complemento ReflexIL:Ver -> Complementos -> Agregar
  • Abrir ReflexIL:Herramientas -> ReflexIL v0.9
  • Busque la función IsWeakKey().(Puedes utilizar Buscar:F3)
  • Aparecerán dos funciones, haga doble clic en la que se encuentra en System.Security.Cryptography.TripleDES
  • ReflexIL también debería haber aparecido.En la pestaña Instrucciones, desplácese hasta la línea 29 (desplazamiento 63).
  • Cambie ldc.i4.1 a ldc.i4.0, esto significa que la función siempre devolverá falso.

En su panel de ensamblajes (el de la izquierda), ahora puede desplazarse hacia arriba y hacer clic en "Biblioteca de Common Language Runtime", el panel de ReflexIL le dará la opción de guardarlo.

Notas importantes:

  • ¡Primero HAGA UNA COPIA DE SEGURIDAD de su ensamblaje original!(mscorlib.dll)
  • mscorlib.dll es un ensamblado firmado y necesitará el SDK de .NET (herramienta sn.exe) para que ReflexIL omita la verificación.Lo acabo de comprobar yo mismo, ya deberías tenerlo con Visual C# instalado.Simplemente haga clic en "Registrarlo para omitir la verificación (en esta computadora)" cuando se le solicite.
  • No creo que tenga que decirte que uses esto solo en tu máquina de desarrollo :)

¡Buena suerte!Si necesita instrucciones adicionales, no dude en utilizar el cuadro de comentarios.

editar2:

¡Estoy confundido!

Eliminé por completo la verificación IsWeakKey de la función set_Key en el ensamblado mscorlib.Estoy absolutamente seguro de que modifiqué la función correcta y de que lo hice correctamente.El desmontaje del reflector ya no muestra el cheque.Sin embargo, lo curioso es que Visual C# todavía genera la misma excepción.

Esto me lleva a creer que mscorlib de alguna manera todavía debe estar almacenado en caché en algún lugar.Sin embargo, cambiar el nombre de mscorlib.dll a mscorlib.dll_ hace que MSVC# falle, por lo que aún debe depender del dll original.

Esto es algo bastante interesante, pero creo que he llegado al punto en el que no tengo ni idea de lo que está pasando, ¡simplemente no tiene ningún sentido!Ver imagen adjunta.:(

editar3:

Noto en Olly que, a diferencia de ensamblados como mscoree, mscorsec y mscorwks;mscorlib.dll en realidad no se encuentra en:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\

Pero en cambio, en lo que parece ser una ubicación inexistente:C:\WINDOWS\assembly ativeImages_v2.0.50727_32\mscorlib\6d667f19d687361886990f3ca0f49816\mscorlib.ni.dll

Creo que me falta algo aquí :) Investigaré esto un poco más.

editar4:

Incluso después de haber parcheado TODO en IsWeakKey y haber jugado con la eliminación y generación de nuevas imágenes nativas (x.ni.dll) de mscorlib.dll usando "ngen.exe", obtengo la misma excepción.Debo señalar que incluso después de desinstalar las imágenes nativas de mscorlib, todavía usa mscorlib.ni.dll...Bueno.

Me rindo.Espero que alguien pueda responder qué diablos está pasando porque seguro que no lo sé.:)

Me enteré de lo que tiene que hacer. Afortunadamente existe un método que dispone que crea los ICryptoTranforms que no comprueba claves débiles. También es necesario tener cuidado con la clase base, ya que también hace comprobaciones de validez. A través de la reflexión simplemente llame a cabo el método _NewEncryptor (que tiene que hacer un poco más de reflexión, pero esa es la idea).

Por suerte los MACTripleDES tiene un campo de TripleDES tipo, por lo que se derivan de MACTripleDES y lo reemplazan por medio de la reflexión de los constructores. He hecho todo el trabajo por usted.

No puedo verificar que el MAC correcta se genera, pero no se lanzan excepciones. Por otra parte, es posible que desee comentario de documentación del código y hacer la gestión de excepciones (fallos de reflexión - por ejemplo, si los campos / métodos no están allí) - pero esto es así; así que no me molesté.

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);
        }
    }
}

Por desgracia, el comportamiento no puede ser anulado.

Hay una gran sugerencia usando reflexión en los foros de MSDN

No soy un experto en seguridad, pero no lo XORing su clave con otro valor suficiente para satisfacer la comprobación de validez? Se podría hacer esto para su versión de depuración (con IFDEF adecuado) para que pueda hacer la comprobación adecuada y extraerlo para su liberación o versión de producción donde la clave sería lo suficientemente fuerte.

Las soluciones basadas en la reflexión que consiguen solucionar el problema, pero son sucios y mal. Nadie ha mencionado un método muy útil: TripleDES.IsWeakKey

He tenido este problema y lo resolvió con una utilidad muy simple que utilizo inmediatamente antes de establecer la clave en mi CryptoServiceProvider:

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

Si llama en cualquier momento que haga una encriptador o descifrador, se debe evitar el accidente y siempre le dará una clave segura.

Muy sencillo (Después de mirar el código de GitHub)

TripleDES.IsWeakKey bool estático (byte [] rgbKey)

Dado que es estática - es fácil de probar su clave contra ella

  1. tamaño debe ser 16 o 24 bytes (???) Por qué no pueden ponerlo en la documentación
  2. El código de verificación de pocas repeticiones simples Basta con crear valores aleatorios enuogh

Ver código en: https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Security.Cryptography/TripleDES.cs

Dekel

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top