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行を削除しました。