如何解密从Mifare Desfire EV1发送的第一条消息
有没有人知道如何解密从卡发送的第一条消息? 我的意思是在身份validation成功后然后你发送一个命令(例如0x51(GetRealTagUID)。它返回00 + random32bits(总是不同)。我尝试解密它:
private byte[] decrypt(byte[] raw, byte[] encrypted, byte[] iv) throws Exception { IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; }
用decrypt调用它(sessionKey,response,iv)
IV =全零(16字节)
响应= 0x51命令后的32randombits(刚刚删除了两个零)
有人告诉我,第一次发送命令(0x51)后IV发生了变化。 如何生成正确的IV来解密该响应? 我认为全零是错误的,因为解密的消息总是不同的,并且它应该始终与同一张卡相同。
-编辑-
在应用您的(Michael Roland)指令后,解密的响应仍然只是随机位。 这是我的代码(我想我做错了):
byte[] x = encrypt(sessionKey, iv, iv); byte[] rx = rotateBitsLeft(x); if ((rx[15] & 0x01) == 0x01) rx[15] = (byte) (rx[15] ^ 0x87); if ((rx[15] & 0x01) == 0x00) rx[15] = (byte) (rx[15] ^ 0x01); byte[] crc_k1 = rx; byte[] rrx = rotateBitsLeft(rx); if ((rrx[15] & 0x01) == 0x01) rrx[15] = (byte) (rrx[15] ^ 0x87); if ((rrx[15] & 0x01) == 0x00) rrx[15] = (byte) (rrx[15] ^ 0x01); byte[] crc_k2 = rrx; byte[] command = { (byte) 0x51, (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; for (int i = 0; i < 16; i++){ command[i] = (byte) (command[i] ^ crc_k2[i]); } byte[] iv2 = encrypt(sessionKey, command, iv); byte[] RealUID = decrypt(sessionKey, ReadDataParsed, iv2); Log.e("RealUID", ByteArrayToHexString(RealUID));
-EDIT3-
仍然总是返回不同的值。 我认为问题可能在于:
byte[] iv2 = encrypt(sessionKey, command, iv);
在创建用于解密响应的新IV时使用什么IV? 那里全是零。
身份validation后,IV将重置为全零。 在使用AES身份validation时,您必须为每个后续命令计算CMAC(即使CMAC实际上未附加到命令中)。 因此,对命令进行CMAC计算将导致正确的IV初始化以解码响应。 即命令的CMAC等于用于解密响应的IV。 类似地,对于所有其他命令,IV是来自先前加密/ CMAC的最后一个密码块。
更新:
如何计算CMAC垫XOR值
- 使用会话密钥(使用零的IV)加密一个零块(
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
)。 – >x[0..15]
- 将
x[0..15]
向左旋转一位。 – >rx[0..15]
- 如果最后一位(
rx[15]
位0)是1:xorrx[15]
和0x86
。 - 将
rx[0..15]
存储为crc_k1[0..15]
。 - 将
rx[0..15]
向左旋转一位。 – >rrx[0..15]
- 如果最后一位(
rrx[15]
位0)是1:xorrrx[15]
和0x86
。 - 将
rrx[0..15]
存储为crc_k2[0..15]
。
如何计算CMAC
- 使用
0x80 0x00 0x00 ...
将命令填充到密码的块大小(AES为16字节)。 如果命令长度与块大小的倍数匹配,则不添加填充。 - 对于您的命令(
0x51
),这将看起来像:0x51 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
- 如果添加了填充,则使用
crc_k2[0..15]
填充填充命令的最后16个字节。 - 如果没有添加填充,则使用
crc_k1[0..15]
xor命令的最后16个字节。 - 加密(在发送模式,即
enc(IV xor datablock)
,前一个块的密文是新的IV)结果与会话密钥。 - 最后一个块的密文是CMAC和新的IV。
更新2:
如何将位向量向左旋转一位
public void rotateLeft(byte[] data) { byte t = (byte)((data[0] >>> 7) & 0x001); for (int i = 0; i < (data.length - 1); ++i) { data[i] = (byte)(((data[i] << 1) & 0x0FE) | ((data[i + 1] >>> 7) & 0x001)); } data[data.length - 1] = (byte)(((data[data.length - 1] << 1) & 0x0FE) | t); }
这不是c / c ++部分,而是进入我的位置,我正在考虑C开发人员可能存在的错误。 我不能在这里certificateJava的实现。 但我认为@Michael Roland的方式并非如此! 莫比! Maby我错了!
Sry for my English, – 太可怕了:)但这不是我的母语。 我是俄国人。
检查数组[0]的第一个MSB(7位),然后将其向左移动。 如果MSB 7位是== 1,那么XOR; 或者保存数组[0]的第一个MSB位,并在shiffting之后将该位放在数组[15]的末尾(LSB位)。
只需在此certificate: 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
SessionKey < - 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);
你得到EncryptedData。
EncryptedData < - 3D 08 A2 49 D9 71 58 EA 75 73 18 F2 FA 6A 27 AC
检查EncryptedData [0]的第7位[MSB – LSB] == 1? 切换到真;
bool i = false; if (EncryptedData[0] & 0x80){ i = true; }
然后将所有EncryptedData Shiffting为1位<<。
ShiftLeft(EncryptedData,16);
现在,当i == true时 – XOR最后一个字节[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的Shiffting改为1位<<。
ShiftLeft(ShiftedEncryptedData,16);
现在,当i == true时 – XOR最后一个字节[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填充命令…
带有KEY_2的XOR数据 – 如果命令被填充,或者KEY_1如果没有。 如果我们有更多像16个字节(例如32个),你必须XOR只是最后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
SessionKey < - 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 ++function:
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; }
祝你今天愉快 :)