DES发送和接收模式,用于DESFire身份验证

İsm*_*kan 9 java android des nfc mifare

我正在尝试使用我的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;
}
Run Code Online (Sandbox Code Playgroud)

根据DesFire文档,我需要两种解密,发送和接收模式.这篇博文有一些解释.

然而,DESFire非密码是从正常DES/CBC方案略有不同:该PCD使用DES"发送模式"(DES之前XOR)发送数据时,和该卡使用DES"收到模式" DES后recieving数据(XOR时).但是当PCD接收数据时,它使用正常的DES/CBC模式(在DES之后的xor),并且卡在发送数据时使用正常的DES发送模式(在DES之前为xor).

在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)));
Run Code Online (Sandbox Code Playgroud)

我得到第一个结果,加密的randB.但是,第二个"结果"始终为"91ae",表示验证错误.我在这里做错了,把错误的数据发送到卡上.

任何人都可以告诉我在代码中我必须更改哪些才能在这些模式下工作?在TripleDES之前/之后我应该对数据进行异或运算?

不是真正的问题,但我读到DesFire卡中的默认"Key"是16个零字节.该文件还指出,我需要将TripleDES用于16字节密钥,DES用于8字节密钥.所以我正在使用并且需要使用TripleDES,因为我没有更改默认密钥,我是对的吗?

对于那些需要了解CipherBlockChaining的人.

编辑:我发现我需要在TripleDES之前和之后进行XORing,我根本不能触及TripleDES的内部操作.我会在一段时间内尝试.

删除了内部TripleDES线,只是说那些第一次看到问题的人.

İsm*_*kan 10

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

3DES(randA + randB') 
Run Code Online (Sandbox Code Playgroud)

但我应该发送

3DES(randA) + 3DES(randB' XOR 3DES(randA))
Run Code Online (Sandbox Code Playgroud)

这是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));
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

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

Cipher.getInstance("DESede/CBC/NoPadding");
Run Code Online (Sandbox Code Playgroud)

  • 感谢您回复.注意`3DES(randA)+ 3DES(randB'XOR 3DES(randA))`仅仅意味着`TDES_CBC(randA | randB)`为零IV. (2认同)
  • 你为什么不用`Cipher.getInstance("DESede/CBC/NoPadding")`??? (2认同)