更改 DESFire master key.What 时出现 0x1E 错误 (INTEGRITY_ERROR) 是我的错误吗?我该如何解决?
I got 0x1E error (INTEGRITY_ERROR) while change DESFire master key.What are my mistakes?And How can I resolve?
整个更新1:再看问题。
我最近在使用 DESFire
卡。我现在决定更改 PICC 的默认 master key。
(我已经可以通过主密钥验证所有 8 字节 0x00 成功)
1- 默认 master key
是 zero.It 的 8 byte
是 00 00 00 00 00 00 00 00
.
2-New master key
我选的是16byte
.
是:
byte[] newPICCKey= new byte[]{(byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44,
(byte)0x55, (byte)0x66, (byte)0x77 ,(byte)0x88,
(byte)0x12, (byte)0x23, (byte)0x34 ,(byte)0x45 ,
(byte)0x56, (byte)0x67, (byte)0x78 ,(byte)0x89};
3- 我使用旧的主密钥进行身份验证(它仍然没有更改,仍然是 8 个零字节。)成功。
随机数是:
随机A = 一个8字节数
随机 B = 一个 8 字节数
4 - 我每次都使用随机 A 和随机 B 创建会话密钥:
会话密钥 = 随机 A 的前 4 字节 + 随机 B 的前 4 字节
//fill sessionKey with RandomA and RandomB
for(int i=0; i<4; i++)
sessionKey[i] = randomA[i];
for(int i=4; i<8; i++)
sessionKey[i] = randomB[i-4];
5 - 我使用波纹管方法为新的主密钥创建 CRC16。结果是:D8 EC
(新的PICC主密钥可以是16字节的吗?还是必须是8字节的?顺便说一句,我为新的主密钥选择了16字节的值)
public class CRC16
{
public static short Crc16(byte[] buffer, short offset, short len)
{
short crcTmp = 0x6363;
for (int i = 0; i < len; ++i)
{
short temp = (short)(buffer[offset + i] ^ crcTmp);
temp = (short)((temp ^ (temp << 4)) & 0xff);
crcTmp = (short)(((crcTmp >> 8) & 0xff) ^ (temp << 8) ^ (temp << 3) ^ (temp >> 4));
}
return crcTmp;
}
}
6 - 我用 crc 和填充(解密)创建了新的 PICC 密钥
byte[] newPICCKey_deciphered = new byte[]{(byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44,
(byte)0x55, (byte)0x66, (byte)0x77 ,(byte)0x88,
(byte)0x12, (byte)0x23, (byte)0x34 ,(byte)0x45 ,
(byte)0x56, (byte)0x67, (byte)0x78 ,(byte)0x89,
(byte)0x00 , (byte)0x00,
(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,
(byte)0x00 ,(byte)0x00 ,(byte)0x00 };
txtNewPICCKeyDeciphered.setText(Utils.bytesToHex(newPICCKey_deciphered));
7- 我计算了 crc16 并将其添加到我的新密钥中:
short res = CRC16_3.Crc16(newPICCKey, (short)0, (short)16);
newPICCKey_deciphered[16] = (byte) (res & 0xFF);
newPICCKey_deciphered[17] = (byte) ((res >> 8) & 0xFF);
8 - 我用下面的方法加密了上面的新主密钥,我得到了一个 24 字节的加密:
byte[] iv1=new byte[]{(byte)0x00 , (byte)0x00 , (byte)0x00 , (byte)0x00 ,
(byte)0x00 , (byte)0x00 , (byte)0x00 , (byte)0x00 };
byte[] newPICCKeyEnciphered = new byte[24];
//..............................
byte[] block1 = new byte[]{(byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44,
(byte)0x55, (byte)0x66, (byte)0x77 ,(byte)0x88};
byte[] block2 = new byte[]{
(byte)0x11, (byte)0x22, (byte)0x33 ,(byte)0x44 ,
(byte)0x55, (byte)0x66, (byte)0x77 ,(byte)0x88};
byte[] block3 = new byte[]{(byte)0x00 , (byte)0x00,
(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,
(byte)0x00 ,(byte)0x00 ,(byte)0x00};
block3[0] = newPICCKey_deciphered[16];
block3[1] = newPICCKey_deciphered[17];
try
{
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
SecretKeyFactory desKeyFact = SecretKeyFactory.getInstance("DES");
DESKeySpec desKeySpec = new DESKeySpec(sessionKey);
SecretKey s = desKeyFact.generateSecret(desKeySpec);
cipher.init(Cipher.DECRYPT_MODE, s);
byte[] r1 = new byte[8];
r1 =Utils.doXorTwoByteArray(block1, iv1);
byte[] r2 = new byte[8];
r2 = cipher.doFinal(r1, 0, 8);
//...............
byte[] r3 = new byte[8];
r3 =Utils.doXorTwoByteArray(block2, r2);
byte[] r4 = new byte[8];
r4 =cipher.doFinal(r3, 0, 8);
//................
byte[] r5 = new byte[8];
r5 =Utils.doXorTwoByteArray(block3, r4);
byte[] r6 = new byte[8];
r6 =cipher.doFinal(r5, 0, 8);
for(int i=0; i<8;i++)
newPICCKeyEnciphered[i] = r2[i];
for(int i=8; i<16;i++)
newPICCKeyEnciphered[i] = r4[i-8];
for(int i=16; i<24;i++)
newPICCKeyEnciphered[i] = r6[i-16];
}
catch(Exception e)
{
e.printStackTrace();
}
这里是Class加密时使用的DES:
public class DES {
public static byte[] doDecryptData(byte[] OriginalData,byte[]key , int sizeKey , byte[] iv , int sizeIV)
{
byte[] masterKeyBytes =new byte[sizeKey];
masterKeyBytes = key;
byte[] ivBytes = new byte[sizeIV];
ivBytes = iv;
byte[] encipheredData=new byte[sizeIV];
try{
DESKeySpec desKeySpec = new DESKeySpec(masterKeyBytes);
SecretKeyFactory desKeyFact = SecretKeyFactory.getInstance("DES");
SecretKey s = desKeyFact.generateSecret(desKeySpec);
Cipher aliceCipher = Cipher.getInstance("DES/CBC/NoPadding");
aliceCipher.init(Cipher.DECRYPT_MODE, s, new IvParameterSpec(ivBytes));
encipheredData= aliceCipher.doFinal(OriginalData);
return encipheredData;
}
catch(Exception e)
{
Log.e("error", "111"+e.toString());
}
return null;
}
9- 最后我填充参数字节数组并使用指令 (c4) 将其发送到
byte[] cmd = new byte[]{(byte)0x00 ,
(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00 };
//fill cmd
for(int i=1 ;i<cmd.length ; i++)
cmd[i] = newPICCKeyEnciphered[i -1];
try {
responseChangeKey = isodep.transceive(Utils.wrapMessage((byte)0xC4, cmd));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
这里是 Utils class:
public class Utils {
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);
byte[] b = stream.toByteArray();
return b;
}
}
我在最后一步(发送 apdu 以更改主密钥到卡)收到异常 0x1E 错误,这意味着完整性错误:CRC 或 MAC 不匹配数据
填充字节无效。
我怎样才能正确执行更改密钥? me.thanks.
有必要
- DESFire 卡使用的 CRC16 与您的略有不同。 CRC16 的初始值为
0xFFFF
,但 DESFire 期望 0x6363
。这可能会给您带来完整性错误的麻烦。
- 我没有看到 DEC/CBC 使用解密您的
newPICCKey_deciphered
数组进行加密。加密时你真的必须使用 DECRYPTION MODE 而不是 ENCRYPTION MODE (虽然这看起来很奇怪)。这意味着:
aliceCipher.init(Cipher.DECRYPT_MODE, s, new IvParameterSpec(ivBytes));
并且由于 CBC 解密模式与加密 CBC 模式不同这一事实,您必须实施自己的 CBC 模式,并且始终仅对具有正确 IV 的一个块使用 aliceCipher
。
整个更新1:再看问题。
我最近在使用 DESFire
卡。我现在决定更改 PICC 的默认 master key。
(我已经可以通过主密钥验证所有 8 字节 0x00 成功)
1- 默认 master key
是 zero.It 的 8 byte
是 00 00 00 00 00 00 00 00
.
2-New master key
我选的是16byte
.
是:
byte[] newPICCKey= new byte[]{(byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44,
(byte)0x55, (byte)0x66, (byte)0x77 ,(byte)0x88,
(byte)0x12, (byte)0x23, (byte)0x34 ,(byte)0x45 ,
(byte)0x56, (byte)0x67, (byte)0x78 ,(byte)0x89};
3- 我使用旧的主密钥进行身份验证(它仍然没有更改,仍然是 8 个零字节。)成功。 随机数是:
随机A = 一个8字节数
随机 B = 一个 8 字节数
4 - 我每次都使用随机 A 和随机 B 创建会话密钥: 会话密钥 = 随机 A 的前 4 字节 + 随机 B 的前 4 字节
//fill sessionKey with RandomA and RandomB
for(int i=0; i<4; i++)
sessionKey[i] = randomA[i];
for(int i=4; i<8; i++)
sessionKey[i] = randomB[i-4];
5 - 我使用波纹管方法为新的主密钥创建 CRC16。结果是:D8 EC (新的PICC主密钥可以是16字节的吗?还是必须是8字节的?顺便说一句,我为新的主密钥选择了16字节的值)
public class CRC16
{
public static short Crc16(byte[] buffer, short offset, short len)
{
short crcTmp = 0x6363;
for (int i = 0; i < len; ++i)
{
short temp = (short)(buffer[offset + i] ^ crcTmp);
temp = (short)((temp ^ (temp << 4)) & 0xff);
crcTmp = (short)(((crcTmp >> 8) & 0xff) ^ (temp << 8) ^ (temp << 3) ^ (temp >> 4));
}
return crcTmp;
}
}
6 - 我用 crc 和填充(解密)创建了新的 PICC 密钥
byte[] newPICCKey_deciphered = new byte[]{(byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44,
(byte)0x55, (byte)0x66, (byte)0x77 ,(byte)0x88,
(byte)0x12, (byte)0x23, (byte)0x34 ,(byte)0x45 ,
(byte)0x56, (byte)0x67, (byte)0x78 ,(byte)0x89,
(byte)0x00 , (byte)0x00,
(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,
(byte)0x00 ,(byte)0x00 ,(byte)0x00 };
txtNewPICCKeyDeciphered.setText(Utils.bytesToHex(newPICCKey_deciphered));
7- 我计算了 crc16 并将其添加到我的新密钥中:
short res = CRC16_3.Crc16(newPICCKey, (short)0, (short)16);
newPICCKey_deciphered[16] = (byte) (res & 0xFF);
newPICCKey_deciphered[17] = (byte) ((res >> 8) & 0xFF);
8 - 我用下面的方法加密了上面的新主密钥,我得到了一个 24 字节的加密:
byte[] iv1=new byte[]{(byte)0x00 , (byte)0x00 , (byte)0x00 , (byte)0x00 ,
(byte)0x00 , (byte)0x00 , (byte)0x00 , (byte)0x00 };
byte[] newPICCKeyEnciphered = new byte[24];
//..............................
byte[] block1 = new byte[]{(byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44,
(byte)0x55, (byte)0x66, (byte)0x77 ,(byte)0x88};
byte[] block2 = new byte[]{
(byte)0x11, (byte)0x22, (byte)0x33 ,(byte)0x44 ,
(byte)0x55, (byte)0x66, (byte)0x77 ,(byte)0x88};
byte[] block3 = new byte[]{(byte)0x00 , (byte)0x00,
(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,
(byte)0x00 ,(byte)0x00 ,(byte)0x00};
block3[0] = newPICCKey_deciphered[16];
block3[1] = newPICCKey_deciphered[17];
try
{
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
SecretKeyFactory desKeyFact = SecretKeyFactory.getInstance("DES");
DESKeySpec desKeySpec = new DESKeySpec(sessionKey);
SecretKey s = desKeyFact.generateSecret(desKeySpec);
cipher.init(Cipher.DECRYPT_MODE, s);
byte[] r1 = new byte[8];
r1 =Utils.doXorTwoByteArray(block1, iv1);
byte[] r2 = new byte[8];
r2 = cipher.doFinal(r1, 0, 8);
//...............
byte[] r3 = new byte[8];
r3 =Utils.doXorTwoByteArray(block2, r2);
byte[] r4 = new byte[8];
r4 =cipher.doFinal(r3, 0, 8);
//................
byte[] r5 = new byte[8];
r5 =Utils.doXorTwoByteArray(block3, r4);
byte[] r6 = new byte[8];
r6 =cipher.doFinal(r5, 0, 8);
for(int i=0; i<8;i++)
newPICCKeyEnciphered[i] = r2[i];
for(int i=8; i<16;i++)
newPICCKeyEnciphered[i] = r4[i-8];
for(int i=16; i<24;i++)
newPICCKeyEnciphered[i] = r6[i-16];
}
catch(Exception e)
{
e.printStackTrace();
}
这里是Class加密时使用的DES:
public class DES {
public static byte[] doDecryptData(byte[] OriginalData,byte[]key , int sizeKey , byte[] iv , int sizeIV)
{
byte[] masterKeyBytes =new byte[sizeKey];
masterKeyBytes = key;
byte[] ivBytes = new byte[sizeIV];
ivBytes = iv;
byte[] encipheredData=new byte[sizeIV];
try{
DESKeySpec desKeySpec = new DESKeySpec(masterKeyBytes);
SecretKeyFactory desKeyFact = SecretKeyFactory.getInstance("DES");
SecretKey s = desKeyFact.generateSecret(desKeySpec);
Cipher aliceCipher = Cipher.getInstance("DES/CBC/NoPadding");
aliceCipher.init(Cipher.DECRYPT_MODE, s, new IvParameterSpec(ivBytes));
encipheredData= aliceCipher.doFinal(OriginalData);
return encipheredData;
}
catch(Exception e)
{
Log.e("error", "111"+e.toString());
}
return null;
}
9- 最后我填充参数字节数组并使用指令 (c4) 将其发送到
byte[] cmd = new byte[]{(byte)0x00 ,
(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00
,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00 };
//fill cmd
for(int i=1 ;i<cmd.length ; i++)
cmd[i] = newPICCKeyEnciphered[i -1];
try {
responseChangeKey = isodep.transceive(Utils.wrapMessage((byte)0xC4, cmd));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
这里是 Utils class:
public class Utils {
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);
byte[] b = stream.toByteArray();
return b;
}
}
我在最后一步(发送 apdu 以更改主密钥到卡)收到异常 0x1E 错误,这意味着完整性错误:CRC 或 MAC 不匹配数据 填充字节无效。 我怎样才能正确执行更改密钥? me.thanks.
有必要- DESFire 卡使用的 CRC16 与您的略有不同。 CRC16 的初始值为
0xFFFF
,但 DESFire 期望0x6363
。这可能会给您带来完整性错误的麻烦。 - 我没有看到 DEC/CBC 使用解密您的
newPICCKey_deciphered
数组进行加密。加密时你真的必须使用 DECRYPTION MODE 而不是 ENCRYPTION MODE (虽然这看起来很奇怪)。这意味着:
aliceCipher.init(Cipher.DECRYPT_MODE, s, new IvParameterSpec(ivBytes));
并且由于 CBC 解密模式与加密 CBC 模式不同这一事实,您必须实施自己的 CBC 模式,并且始终仅对具有正确 IV 的一个块使用 aliceCipher
。