我正在尝试使用我的 android 应用程序对 DESFire 卡进行身份验证。我使用此链接中的示例来解密从卡中获得的字节。为此,我排除了解密中的填充(在下面注释掉),因为 DESFire 文档指出了这一点。另外,如果我不这样做,解密会返回 7 个字节来输入 8 个字节。以下是我使用的 DES 和 TripleDES 解密函数:
public static byte[] TripleDES_Decrypt(byte[] data,byte[][] keys)
{
int i;
byte[] tmp = new byte[data.length];
byte[] bloc = new byte[8];
K = generateSubKeys(keys[0]);
K1 = generateSubKeys(keys[1]);
K2 = generateSubKeys(keys[2]);
for (i = 0; i < data.length; i++) {
if (i > 0 && i % 8 == 0) {
bloc = encrypt64Bloc(bloc,K2, true);
bloc = encrypt64Bloc(bloc,K1, false);
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
}
if (i < data.length)
bloc[i % 8] = data[i];
}
bloc = encrypt64Bloc(bloc,K2, true);
bloc = encrypt64Bloc(bloc,K1, false);
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
//tmp = deletePadding(tmp);
return tmp;
}
public static byte[] decrypt(byte[] data, byte[] key) {
int i;
byte[] tmp = new byte[data.length];
byte[] bloc = new byte[8];
K = generateSubKeys(key);
for (i = 0; i < data.length; i++) {
if (i > 0 && i % 8 == 0) {
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
}
if (i < data.length)
bloc[i % 8] = data[i];
}
bloc = encrypt64Bloc(bloc,K, true);
System.arraycopy(bloc, 0, tmp, i - 8, bloc.length);
//tmp = deletePadding(tmp);
return tmp;
}
根据 DesFire 文档,我需要两种解密模式,发送和接收。这篇博文对此有一些解释。
但是,DESFire 加密与普通的 DES/CBC 方案有点不同:PCD 在发送数据时使用 DES“发送模式”(在 DES 之前异或),而卡在接收数据时使用 DES“接收模式”(在 DES 之后异或)。但是PCD接收数据时使用普通DES/CBC模式(DES后异或),卡发送数据时使用普通DES发送模式(DES前异或)。
在 Android 方面,我遵循示例和建议:
// connected to tag and application
// result = encoded(randB) + af
byte[] result = idTag.transceive(Utils.wrapMessage((byte)0x0a, new byte[]{(byte)0x0}));
byte[] b0 = new byte[8];
for(int i = 0; i < 8; i++) {
b0[i] = result[i];
}
// key
byte[] key = new byte[] {(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0,
(byte)0x0,(byte)0x0,(byte)0x0,(byte)0x0 };
byte[][] keys = new byte[3][];
keys[0]=key; keys[1]=key; keys[2]=key;
// decrypt encoded(randB)
byte[] r0 = DES.TripleDES_Decrypt(b0, keys);
// generate randA (integer 0-7 for trying)
byte[] nr = new byte[8];
for(int i = 0; i < 8; i++) {
nr[i] = Byte.parseByte(Integer.toString(i), 16);
}
// decrypt randA
byte[] b1 = DES.TripleDES_Decrypt(nr, keys);
// shift randB and get randB'
byte[] r1 =new byte[8];
for(int i = 0; i < 7; i++) {
r1[i] = r0[i + 1];
}
r1[7]=r0[0];
// concat (randA + randB')
byte[] b2 = new byte[16];
for(int i = 0; i < 16; i++)
{
if(i <= 7) {
b2[i] = b1[i];
} else {
b2[i] = r1[i - 8];
}
}
// XOR (randA + randB') with IV
// IV is told to be consisting of 0's,
// but XOR something with 0 results the same?
for(int i=0;i<16;i++) {
b2[i] = (byte) (b2[i] ^ (byte)0x0);
}
// send AF and decrypt(A+B)
// wrap message adds needed wrapping to message (90 to left, offset bytes etc.)
result = isodepTag.transceive(Utils.wrapMessage((byte)0xaf, DES.TripleDES_Decrypt(b2, keys)));
我得到第一个结果,加密的 randB。但是,第二个“结果”总是“91ae”,表示认证错误。我在这里做错了,将错误的数据发送到卡。
谁能告诉我我必须在代码中更改什么才能在这些模式下工作?在 TripleDES 之前/之后,我应该对数据进行什么异或操作?
不是真正的问题,但我读到 DesFire 卡中的默认“密钥”是 16 个零字节。此外,我需要将 TripleDES 用于 16 个字节的密钥,将 DES 用于 8 个字节的密钥。所以我正在使用并且需要使用 TripleDES,因为我没有更改默认密钥,对吗?
对于那些需要了解CipherBlockChaining的人。
编辑:我发现我需要在 TripleDES 之前和之后进行 XORing,而且我根本不能触及 TripleDES 的内部操作。我会在一段时间内尝试。
删除了内部的 TripleDES 行,只是为第一次看到问题的人说。