TripleDES: Chave especificada é uma chave fraca conhecido por 'TripleDES' e não pode ser usado
-
09-09-2019 - |
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á.
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
- tamanho deve ser 16 ou 24 bytes (???) Por que eles não podem colocá-lo na documentação
- 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