DES发送和接收模式的DESFire非认证模式、DES、DESFire

2023-09-06 00:13:30 作者:俄把心寄错了地址

我想用我的Andr​​oid应用程序验证的DESFire卡。我在这个环节到正是DeCypher我从卡上得到的字节数。对于这一点,我排除了填充解密(注释掉以下),因为DESFire非文件指出出来。另外,如果我不这样做,解密返回7个字节的8个字节的输入。下面是DES和TripleDES的解密功能,我使用:

I'm trying to authenticate DESFire card with my android application. I use the example in this link to decypher the bytes I got from the card. For that, I ruled out padding in decryption (commented out below), because DESFire documentation points it out. Also, if I don't do so, decryption returns 7 bytes for input of 8 bytes. Below are DES and TripleDES decryption functions I use:

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文件,我需要两种模式解密,发送和接收。 本博客文章有一些解释关于它。

According to DesFire document, I need two modes of decryption, send and receive. This blog post has some explanation about it.

不过,DESFire非加密是从正常的DES / CBC方式有点不同:PCD的使用DES(DES前XOR)发送数据时,发送模式,并且该显卡采用DES免费获赠模式recieving数据时( DES后XOR)。但是,当PCD临危数据,它使用正常的DES / CBC模式(异或DES后),和卡使用常规的DES发送数据时(异或DES之前)发送模式

However, the DESFire crypto is a bit different from the normal DES/CBC scheme: The PCD uses DES "send mode" when sending data (xor before DES), and the card uses DES "recieve mode" when recieving data (xor after DES). But when the PCD recieves data, it uses normal DES/CBC mode (xor after DES), and the card uses normal DES send mode when sending data (xor before DES).

和Android中端我下面的例子和建议:

And in Android side I follow the examples and recommendations:

// 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,意味着认证错误。我在这里做得不对,发送错误的数据卡。

I get the first result, the encrypted randB. However, the second "result" is always "91ae", means authentication error. I'm doing something wrong here, send wrong data to card.

谁能告诉我在code我必须改变在这些模式下工作? TripleDes的前/后我应该怎么XOR数据?

Can anyone tell me what must I change in the code to work in these modes? What should I XOR with data before/after TripleDES?

不是真正的问题,但我读到的DESFIRE卡默认密钥是16零字节。另外,我需要使用TripleDes的16个字节的密钥,DES的8个字节的密钥文件点。所以,我使用的,需要使用的TripleDES因为我并没有改变默认的关键,是吗?

Not the real question, but I read that default "Key" in DesFire card is 16 zero bytes. Also the document points that I need to use TripleDES for 16 bytes of key, DES for 8 bytes of key. So I'm using and need to use TripleDES as I haven't changed the default key, am I right?

对于那些谁需要知道 CipherBlockChaining 。

修改:我发现我需要前后TripleDes的后做异或,我摸不得TripleDes的内部运作的。我将尝试在一段时间。

EDIT: I found out that I need to do XORing before and after TripleDES and I mustn't touch TripleDES's internal operations at all. I will be trying that in a while.

删除内TripleDes的线条,只是说对于那些看到了第一次的问题。

Deleted the inner TripleDES lines, just saying for the ones seeing the question for the first time.

推荐答案

确定我得到了解决。我的错误是,我会发送

OK I got the solution. My mistake was that I was sending

3DES(randA + randB') 

不过,我应该送

But I should send

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

下面是验证code为Android / Java的(这是太难过了,这是可以发现在网络上目前唯一的一个!):

Here's the authentication code for Android/Java (it's so sad that this is the only one that can be found on the net currently!):

实际验证code:

// 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功能:

TripleDes is the one in the question. wrapMessage function:

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功能,只需拨打:

Thanks to VGe0rge, we found out the reason why this authentication doesn't work from time to time. Instead of calling the 3DES function in the question, just call:

Cipher.getInstance("DESede/CBC/NoPadding");