9

AndroidアプリケーションでDESFireカードを認証しようとしています。このリンクの例を使用して、カードから取得したバイトを解読します。そのため、DESFireのドキュメントで指摘されているため、復号化でのパディングを除外しました(以下でコメント)。また、そうしないと、復号化は8バイトの入力に対して7バイトを返します。以下は、私が使用する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のドキュメントによると、送信と受信の2つの復号化モードが必要です。このブログ投稿には、それについての説明があります。

ただし、DESFire暗号化は通常のDES / CBCスキームとは少し異なります。PCDはデータの送信時にDESの「送信モード」を使用し(DESの前のxor)、カードはデータの受信時にDESの「受信モード」を使用します(DESの後にxor)。 )。ただし、PCDがデータを受信するときは、通常のDES / CBCモード(DESの後のxor)を使用し、カードはデータを送信するときに(DESの前のxor)通常の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を取得します。ただし、2番目の「結果」は常に「91ae」であり、認証エラーを意味します。私はここで何か間違ったことをしています。間違ったデータをカードに送信します。

これらのモードで動作するためにコードを変更する必要があることを誰かに教えてもらえますか?TripleDESの前後のデータと何をXORする必要がありますか?

本当の質問ではありませんが、DesFireカードのデフォルトの「キー」は16個のゼロバイトであると読みました。また、このドキュメントでは、16バイトのキーにTripleDESを使用し、8バイトのキーにDESを使用する必要があると指摘しています。デフォルトのキーを変更していないので、TripleDESを使用していますが、使用する必要があります。

CipherBlockChainingについて知る必要がある人のために。

編集:TripleDESの前後にXORを実行する必要があり、TripleDESの内部操作にまったく触れてはならないことがわかりました。しばらくしてみます。

質問を初めて見た人のために言って、内側のTripleDES行を削除しました。

4

2 に答える 2

10

OK私は解決策を得ました。私の間違いは私が送っていたということでした

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が問題の1つです。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の前にxor)、カードはデータを受信するときにDESの「受信モード」を使用します(DESの後にxor)

そのため、以前に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 に答える