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); } }
错误原因分析
AES块大小判断错误
你使用的ALG_AES_BLOCK_128_ECB_NOPAD模式中,AES的块大小固定为16字节,而非代码中写的8字节。当原始数据长度补全后不是16的整数倍时,调用cipher.doFinal()会直接触发ILLEGAL_USE异常。MD5哈希结果获取错误
sha.doFinal()的计算结果会写入buffer2,但你却从buffer1复制数据到hashedPin,导致AES密钥实际是原始PIN的前16字节,而非PIN的MD5哈希值,属于核心逻辑错误。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
相关产品推荐
相关产品推荐

