You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaCard AES128 Cipher doFinal抛出0x6306非法使用异常求助

问题:AES加密触发CryptoException.ILLEGAL_USE(0x6306)错误

我在JavaCard程序中调用split()方法对PIN进行MD5哈希,将哈希结果作为AES128密钥使用,但运行时触发0x6306错误,对应CryptoException.ILLEGAL_USE异常。查阅Oracle官方JavaCard Cipher文档后仍无法解决,相关代码如下:

package baocao;

import javacard.framework.*;
import javacard.security.*;
import javacardx.crypto.*;

public class test extends Applet {

    private final static byte INS_IMPORT = (byte) 0x00;
    private final static byte INS_EXPORT = (byte) 0x01;
    private final static byte INS_VERIFY = (byte) 0x02;
    private final static byte INS_VERIFY_PIN = (byte) 0x03;
    private final static byte INS_CHANGE_PIN = (byte) 0x04;
    private final static byte INS_RECHARGE = (byte) 0x03;
    private final static byte INS_PAYMENT = (byte) 0x04;
    private final static byte INS_RESET_PIN = (byte) 0x07;
    private final static byte P1_EXPORT_ID = (byte) 0x00;
    private final static byte P1_EXPORT_REST = (byte) 0x01;
    private final static byte P1_EXPORT_AVATAR = (byte) 0x02;

    private final static byte PIN_TRY_LIMIT = 0x03;
    private final static byte PIN_SIZE = 0x10;
    private final static short SW_PIN_BLOCKED = 0x6300;
    private final static short SW_VERIFICATION_FAILED = 0x6301;
    private final static short SW_PIN_DUPPLICATED = 0x6302;
    private final static short SW_PIN_VERIFICATION_REQUIRED = 0x6303;
    private final static short SW_UNINITIALIZED_KEY = 0x6304;
    private final static short SW_INVALID_INIT = 0x6305;
    private final static short SW_ILLEGAL_USE = 0x6306;
    private short failedAttempts = 0;

    private MessageDigest sha;
    private Cipher cipher;
    private AESKey aesKey;
    private RSAPrivateKey rsaPrivKey;
    private RSAPublicKey rsaPubKey;
    private Signature rsaSig;
    private OwnerPIN pin;

    private byte[] temp, buffer1, buffer2;
    private final static short MAX_SIZE = (short) 1024;

    private short dataLen, restDataLen, avatarLen, hashedPinLen = (short) 16;
    private byte[] id;
    private byte[] avatar;
    private byte[] restData;
    private byte[] hashedPin;

