algorithm - ISO 9797-1 Algoritmo 1[CBC-MAC] en C#
(3)
Estoy bastante seguro (IIRC) de que necesita llamar a TransformFinalBlock
al final (por encriptador).
Parece que hay 6 variaciones al algoritmo CBC-MAC. He intentado hacer coincidir el algoritmo MAC en el PINPad 1000SE [que por manual es ISO 9797-1 Algoritmo 1].
Tengo un excelente comienzo desde aquí .
Y codifiqué el algoritmo de la siguiente manera:
public static byte[] CalculateMAC(this IPinPad pinpad, byte[] message, byte[] key)
{
//Divide the key with Key1[ first 64 bits] and key2 [last 64 bits]
var key1 = new byte[8];
Array.Copy(key, 0, key1, 0, 8);
var key2 = new byte[8];
Array.Copy(key, 8, key2, 0, 8); //64 bits
//divide the message into 8 bytes blocks
//pad the last block with "80" and "00","00","00" until it reaches 8 bytes
//if the message already can be divided by 8, then add
//another block "80 00 00 00 00 00 00 00"
Action<byte[], int> prepArray = (bArr, offset) =>
{
bArr[offset] = 0; //80
for (var i = offset + 1; i < bArr.Length; i++)
bArr[i] = 0;
};
var length = message.Length;
var mod = length > 8? length % 8: length - 8;
var newLength = length + ((mod < 0) ? -mod : (mod > 0) ? 8 - mod : 0);
//var newLength = length + ((mod < 0) ? -mod : (mod > 0) ? 8 - mod : 8);
Debug.Assert(newLength % 8 == 0);
var arr = new byte[newLength];
Array.Copy(message, 0, arr, 0, length);
//Encoding.ASCII.GetBytes(message, 0, length, arr, 0);
prepArray(arr, length);
//use initial vector {0,0,0,0,0,0,0,0}
var vector = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
//encrypt by DES CBC algorith with the first key KEY 1
var des = new DESCryptoServiceProvider { Mode = CipherMode.CBC };
var cryptor = des.CreateEncryptor(key1, vector);
var outputBuffer = new byte[arr.Length];
cryptor.TransformBlock(arr, 0, arr.Length, outputBuffer, 0);
//Decrypt the result by DES ECB with the second key KEY2 [Original suggestion]
//Now I''m Encrypting
var decOutputBuffer = new byte[outputBuffer.Length];
des.Mode = CipherMode.ECB;
var decryptor = des.CreateEncryptor(key2, vector);
//var decryptor = des.CreateDecryptor(key2, vector);
decryptor.TransformBlock(outputBuffer, 0, outputBuffer.Length, decOutputBuffer, 0);
//Encrypt the result by DES ECB with the first key KEY1
var finalOutputBuffer = new byte[decOutputBuffer.Length];
var cryptor2 = des.CreateEncryptor(key1, vector);
cryptor2.TransformBlock(decOutputBuffer, 0, decOutputBuffer.Length, finalOutputBuffer, 0);
//take the first 4 bytes as the MAC
var rval = new byte[4];
Array.Copy(finalOutputBuffer, 0, rval, 0, 4);
return rval;
}
Luego descubrí que hay 3 esquemas de relleno y el que me dio un inicio puede no ser necesariamente el correcto. El manual vino a mi rescate otra vez. Parece que el dispositivo solo tiene almohadillas con 0s. El bloqueo adicional tampoco se menciona en ninguna parte, así que hice los cambios a continuación:
Action<byte[], int> prepArray = (bArr, offset) =>
{
bArr[offset] = 0; ... }
Sin bloque adicional (si mod 0 [divisible por 8] no cambia la longitud de la matriz)
var newLength = length + ((mod < 0) ? -mod : (mod > 0) ? 8 - mod : 0);
La sugerencia original quería que descifrase en el segundo paso ... pero Valery sugiere que se encripte todo el camino. Así que cambié Decrypt a Encrypt. Pero aún no puedo obtener el MAC requerido ...
El manual dice para la clave "6AC292FAA1315B4D8234B3A3D7D5933A" [dado que la clave debe tener 16 bytes, calculé que la clave aquí es una cadena hexadecimal, así que tomé valores de bytes de 6A, C2, 92, FA ... nuevo byte [] {106, 194, 146, ...] el MAC debería ser 7B, 40, BA, 95 [4 bytes] si el mensaje es [0x1a + array de bytes de MENTERODOMETER]
¿Alguien puede ayudar? ¿Por favor?
Como Pinpad requiere que el primer carácter en el mensaje sea un 0x1a ...
public static byte[] CalculateAugmentedMAC(this IPinPad pinpad, string message, byte[] key)
{
var arr = new byte[message.Length + 1];
var source = Encoding.ASCII.GetBytes(message);
arr[0] = 0x1a; //ClearScreenIndicator
Array.Copy(source, 0, arr, 1, source.Length);
return CalculateMAC(pinpad, arr, key);
}
Estoy llamando al código de arriba con esta entrada:
var result = pad.CalculateAugmentedMAC("MENTERODOMETER", new byte[] { 106, 194, 146, 250, 161, 49, 91, 77, 130, 52, 179, 163, 215, 213, 147, 58 });
La mayoría de los algoritmos de CBC MAC se implementan en el proveedor de JCE de BouncyCastle .
Mira: BouncyCastleProvider.java
Probablemente esté buscando DESEDEISO9797ALG1MACWITHISO7816-4PADDING
, que es un alias para DESEDEMAC64WITHISO7816-4PADDING
, implementado aquí (bueno, es una configuración específica de CBCBlockCipherMac que utiliza el DESedeEngine y el documento ISO7816d4Padding, deberá saltar entre algunas clases para obtener una visión completa ): JCEMac.java
Además, eche un vistazo a jPos:
y su implementación contribuida de algoritmo de MAC minorista:
No puedo responder a su terminal específico, pero lo uso para probar los MAC.
public static byte[] GenerateMAC(byte[] key, byte[] data)
{
using (MACTripleDES mac = new MACTripleDES(key))
return mac.ComputeHash(data);
}