9

我正在尝试使用我的 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 行,只是为第一次看到问题的人说。

4

2 回答 2

10

好的,我得到了解决方案。我的错误是我发送

3DES(randA + randB') 

但我应该发送

3DES(randA) + 3DES(randB' XOR 3DES(randA))

这是Android/Java的验证码(很遗憾,这是目前在网上能找到的唯一一个!):

实际验证码:

// send initial authentication request
byte[] result = idTag.transceive(Utils.wrapMessage((byte)0x0a, new byte[]{(byte)0x0}));

// get encrypted(randB) from the response
byte[] b0 = new byte[8];
for(int i = 0; i < 8; i++) {
    b0[i] = result[i];
}

// 16 bytes default 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 };
// keys for TripleDes
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, should randomize for real-life use) 
byte[] nr = new byte[8];
for(int i = 0; i < 8; i++) {
    nr[i] = Byte.parseByte(Integer.toString(i), 16);
}

// decrypt randA, should XOR with IV, but IV is all 0's, not necessary
byte[] b1 = DES.TripleDES_Decrypt(nr, keys);

// shift randB one byte left and get randB'
byte[] r1 =new byte[8];
for(int i = 0; i < 7; i++) {
    r1[i] = r0[i + 1];
}
r1[7]=r0[0];

// xor randB' with randA and decrypt
byte[] b2 = new byte[8];
for(int i = 0; i < 8; i++) {
    b2[i] = (byte) (b1[i] ^ r1[i]);
}
b2 = DES.TripleDES_Decrypt(b2, keys);

// concat (randA + randB')
byte[] b1b2 = new byte[16];

for (int i = 0; i < b1b2.length; i++) {
    if(i <= 7) {
        b1b2[i] = b1[i];
    } else {
        b1b2[i]=b2[i-8];
    }
}

result = idTag.transceive(Utils.wrapMessage((byte)0xaf, b1b2));

TripleDes 是问题中的一个。wrapMessage 函数:

public static byte[] wrapMessage (byte command, byte[] parameters) throws Exception {
    ByteArrayOutputStream stream = new ByteArrayOutputStream();

    stream.write((byte) 0x90);
    stream.write(command);
    stream.write((byte) 0x00);
    stream.write((byte) 0x00);
    if (parameters != null) {
        stream.write((byte) parameters.length);
        stream.write(parameters);
    }
    stream.write((byte) 0x00);

    return stream.toByteArray();
}

编辑:感谢 VGe0rge,我们发现了此身份验证有时不起作用的原因。不要在问题中调用 3DES 函数,只需调用:

Cipher.getInstance("DESede/CBC/NoPadding");
于 2013-01-04T16:02:34.133 回答
0

Ismat,您是否尝试将数据写入 DESFIRE 文件?

如您所述,向卡发送数据时:PCD在发送数据时使用DES“发送模式”(DES之前的异或),而卡在接收数据时使用DES“接收模式”(DES之后的异或)

所以我之前无法获得正确的代码来使用 XOR 实现 TDES。我需要做所有的 crypt-decrypt-crypt 并且这个过程对我的应用程序来说很慢:

res = criptoTransformDec.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
res = criptoTransformEnc.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);
res = criptoTransformDec1.TransformBlock(datosEscribir, 0, 8, datosEscribir, 0);

int l_iAux = 0;
while (l_iAux < (datosEscribir.Length - 8))
    {
        criptoTransformDec2 = desDec.CreateDecryptor(claveSes1, tdesInitialVector);
        //desEnc2 = new DESCryptoServiceProvider();
        criptoTransformEnc2 = desEnc.CreateEncryptor(claveSes2, tdesInitialVector);
        //desDec3 = new DESCryptoServiceProvider();
                criptoTransformDec3 = desDec.CreateDecryptor(claveSes1, tdesInitialVector);

        Array.Copy(datosEscribir, 8 + l_iAux, aux1, 0, 8);
        Array.Copy(datosEscribir, l_iAux, aux2, 0, 8);
        DesfireBarik.XorStr(ref aux1, ref aux2, 8);
        res = criptoTransformDec2.TransformBlock(aux1, 0, 8, datosEscribir, 8 + l_iAux);
        res = criptoTransformEnc2.TransformBlock(datosEscribir, 8 + l_iAux, 8, datosEscribir, 8 + l_iAux);
        res = criptoTransformDec3.TransformBlock(datosEscribir, 8 + l_iAux, 8, datosEscribir, 8 + l_iAux);

        l_iAux += 8;
    }

private static void XorStr (ref byte[] str1, ref byte[] str2, int qty )
    {
        int i = 0;
        for (i = 0; i < qty; i++ )
            str1[i] = (byte)(str1[i] ^ str2[i]);
    }
于 2015-12-10T12:35:21.573 回答