我正在尝试使用
计算MAC使用 ISOIEC 9797-1 MAC 计算加密校验和 算法 3,采用分组密码 DES、零 IV(8 字节)和 1S09797-1 填充方法2. MAC长度必须是8字节
摘自提供 ICC 只读访问的机读旅行证件技术报告 PM 版本:1.1 日期:2004 年 10 月 1 日。
我正在使用报告中的示例值:
肯克:AB 94 FD EC F2 67 4F DF B9 B3 91 F8 5D 7F 76 F2
Kmac:79 62 D9 EC E0 3D 1A CD 4C 76 08 9D CE 13 15 43
eIFD:72 C2 9C 23 71 CC 9B DB 65 B7 79 B8 E8 D3 7B 29 EC C1 54 AA 56 A8 79 9F AE 2F 49 8F 76 ED 92 F2
但是,我没有得到相同的 MAC,并且不确定我需要如何处理它。我的第一次尝试是:
MACTripleDES mac = new System.Security.Cryptography.MACTripleDES(Kmac);
mac.Initialize();
mac.Padding = PaddingMode.None;
mac.Key = Kmac;
mIfd = mac.TransformFinalBlock(eIfd, 0, eIfd.Length);
结果:
mIFD:1C DE 09 70 4C 0D 9B 12
预期:
mIFD:5F 14 48 EE A8 AD 90 A7
然后我尝试手动执行我理解的“ISO/IEC 9797-1 MAC 算法 3,使用块密码 DES、零 IV(8 字节)和 1S09797-1 填充方法 2”,其中包含以下内容:(我基于此Rasmus Faber 的答案,但将数据分成 64 位块以进行迭代步骤)
byte[] key1 = new byte[8];
Array.Copy(kMAC, 0, key1, 0, 8);
byte[] key2 = new byte[8];
Array.Copy(kMAC, 8, key2, 0, 8);
Console.WriteLine("key1:{0}", Hex.BytesToSpacedHexString(key1));
Console.WriteLine("key2:{0}", Hex.BytesToSpacedHexString(key2));
// Plit the blocks
byte[] d1 = new byte[8];
byte[] d2 = new byte[8];
byte[] d3 = new byte[8];
byte[] d4 = new byte[8];
Array.Copy(eIfd, 0, d1, 0, 8);
Array.Copy(eIfd, 8, d2, 0, 8);
Array.Copy(eIfd, 16, d3, 0, 8);
Array.Copy(eIfd, 24, d4, 0, 8);
DES des1 = DES.Create();
des1.BlockSize = 64;
des1.Key = key1;
des1.Mode = CipherMode.CBC;
des1.Padding = PaddingMode.None;
des1.IV = new byte[8];
DES des2 = DES.Create();
des2.BlockSize = 64;
des2.Key = key2;
des2.Mode = CipherMode.CBC;
des2.Padding = PaddingMode.None;
des2.IV = new byte[8];
// MAC Algorithm 3
// Initial Transformation 1
byte[] h1 = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);
// Iteration on the rest of blocks
// XOR
byte[] int2 = new byte[8];
for (int i = 0; i < 8; i++)
int2[i] = (byte)(h1[i] ^ d2[i]);
// Encrypt
byte[] h2 = des1.CreateEncryptor().TransformFinalBlock(int2, 0, 8);
// XOR
byte[] int3 = new byte[8];
for (int i = 0; i < 8; i++)
int3[i] = (byte)(h2[i] ^ d3[i]);
// Encrypt
byte[] h3 = des1.CreateEncryptor().TransformFinalBlock(int3, 0, 8);
// XOR
byte[] int4 = new byte[8];
for (int i = 0; i < 8; i++)
int4[i] = (byte)(h3[i] ^ d4[i]);
// Encrypt
byte[] h4 = des1.CreateEncryptor().TransformFinalBlock(int4, 0, 8);
// Output Transformation 3
byte[] h4decrypt = des2.CreateDecryptor().TransformFinalBlock(h4, 0, 8);
mIfd = des1.CreateEncryptor().TransformFinalBlock(h4decrypt, 0, 8);
Console.WriteLine("mIFD:{0}", Hex.BytesToSpacedHexString(mIfd));
输出是:
eIFD:72 C2 9C 23 71 CC 9B DB 65 B7 79 B8 E8 D3 7B 29 EC C1 54 AA 56 A8 79 9F AE 2F 49 8F 76 ED 92 F2
键1:79 62 D9 EC E0 3D 1A CD
密钥2:4C 76 08 9D CE 13 15 43
结果:
mIFD:AA E3 F3 51 32 ED 34 65
预期:
mIFD:5F 14 48 EE A8 AD 90 A7
这两种情况都与预期不同。我错过了什么?
感谢您的宝贵时间。
感谢 owlstead,技巧是即使数据字符串恰好是 32 字节,也必须进行填充。对于需要完整代码的人。
的 MAC 哈希代码eIFD:72 C2 9C 23 71 CC 9B DB 65 B7 79 B8 E8 D3 7B 29 EC C1 54 AA 56 A8 79 9F AE 2F 49 8F 76 ED 92 F2
数据字符串如下所示:
// Split the 16 byte MAC key into two keys
byte[] key1 = new byte[8];
Array.Copy(kMAC, 0, key1, 0, 8);
byte[] key2 = new byte[8];
Array.Copy(kMAC, 8, key2, 0, 8);
Console.WriteLine("key1:{0}", Hex.BytesToSpacedHexString(key1));
Console.WriteLine("key2:{0}", Hex.BytesToSpacedHexString(key2));
// Padd the data with Padding Method 2 (Bit Padding)
System.IO.MemoryStream out_Renamed = new System.IO.MemoryStream();
out_Renamed.Write(eIfd, 0, eIfd.Length);
out_Renamed.WriteByte((byte)(0x80));
while (out_Renamed.Length % 8 != 0)
{
out_Renamed.WriteByte((byte)0x00);
}
byte[] eIfd_padded = out_Renamed.ToArray();
Console.WriteLine("eIfd_padded:{0}", Hex.BytesToSpacedHexString(eIfd_padded));
// Split the blocks
byte[] d1 = new byte[8];
byte[] d2 = new byte[8];
byte[] d3 = new byte[8];
byte[] d4 = new byte[8];
byte[] d5 = new byte[8];
Array.Copy(eIfd_padded, 0, d1, 0, 8);
Array.Copy(eIfd_padded, 8, d2, 0, 8);
Array.Copy(eIfd_padded, 16, d3, 0, 8);
Array.Copy(eIfd_padded, 24, d4, 0, 8);
Array.Copy(eIfd_padded, 32, d5, 0, 8);
DES des1 = DES.Create();
des1.BlockSize = 64;
des1.Key = key1;
des1.Mode = CipherMode.CBC;
des1.Padding = PaddingMode.None;
des1.IV = new byte[8];
DES des2 = DES.Create();
des2.BlockSize = 64;
des2.Key = key2;
des2.Mode = CipherMode.CBC;
des2.Padding = PaddingMode.None;
des2.IV = new byte[8];
// MAC Algorithm 3
// Initial Transformation 1
byte[] h1 = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);
// Iteration on the rest of blocks
// XOR
byte[] int2 = new byte[8];
for (int i = 0; i < 8; i++)
int2[i] = (byte)(h1[i] ^ d2[i]);
// Encrypt
byte[] h2 = des1.CreateEncryptor().TransformFinalBlock(int2, 0, 8);
// XOR
byte[] int3 = new byte[8];
for (int i = 0; i < 8; i++)
int3[i] = (byte)(h2[i] ^ d3[i]);
// Encrypt
byte[] h3 = des1.CreateEncryptor().TransformFinalBlock(int3, 0, 8);
// XOR
byte[] int4 = new byte[8];
for (int i = 0; i < 8; i++)
int4[i] = (byte)(h3[i] ^ d4[i]);
// Encrypt
byte[] h4 = des1.CreateEncryptor().TransformFinalBlock(int4, 0, 8);
// XOR
byte[] int5 = new byte[8];
for (int i = 0; i < 8; i++)
int5[i] = (byte)(h4[i] ^ d5[i]);
// Encrypt
byte[] h5 = des1.CreateEncryptor().TransformFinalBlock(int5, 0, 8);
// Output Transformation 3
byte[] h5decrypt = des2.CreateDecryptor().TransformFinalBlock(h5, 0, 8);
byte[] mIfd = des1.CreateEncryptor().TransformFinalBlock(h5decrypt, 0, 8);
Console.WriteLine("mIFD:{0}", Hex.BytesToSpacedHexString(mIfd));
您至少缺少填充模式。 ICAO 技术规范使用位填充(至少一个值为
80
的字节,然后是一到七个 00
值的字节,直到到达块的末尾。
感谢分享代码。这是一个很好的例子。我已更改代码以寻址任何大小的块,而不仅仅是 5 个字节。现在,在下面的代码中,可以使用块中的单个字节到块中任意大小的字节来获取 MAC。
private static byte[] getCC_MACNbytes(string Key_MAC, byte[] eIFD, string Init_Vec)
{
byte[] Kmac = StringToByteArray(Key_MAC);
// Split the 16 byte MAC key into two keys
byte[] key1 = new byte[8];
Array.Copy(Kmac, 0, key1, 0, 8);
byte[] key2 = new byte[8];
Array.Copy(Kmac, 8, key2, 0, 8);
DES des1 = DES.Create();
des1.BlockSize = 64;
des1.Key = key1;
des1.Mode = CipherMode.CBC;
des1.Padding = PaddingMode.None;
des1.IV = new byte[8];
DES des2 = DES.Create();
des2.BlockSize = 64;
des2.Key = key2;
des2.Mode = CipherMode.CBC;
des2.Padding = PaddingMode.None;
des2.IV = new byte[8];
// Padd the data with Padding Method 2 (Bit Padding)
System.IO.MemoryStream out_Renamed = new System.IO.MemoryStream();
out_Renamed.Write(eIFD, 0, eIFD.Length);
out_Renamed.WriteByte((byte)(0x80));
while (out_Renamed.Length % 8 != 0)
{
out_Renamed.WriteByte((byte)0x00);
}
byte[] eIfd_padded = out_Renamed.ToArray();
int N_bytes = eIfd_padded.Length/8; // Number of Bytes
byte[] d1 = new byte[8];
byte[] dN = new byte[8];
byte[] hN = new byte[8];
byte[] intN = new byte[8];
// MAC Algorithm 3
// Initial Transformation 1
Array.Copy(eIfd_padded, 0, d1, 0, 8);
hN = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);
// Split the blocks
// Iteration on the rest of blocks
for (int j = 1; j<N_bytes; j++)
{
Array.Copy(eIfd_padded, (8*j), dN, 0, 8);
// XOR
for (int i = 0; i < 8; i++)
intN[i] = (byte)(hN[i] ^ dN[i]);
// Encrypt
hN = des1.CreateEncryptor().TransformFinalBlock(intN, 0, 8);
}
// Output Transformation 3
byte[] hNdecrypt = des2.CreateDecryptor().TransformFinalBlock(hN, 0, 8);
byte[] mIfd = des1.CreateEncryptor().TransformFinalBlock(hNdecrypt, 0, 8);
// Get check Sum CC
return mIfd;
}
您的问题是输入了错误的数据,如下所示。
eIFD: 72C29C2371CC9BDB65B779B8E8D37B29ECC154AA56A8799FAE2F498F76ED92F22
正确的是: 72C29C2371CC9BDB65B779B8E8D37B29ECC154AA56A8799FAE2F498F76ED92F2