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

Java Card RSA加解密报CryptoException原因码5问题求解

问题根因

你遇到的CryptoException原因码5(ILLEGAL_USE,对应填充校验失败)核心是三个低级错误导致的,和Cipher算法参数配置无关:

  • 密文传输截断,解密输入长度非法:RSA算法的输出密文长度必须和密钥模长完全相等。你配置的是2048位RSA,对应模长256字节,正常加密返回的密文应该固定为256字节,但你测试只拿到63字节密文,说明密文在APDU传输环节被截断了。一方面你读LC字段的写法错误:short lc=buffer[ISO7816.OFFSET_LC]直接将有符号byte值转为short,当LC值大于127时会被识别为负数,导致读取的输入长度错误;另一方面标准短APDU单次最多传输255字节数据,根本装不下256字节的2048位RSA密文,解密时输入长度不足,填充校验必然失败。
  • 冗余错误的密钥初始化逻辑:你提前创建3个64字节的瞬态数组填充1,手动set到密钥对象后再调用genKeyPair()是完全无效的操作。genKeyPair()执行时会自动生成符合安全规范的模数、公钥指数、私钥指数,直接覆盖你之前手动设置的所有参数;且64字节的数组长度和2048位RSA要求的256字节参数长度不匹配,部分模拟器做了容错没抛错,但部署到实体卡会直接触发参数异常。
  • 动态申请大数组触发隐性问题:你在加解密方法里每次调用都执行new byte[2048]创建临时数组,Java Card中new操作创建的是EEPROM持久化对象,频繁调用会快速磨损EEPROM,且卡上存储空间极其有限,大数组动态申请很容易触发空间不足、内存踩踏等隐性问题,导致数据异常。
可直接运行的修正方案

如果仅需要实现RSA加解密还原功能,优先使用512位RSA做测试,对应模长64字节,单条短APDU即可完成传输,不需要处理分段逻辑,兼容性最好:

构造函数修正(去掉冗余逻辑,一次性初始化全局复用对象)

// 全局常量定义
private static final short RSA_BITS = KeyBuilder.LENGTH_RSA_512;
private static final short RSA_BLOCK_LEN = 64; // 512位RSA对应块长64字节
// 全局复用对象,不要在业务方法里重复创建
private KeyPair rsaKeyPair;
private Cipher rsaCipher;
private byte[] tempBuf;

protected MyApplet() {
    // 生成RSA密钥对,不需要提前手动设置模数/指数
    rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, RSA_BITS);
    rsaKeyPair.genKeyPair();
    // 使用PKCS1填充,是所有Java Card合规设备强制支持的算法,兼容性远好于OAEP
    rsaCipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
    // 一次性创建瞬态临时缓冲区,全局复用,避免频繁new持久化数组
    tempBuf = JCSystem.makeTransientByteArray(RSA_BLOCK_LEN, JCSystem.CLEAR_ON_DESELECT);
    register();
}

加密方法修正

private void encrypt(APDU apdu) {
    byte[] apduBuf = apdu.getBuffer();
    // 正确读取LC值:将有符号byte转为无符号short,避免长度识别错误
    short lc = (short)(apduBuf[ISO7816.OFFSET_LC] & 0xFF);
    
    rsaCipher.init(rsaKeyPair.getPublic(), Cipher.MODE_ENCRYPT);
    short cipherLen = rsaCipher.doFinal(
        apduBuf, ISO7816.OFFSET_CDATA, lc,
        tempBuf, (short)0
    );
    
    apdu.setOutgoing();
    apdu.setOutgoingLength(cipherLen);
    apdu.sendBytesLong(tempBuf, (short)0, cipherLen);
}

解密方法修正

private void decrypt(APDU apdu) {
    byte[] apduBuf = apdu.getBuffer();
    // 同样正确读取LC值,确保传入的密文长度和RSA块长一致
    short lc = (short)(apduBuf[ISO7816.OFFSET_LC] & 0xFF);
    
    rsaCipher.init(rsaKeyPair.getPrivate(), Cipher.MODE_DECRYPT);
    short plainLen = rsaCipher.doFinal(
        apduBuf, ISO7816.OFFSET_CDATA, lc,
        tempBuf, (short)0
    );
    
    apdu.setOutgoing();
    apdu.setOutgoingLength(plainLen);
    apdu.sendBytesLong(tempBuf, (short)0, plainLen);
}
部署注意事项
  • 测试时先调用加密接口,确认返回密文长度固定为64字节,再传入解密接口即可正常还原明文。
  • 如果必须使用2048位RSA,需要开启终端和模拟器/卡片的扩展APDU支持,或者实现密文分段收发逻辑,保证解密时传入的密文长度为完整的256字节,否则必然触发填充错误。
  • 部署到NXP J3H145卡片时,上述代码完全兼容Java Card 3.0.4版本规范,不需要额外修改;如果要使用OAEP填充,需要显式传入SHA-1的MessageDigest对象初始化,不要使用单参数的Cipher.getInstance方法,避免不同卡片默认哈希配置不一致导致解密失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 09:51:47