AES Applet return 不同卡上的不同结果

AES Applet return different result on different cards

我写了下面的小程序在CBC和ECB模式下做AES加解密:

package cryptoPack;

import javacard.framework.*;
import javacard.security.AESKey;
import javacard.security.CryptoException;
import javacard.security.KeyBuilder;
import javacardx.crypto.Cipher;

public class CryptoAES extends Applet {

    // Abbreviations
    private static final boolean NO_EXTERNAL_ACCESS = false;

    // AES Cipher AND its required key
    Cipher cipher;
    AESKey AESkey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, NO_EXTERNAL_ACCESS);

    // A fixed Initial vector for AES CBC mode.
    public byte[] IV = { (byte) 0x00, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66,
            (byte) 0x77, (byte) 0x88, (byte) 0x99, (byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE,
            (byte) 0xFF };

    // Defining switch case variables for supported instructions ::: INS in APDU
    // command
    final byte SET_KEY = (byte) 0xC0;
    final byte DO_CRYPTO = (byte) 0xC2;

    // Defining switch case variables for cipher algorithms ::: P1 in APDU
    // command
    final byte AES_BLOCK_128_CBC_NOPAD = (byte) 0x00;
    final byte AES_BLOCK_128_ECB_NOPAD = (byte) 0x01;

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new CryptoAES();
    }

    protected CryptoAES() {
        register();
    }

    public void process(APDU apdu) {

        if (selectingApplet()) {
            return;
        }

        byte[] buffer = apdu.getBuffer();

        // Analyzing the command.
        try {

            switch (buffer[ISO7816.OFFSET_INS]) {

            case SET_KEY:
                setKeyAndInit(apdu);
                break;

            case DO_CRYPTO:
                do_crypto(apdu);

                break;

            default:
                ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);

            }

        } catch (CryptoException e) {
            ISOException.throwIt(((CryptoException) e).getReason());
        }
    }



    public void setKeyAndInit(APDU apdu) throws ISOException {
        byte[] buffer = apdu.getBuffer();
        if (buffer[ISO7816.OFFSET_LC] == 16) {
            AESkey.setKey(buffer, (short) ISO7816.OFFSET_CDATA);
        } else {
            ISOException.throwIt(ISO7816.SW_DATA_INVALID);
        }

        switch (buffer[ISO7816.OFFSET_P1]) {
        case AES_BLOCK_128_CBC_NOPAD:
            cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, NO_EXTERNAL_ACCESS);
            break;
        case AES_BLOCK_128_ECB_NOPAD:
            cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, NO_EXTERNAL_ACCESS);
            break;
        }
    }



    public void do_crypto(APDU apdu) throws ISOException {
        byte[] buffer = apdu.getBuffer();

        short datalen = apdu.setIncomingAndReceive();
        if ((datalen % 16) != 0) {
            ISOException.throwIt(ISO7816.SW_DATA_INVALID);
        }

        byte[] out_data = JCSystem.makeTransientByteArray((short) 16, JCSystem.CLEAR_ON_DESELECT);

        switch (buffer[ISO7816.OFFSET_P1]) {

        case AES_BLOCK_128_CBC_NOPAD:
            if(buffer[ISO7816.OFFSET_P2]== 0x00){
                cipher.init(AESkey, Cipher.MODE_DECRYPT, IV, (short) 0x00, (short) 0x10);       
            }else{
                cipher.init(AESkey, Cipher.MODE_ENCRYPT, IV, (short) 0x00, (short) 0x10);       
            }

            break;

        case AES_BLOCK_128_ECB_NOPAD:
            if(buffer[ISO7816.OFFSET_P2]== 0x00){
                cipher.init(AESkey, Cipher.MODE_DECRYPT);       
            }else{
                cipher.init(AESkey, Cipher.MODE_ENCRYPT);       
            }
            break;

        default:
            break;
        }

        short out_data_len = cipher.doFinal(buffer, (short) ISO7816.OFFSET_CDATA, datalen, out_data,
                (short) 0);
        Util.arrayCopyNonAtomic(out_data, (short) 0, buffer, (short) 0, out_data_len);
        apdu.setOutgoingAndSend((short) 0, out_data_len);
    }

}

在三个不同的卡上安装上面的小程序后,我有以下结果:

NXP JCOP v2.4.2 r3 - T=1 TPDU:

Send: 00 A4 04 00 06 01 02 03 04 07 01 00
Recv: 90 00
Time used: 15.000 ms
Send: 00 C0 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 90 00
Time used: 123.000 ms
Send: 00 C2 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: C6 11 20 8A 02 37 B4 21 82 80 BC 62 CB 14 6C 46 90 00
Time used: 102.000 ms
Send: 00 C2 00 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: BB E0 95 7A 71 0E 04 4B FB 6B 5B 81 04 F6 7A A1 90 00
Time used: 72.000 ms
Send: 00 C2 01 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: C6 00 02 B9 46 62 D2 56 0A 19 16 D9 07 C9 82 B9 90 00
Time used: 73.000 ms
Send: 00 C2 01 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: E5 6E 26 F5 60 8B 8D 26 8F 25 56 E1 98 A0 E0 1B 90 00
Time used: 73.000 ms

两张其他卡片 - T=0 TPDU:

Send: 00 A4 04 00 06 01 02 03 04 07 01 00
Recv: 90 00
Time used: 675.000 ms
Send: 00 C0 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 90 00
Time used: 57.000 ms
Send: 00 C2 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: A8 03 C8 BC 28 C3 C9 AD EB 56 82 55 9B 7A 68 1E 90 00
Time used: 88.000 ms
Send: 00 C2 00 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 74 3F B8 61 66 76 1C E0 B3 85 A3 AF E7 55 D1 29 90 00
Time used: 80.000 ms
Send: 00 C2 01 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: A8 12 EA 8F 6C 96 AF DA 63 CF 28 EE 57 A7 86 E1 90 00
Time used: 86.000 ms
Send: 00 C2 01 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: FF A3 C7 ED 04 71 0B 98 06 7D AE 68 15 E2 75 1F 90 00
Time used: 83.000 ms

正如您在上面看到的,结果是不同的。我将以上结果与 this online tool 进行了比较。看来我的 NXP JCOP 卡工作正常。另外两张卡有什么问题?

在访问 INS == 0xC0 (setKeyAndInit) 的 APDU 缓冲区之前未调用 setIncomingAndReceive 方法。

The applet receives the APDU instance to process from the Java Card runtime environment in the Applet.process(APDU) method, and the first five header bytes [ CLA, INS, P1, P2, P3 ] are available in the APDU buffer.

在调用 setIncomingAndReceive 之前 APDU 缓冲区中没有保证数据部分,您不应该访问它。但是,实际行为通常取决于特定的 Java 卡实现,这就是为什么您的小程序在 NXP 卡上正常工作的原因。