3

使用 Desfire 原生封装的 APDU 与卡通信时,必须使用命令和响应的哪些部分来计算 CMAC?

成功认证后,我有以下会话密钥:

Session Key: 7CCEBF73356F21C9191E87472F9D0EA2

然后,当我发送 GetKeyVersion 命令时,卡返回以下我正在尝试验证的 CMAC:

<< 90 64 00 00 01 00 00
>> 00 3376289145DA8C27 9100

我根据“NIST 特别出版物 800-38B”实现了 CMAC 算法并确保它是正确的。但我不知道必须使用命令和响应 APDU 的哪些部分来计算 CMAC。

我使用的是 TDES,所以 MAC 是 8 个字节。

4

2 回答 2

2

在过去的几天里,我一直在研究完全相同的问题,我想我至少可以给你一些建议。让一切都“如此”需要一些时间,并且在某些情况下,NXP 的文档(假设您可以访问)有点难以解释。

因此,您可能知道,您需要在发送和接收时计算 CMAC(并更新您的 init vec)。每次将 CMAC 计算为下一次加密操作(无论是 CMAC 还是加密等)的 init vec 时,都需要保存 CMAC。

在为您的示例计算 CMAC 时,输入 CMAC 算法的数据是 INS 字节 ( 0x64) 和命令数据 ( 0x00)。当然,这将按照 CMAC 的规定进行填充等。但是请注意,您计算整个 APDU 包装(即90 64 00 00 01 00 00)的 CMAC,仅使用 INS 字节和数据有效负载。

在接收时,您需要获取数据(0x00)和第二个状态字节(也0x00)并计算 CMAC。在这个例子中它并不重要,但在这里顺序很重要。您使用响应正文(不包括 CMAC),然后使用 SW2。

请注意,实际上只发送了一半的 CMAC - CMAC 应该产生 16 个字节,并且卡正在发送前 8 个字节。

还有其他一些事情让我受不了,包括:

  • 我计算的会话密钥不正确——如果事情没有像你期望的那样出现,值得仔细检查一下
  • 我将文档解释为说整个 APDU 结构用于计算 CMAC(很难以其他任何方式阅读它们)

我仍在努力正确计算写入数据命令的响应。命令成功,但我无法验证 CMAC。我确实知道写入数据没有用 CMAC 填充,而只是零 - 还不确定我还错过了什么。

最后,这是一个与我的日志中的卡进行通信的真实示例:

  1. 身份验证完成 (AES) 并且会话密钥被确定为F92E48F9A6C34722A90EA29CFA0C3D12; 初始化 vec 为零

  2. 我将发送 Get Key Version 命令(如您的示例中所示),因此我计算 CMAC6400并获取1200551CA7E2F49514A1324B7E3428F1(现在是我用于下一次计算的 init vec)

  3. 发送90640000010000到卡并接收00C929939C467434A8(状态为 9100)。

  4. 计算 CMAC00 00并获取C929939C467434A8A29AB2C40B977B83(并更新 init vec 以进行下一次计算)

  5. 第 4 步中我们的 CMAC 的前半部分与第 3 步中从卡接收到的 8 字节匹配

于 2016-11-28T05:19:36.633 回答
2

对不起我的英语,- 太糟糕了:) 但这不是我的母语。我是俄国人。

检查数组 [0] 的第一个 MSB(7 位),然后将其向左移动。如果 MSB 7 位为 == 1,则异或;或者保存数组 [0] 的第一个 MSB 位,并在移位后将此位放在数组 [15] 的末尾(LSB 位)。

只是证明它在这里: https ://www.nxp.com/docs/en/application-note/AN10922.pdf

试试这个方法:

零 <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

会话密钥 <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5

数据 <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00

首先你必须用 SesionKey 加密 16 个字节(零);

enc_aes_128_ecb(Zeros);

你得到加密数据。

加密数据 <- 3D 08 A2 49 D9 71 58 EA 75 73 18 F2 FA 6A 27 AC

检查 EncryptedData[0] 的第 7 位 [MSB - LSB] == 1?将 i 切换为 true;

 bool i = false;
  if (EncryptedData[0] & 0x80){
    i = true;
  }

然后将所有 EncryptedData 移位到 1 位 <<。

ShiftLeft(EncryptedData,16);

现在,当 i == true - 最后一个字节 [15] 与 0x87 异或

if (i){
    ShiftedEncryptedData[15] ^= 0x87;
  }

7A 11 44​​ 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58

将其另存为 KEY_1。

尝试 ShiftedEncryptedData[0] == 1 的第 7 位 [MSB - LSB]?

 i = false;
  if (ShiftedEncryptedData[0] & 0x80){
    i = true;
  }

然后将所有 ShiftedEncryptedData 移位到 1 位 <<。

ShiftLeft(ShiftedEncryptedData,16);

现在,当 i == true - 最后一个字节 [15] 与 0x87 异或

if (i){
   ShiftedEncryptedData[15] ^= 0x87;
}

F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0

将其另存为 KEY_2。

现在我们获取我们的数据(6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00

正如迈克尔所说 - 使用 0x80 0x00 填充命令...

XOR Data with KEY_2 - 如果命令被填充,或者 KEY_1 如果没有。如果我们有更多的 16 个字节(例如 32 个),你必须对最后 16 个字节进行异或。

然后加密它:

enc_aes_128_ecb(Data);

现在你有一个 CMAC。

CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3


零 <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

会话密钥 <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5

Key_1 <- 7A 11 44​​ 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58

Key_2 <- F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0

数据 <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00

CMAC <- CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3

C/C++ 函数:

void ShiftLeft(byte *data, byte dataLen){
  for (int n = 0; n < dataLen - 1; n++) {
   data[n] = ((data[n] << 1) | ((data[n+1] >> 7)&0x01));
  }
  data[dataLen - 1] <<= 1;
}   

祝你今天过得愉快 :)

于 2017-12-15T19:17:16.553 回答