编程语言
首页 > 编程语言> > java – 如何解密从Mifare Desfire EV1发送的第一条消息

java – 如何解密从Mifare Desfire EV1发送的第一条消息

作者:互联网

有没有人知道如何解密从卡发送的第一条消息?我的意思是在身份验证成功后然后你发送一个命令(例如0x51(GetRealTagUID).它返回00 random32bits(总是不同).我尝试解密它:

        private byte[] decrypt(byte[] raw, byte[] encrypted, byte[] iv)
            throws Exception {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
    }

用decrypt调用它(sessionKey,response,iv)

IV =全零(16字节)

响应= 0x51命令后的32randombits(刚刚删除了两个零)

有人告诉我,第一次发送命令(0x51)后IV发生了变化.如何生成正确的IV来解密该响应?我认为全零是错误的,因为解密的消息总是不同的,并且它应该始终与同一张卡相同.

-编辑-

在应用您的(Michael Roland)指令后,解密的响应仍然只是随机位.这是我的代码(我想我做错了):

            byte[] x = encrypt(sessionKey, iv, iv);

            byte[] rx = rotateBitsLeft(x);

            if ((rx[15] & 0x01) == 0x01)
                rx[15] = (byte) (rx[15] ^ 0x87);

            if ((rx[15] & 0x01) == 0x00)
                rx[15] = (byte) (rx[15] ^ 0x01);

            byte[] crc_k1 = rx;

            byte[] rrx = rotateBitsLeft(rx);

            if ((rrx[15] & 0x01) == 0x01)
                rrx[15] = (byte) (rrx[15] ^ 0x87);

            if ((rrx[15] & 0x01) == 0x00)
                rrx[15] = (byte) (rrx[15] ^ 0x01);

            byte[] crc_k2 = rrx;

            byte[] command = { (byte) 0x51, (byte) 0x80, (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 };

            for (int i = 0; i < 16; i++){
                command[i] = (byte) (command[i] ^ crc_k2[i]);
            }

            byte[] iv2 = encrypt(sessionKey, command, iv);

            byte[] RealUID = decrypt(sessionKey, ReadDataParsed, iv2);

            Log.e("RealUID", ByteArrayToHexString(RealUID));

-EDIT3-

仍然总是返回不同的值.我认为问题可能在于:

byte[] iv2 = encrypt(sessionKey, command, iv);

在创建用于解密响应的新IV时使用什么IV?那里全是零.

解决方法:

身份验证后,IV将重置为全零.在使用AES身份验证时,您必须为每个后续命令计算CMAC(即使CMAC实际上未附加到命令中).因此,对命令进行CMAC计算将导致正确的IV初始化以解码响应.即该命令的CMAC等于用于解密响应的IV.类似地,对于所有其他命令,IV是来自先前加密/ CMAC的最后一个密码块.

更新:

如何计算CMAC垫XOR值

>使用会话密钥(使用零的IV)加密一个零块(0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00). – &GT X [0..15]
>向左旋转x [0..15]一位. – &GT RX [0..15]
>如果最后一位(rx [15]中的位0)为1:xor rx [15]为0x86.
>将rx [0..15]存储为crc_k1 [0..15].
>向左旋转rx [0..15]一位. – &GT RRX [0..15]
>如果最后一位(rrx [15]中的位0)为1:xor rrx [15]为0x86.
>将rrx [0..15]存储为crc_k2 [0..15].

如何计算CMAC

>使用0x80 0x00 0x00 …将命令填充到密码的块大小(AES为16字节).如果命令长度与块大小的倍数匹配,则不添加填充.
>对于您的命令(0x51),这将看起来像:0x51 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
>如果添加了填充,则使用crc_k2 [0..15]填充填充命令的最后16个字节.
>如果未添加填充,则使用crc_k1 [0..15] xor命令的最后16个字节.
>加密(在发送模式,即enc(IV xor数据块),前一个块的密文是新的IV)结果与会话密钥.
>最后一个块的密文是CMAC和新的IV.

更新2:

如何将位向量向左旋转一位

public void rotateLeft(byte[] data) {
    byte t = (byte)((data[0] >>> 7) & 0x001);
    for (int i = 0; i < (data.length - 1); ++i) {
        data[i] = (byte)(((data[i] << 1) & 0x0FE) | ((data[i + 1] >>> 7) & 0x001));
    }
    data[data.length - 1] = (byte)(((data[data.length - 1] << 1) & 0x0FE) | t);
}

标签:java,encryption,aes,cryptography,mifare
来源: https://codeday.me/bug/20191004/1851575.html