【问题标题】:I got 0x1E error (INTEGRITY_ERROR) while change DESFire master key.What are my mistakes?And How can I resolve?更改 DESFire 主密钥时出现 0x1E 错误(INTEGRITY_ERROR)。我的错误是什么?我该如何解决?
【发布时间】:2015-05-11 11:18:33
【问题描述】:

整个更新1:再次查看问题。

我最近正在使用DESFire 卡。我现在决定更改PICC 的默认主密钥

(我已经可以成功使用主密钥全部 8 字节 0x00 进行身份验证)

1- 默认master key 是零的 8 个byte。它是00 00 00 00 00 00 00 00

2-我选择的新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 - 我每次都使用 Random A 和 Random B 创建会话密钥: 会话密钥 = Random A 的前 4 个字节 + Random 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();
                }

这是用于加密的 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 类:

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 与数据不匹配 填充字节无效。 如何正确执行更改密钥?这对我来说是必要的。谢谢。

【问题讨论】:

  • block1block2 相同,这可能是问题...

标签: java apdu crc16 contactless-smartcard


【解决方案1】:
  1. DESFire 卡使用的 CRC16 与您的略有不同。 CRC16 的初始值为 0xFFFF,但 DESFire 需要 0x6363。这可能会导致您遇到完整性错误。
  2. 我看不到使用您的 newPICCKey_deciphered 数组解密的 DEC/CBC 加密。加密时您确实必须使用 DECRYPTION MODE 而不是 ENCRYPTION MODE(尽管它看起来很奇怪)。这意味着:

aliceCipher.init(Cipher.DECRYPT_MODE, s, new IvParameterSpec(ivBytes));

而且由于解密的 CBC 模式与加密的 CBC 模式不同,您必须实现自己的 CBC 模式并始终使用 aliceCipher 用于一个块,仅使用正确的 IV。

【讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • @setareab 请不要使用 cmets 将回答者推荐给您的新问题。或者在收到您的回答后直接删除它们。 stackoverflow 不应该在答案下方对提问者的其他问题有很多参考。所以请删除以上所有三个 cmets。他们对未来的观众没有帮助。谢谢。 :)(我也会删除我的评论。)
猜你喜欢
  • 2020-03-30
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 2016-11-18
相关资源
最近更新 更多