TripleDES: Chave especificada é uma chave fraca conhecido por 'TripleDES' e não pode ser usado

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

Pergunta

Eu estou usando o .NET classe System.Security.Cryptography.MACTripleDES 3.0 classe para gerar um valor MAC. Infelizmente, eu estou trabalhando com um dispositivo de hardware que usa "1111111111111111" (como hex) como uma chave DES de comprimento simples. A biblioteca System.Security.Cryptography faz alguma verificação de sanidade na chave e retorna uma exceção se você tentar usar uma chave de criptografia fraca.

Por exemplo:

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

lança uma exceção

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

Eu sei que isto não é uma chave segura. Na produção, o dispositivo será brilhou com uma chave nova, segura. Nesse meio tempo, há alguma maneira de inibir essa exceção de ser lançada? Talvez um cenário app.config ou registro?

Edit: A chave seria realmente 101010 ... devido ao algoritmo forçando paridade ímpar. Eu não tenho certeza se isso é universal para o algoritmo DES ou apenas uma exigência no trabalho de processamento de pagamentos que faço.

Edit 2: A resposta de Daniel abaixo tem algumas informações muito boas sobre hacking .NET. Infelizmente, não foi capaz de resolver o meu problema usando esta técnica, mas ainda há alguma leitura interessante lá.

Foi útil?

Solução

Em vez de usar MACTripleDES com a chave DES repetida para falsificar um único DES CBC-MAC, você pode simplesmente implementar CBC-MAC-se no topo de DESCryptoServiceProvider .

<1111111111111111> não é um fraco chave DES.

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

Outras dicas

Eu realmente não recomendo, mas você deve ser capaz de modificar o código-IL que verifica a existência de chaves fracas usando refletor eo Add-in Reflexil

edit:

Desculpe, demorou um tempo para mim para carregar tudo isso na minha máquina virtual (rodando Ubuntu) e não quero mexer com Mono.

  • Instale o Reflexil Add-in: View -> Suplementos -> Adicionar
  • Open Reflexil: Ferramentas -> Reflexil v0.9
  • Encontre a função IsWeakKey (). (Você pode usar Pesquisa: F3)
  • Duas funções vai aparecer, clique duas vezes o encontrado em System.Security.Cryptography.TripleDES
  • Reflexil deveria ter vindo até demais. Na guia Instruções, percorrer todo o caminho até a linha 29 (offset 63).
  • Alterar ldc.i4.1 para ldc.i4.0, isso significa que a função sempre retornará false.

Em seu painel assembléias (esquerdo), agora você pode rolar para cima e clique em "Biblioteca Runtime da língua comum", o painel de Reflexil lhe dará a opção de salvá-lo.

Notas importantes:

  • Faça backup de seus originais de assembléia primeiro! (Mscorlib.dll)
  • mscorlib.dll é um assinado montagem e você vai precisar do .NET SDK (ferramenta sn.exe) para Reflexil para torná-lo ignorar a verificação. Acabei de verificar isso mesmo, você já deve ter isso com Visual C # instalado. Basta clicar em "Registre-lo para verificação pular (neste computador)" quando solicitado a.
  • eu não acho que eu tenho que dizer-lhe apenas para usar isso em sua máquina de desenvolvimento:)

Boa sorte! Se precisar de instruções adicionais, não hesite em usar o commentbox.

edit2:

Estou confuso!

I removido completamente a verificação da função de IsWeakKey set_Key no mscorlib montagem. Estou absolutamente certo de que eu modifiquei a função correta, e que eu fiz isso corretamente. desmontador de refletor que não mostrar o cheque. O engraçado é que no entanto, que o Visual C # ainda lança a mesma exceção.

Isso me leva a crer que mscorlib deve de alguma forma ainda ser armazenado em cache em algum lugar. No entanto, renomeando mscorlib.dll para mscorlib.dll_ leva MSVC # para acidente, por isso ainda deve ser dependente da dll originais.

Isso é coisa bastante interessante, mas eu acho que já alcançou o ponto onde eu não tenho idéia o que está acontecendo, ele simplesmente não faz qualquer sentido! Veja a imagem anexada. : (

edit3:

noto em Olly, que ao contrário de montagens como mscoree, mscorsec e mscorwks; mscorlib.dll não está realmente localizado em: c: \ WINDOWS \ Microsoft.NET \ Framework \ v2.0.50727 \

Mas em vez disso, no que parece ser um local inexistente: C: \ WINDOWS \ montagem \ NativeImages_v2.0.50727_32 \ mscorlib \ 6d667f19d687361886990f3ca0f49816 \ mscorlib.ni.dll

Eu acho que eu estou faltando alguma coisa aqui :) vai investigar isso um pouco mais.

edit4:

Mesmo depois de ter remendado tudo em IsWeakKey, e brincou com as duas novas imagens nativas remoção e gerando (x. ni .dll) de mscorlib.dll usando "ngen.exe", eu sou recebendo a mesma exceção. I deve-se notar que, mesmo depois de desinstalar as imagens mscorlib nativos, ele ainda está usando mscorlib.ni.dll ... Meh.

Eu desisto. Espero que alguém será capaz de responder o que diabos está acontecendo, porque eu com certeza não sei. :)

Eu descobri o que você precisa fazer. Felizmente, existe um método que disponíveis, que cria as ICryptoTranforms que não verifica chaves fracas. Você também precisa estar atento para a classe base, uma vez que também faz checagens. Via reflexão simplesmente chamar o método _NewEncryptor (você precisa fazer um pouco mais de reflexão, mas essa é a idéia).

Felizmente, o MACTripleDES tem um campo de TripleDES tipo, de modo que derivam de MACTripleDES e substituí-lo por meio de reflexão nos construtores. Eu tenho feito todo o trabalho para você.

Eu não posso verificar se o MAC correto é gerado, mas há exceções são lançadas. Além disso, você pode querer comentário doc o código e fazer o tratamento de exceções (falhas reflexão - por exemplo, se os campos / métodos não existem) - mas isso é assim; então eu não me incomodei.

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

Infelizmente, o comportamento pode não ser substituído.

Há uma grande sugestão usando o reflexão nos fóruns MSDN

Eu não sou um especialista em segurança, mas não XORing sua chave com outro valor seria suficiente para satisfazer a verificação de sanidade? Você poderia fazer isso para a sua versão de depuração (com IFDEF adequado) para que você possa fazer a checagem apropriada e removê-lo para o seu lançamento ou versão de produção onde a chave seria forte o suficiente.

As soluções reflexão baseada chegar em torno do problema, mas eles são sujos e mal. Ninguém ainda mencionou um método muito útil: TripleDES.IsWeakKey

Eu tive esse problema e resolveu-o com uma utilidade muito simples que eu uso imediatamente antes eu definir a chave no meu CryptoServiceProvider:

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

Se você chamá-lo quando você faz um codificador ou decodificador, deve evitar o acidente e sempre dar-lhe uma chave segura.

Muito simples (Depois de olhar para o código do GitHub)

static bool TripleDES.IsWeakKey (Byte [] rgbKey)

Uma vez que é estática - é fácil de testar sua chave contra ele

  1. tamanho deve ser 16 ou 24 bytes (???) Por que eles não podem colocá-lo na documentação
  2. A verificação de código para algumas repetições simples Basta criar valores enuogh aleatórios

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

Dekel

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top