    private test() {
        temp = new byte[MAX_SIZE];
        buffer1 = new byte[MAX_SIZE];
        buffer2 = new byte[MAX_SIZE];
        sha = MessageDigest.getInstance(MessageDigest.ALG_MD5, false); // MD5 -> 16bytes
        cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false);
        aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, (short) 128, false);
        rsaPrivKey = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, (short) 1024, false);
        rsaPubKey = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, (short) 1024, false);
        KeyPair keyPair = new KeyPair(KeyPair.ALG_RSA, (short) 1024);
        keyPair.genKeyPair();
        rsaPrivKey = (RSAPrivateKey) keyPair.getPrivate();
        rsaPubKey = (RSAPublicKey) keyPair.getPublic();
        pin = new OwnerPIN(PIN_TRY_LIMIT, PIN_SIZE);
    }
    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new test().register(bArray, (short)(bOffset + 1), bArray[bOffset]);
    }

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return;
        }

        byte[] buf = apdu.getBuffer();
        short recvLen = apdu.setIncomingAndReceive();
        short pointer = 0;
        switch (buf[ISO7816.OFFSET_INS]) {
            case INS_IMPORT:
                dataLen = (short)(buf[ISO7816.OFFSET_LC] & 0xf);
                if (dataLen > MAX_SIZE) {
                    ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
                }
                short dataOffset = (short) ISO7816.OFFSET_CDATA;
                pointer = 0;
                while (recvLen > 0) {
                    Util.arrayCopy(buf, dataOffset, temp, pointer, recvLen);
                    pointer += recvLen;
                    recvLen = apdu.receiveBytes(dataOffset);
                }
                split();
                short len = rsaPubKey.getModulus(buffer1, (short) 0);
                Util.arrayCopy(buffer1, (short) 0, buf, (short) 0, len);
                apdu.setOutgoingAndSend((short) 0, len);
                break;

            default:
                ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }
    }


    public void split() {
        short i = 0, j = 0, len = 0, old_i = 0, lenTemp;
        byte opt = 0;

        for (; i < dataLen; i++) {
            if (temp[i] == (byte) 0x00) {
                len = (short)(i - old_i);

                switch (opt) {
                    case 0: // pin
                        Util.arrayCopy(temp, old_i, buffer1, (short) 0, len);
                        sha.doFinal(buffer1, (short) 0, (short) buffer1.length, buffer2, (short) 0);
                        hashedPin = new byte[16];
                        Util.arrayCopy(buffer1, (short) 0, hashedPin, (short) 0, (short) 16);
                        pin.update(hashedPin, (short) 0, (byte) hashedPin.length);
                        aesKey.setKey(hashedPin, (short) 0);
                        cipher.init(aesKey, Cipher.MODE_ENCRYPT);
                        break;

                    case 1: // id
                        id = new byte[len];
                        Util.arrayCopy(temp, old_i, id, (short) 0, len);
                        break;

                    case 2: // avatar
                        avatarLen = len;
                        lenTemp = len;
                        if(len % 8 > 0) lenTemp = (short) (len + 8 - len % 8);
                        byte[] avatarTemp = new byte[lenTemp];
                        avatar = new byte[lenTemp];
                        Util.arrayCopy(temp, old_i, avatarTemp, (short) 0, lenTemp);
                        for (j = len; j < lenTemp; j++) {
                            avatarTemp[j] = 0x00;
                        }
                        try {
                            cipher.doFinal(avatarTemp, (short) 0, lenTemp, avatar, (short) 0);
                        } catch (CryptoException e) {
                            short reason = e.getReason();
                            switch (reason) {
                                case CryptoException.UNINITIALIZED_KEY:
                                    ISOException.throwIt(SW_UNINITIALIZED_KEY);
                                    break;
                                case CryptoException.INVALID_INIT:
                                    ISOException.throwIt(SW_INVALID_INIT);
                                    break;
                                case CryptoException.ILLEGAL_USE:
                                    ISOException.throwIt(SW_ILLEGAL_USE);
                                    break;
                                default:
                                    break;
                            }
                        }
                        
                        break;
                }
                opt++;
                old_i = (short)(i + 1);
            }
        }

        len = (short)(dataLen - old_i);
        restDataLen = len;
        restData = new byte[len];
        Util.arrayCopy(temp, old_i, restData, (short) 0, len);
    }
}

错误原因分析

  1. AES块大小判断错误
    你使用的ALG_AES_BLOCK_128_ECB_NOPAD模式中,AES的块大小固定为16字节,而非代码中写的8字节。当原始数据长度补全后不是16的整数倍时,调用cipher.doFinal()会直接触发ILLEGAL_USE异常。

  2. MD5哈希结果获取错误
    sha.doFinal()的计算结果会写入buffer2,但你却从buffer1复制数据到hashedPin,导致AES密钥实际是原始PIN的前16字节,而非PIN的MD5哈希值,属于核心逻辑错误。

  3. MD5哈希输入长度错误
    调用sha.doFinal()时传入的长度是buffer1.length(1024字节),但实际PIN的有效长度是len,这会导致哈希整个1024字节的缓冲区(包含大量默认0值),而非仅输入的PIN内容。

修复方案

1. 修正AES数据长度补全逻辑

将块大小从8改为16:

// 原代码
if(len % 8 > 0) lenTemp = (short) (len + 8 - len % 8);
// 修改为
if(len % 16 > 0) lenTemp = (short) (len + 16 - len % 16);

2. 正确获取MD5哈希结果

从buffer2复制哈希结果到hashedPin,同时传入正确的PIN长度:

// 原代码
sha.doFinal(buffer1, (short) 0, (short) buffer1.length, buffer2, (short) 0);
hashedPin = new byte[16];
Util.arrayCopy(buffer1, (short) 0, hashedPin, (short) 0, (short) 16);
// 修改为
sha.doFinal(buffer1, (short) 0, len, buffer2, (short) 0);
hashedPin = new byte[16];
Util.arrayCopy(buffer2, (short) 0, hashedPin, (short) 0, (short) 16);

3. 验证密钥初始化

确保aesKey.setKey()使用的是正确的哈希值,并且cipher.init()执行成功后再进行加密操作。

内容的提问来源于stack exchange,提问作者Lucia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 18:58:08