基于DES/AES对称密钥的Java Card小程序签名验签实现问询
Java Card 基于对称密钥的签名验签实现(AES/DES,无RSA)
核心原理说明
对称密钥体系下的“签名验签”本质是使用**消息认证码(MAC)**机制:通信双方共享对称密钥,发送方用密钥对消息生成MAC值(相当于“签名”),接收方用同一密钥对消息重新计算MAC并与收到的MAC比对,一致则验证通过。和非对称签名不同,这种方式不具备抗抵赖性,但完全满足消息完整性、真实性的验证需求,符合你的要求。
常用的对称MAC算法有:
- AES-MAC(基于AES密钥,推荐,安全性更高)
- DES-MAC/3DES-MAC(基于DES/3DES密钥)
代码实现(以AES-MAC为例)
以下是完整的Java Card小程序代码,包含密钥初始化、MAC生成、MAC验证功能:
package com.example.symmetricmac; import javacard.framework.*; import javacard.security.*; import javacardx.crypto.*; public class SymmetricMACApplet extends Applet { // APDU指令常量 private static final byte INS_GENERATE_MAC = (byte) 0x01; private static final byte INS_VERIFY_MAC = (byte) 0x02; private static final byte INS_SET_KEY = (byte) 0x03; // 算法与密钥参数 private static final byte AES_KEY_LENGTH = (byte) 16; // 128位AES密钥 private final AESKey aesKey; private final MAC mac; private SymmetricMACApplet() { // 初始化AES密钥对象 aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); // 初始化AES-MAC对象(使用ISO9797_ALG3_MAC算法,对应AES-CBC-MAC) mac = MAC.getInstance(MAC.ALG_AES_MAC_128_ISO9797_3, false); } public static void install(byte[] bArray, short bOffset, byte bLength) { new SymmetricMACApplet().register(); } public void process(APDU apdu) { if (selectingApplet()) { return; } byte[] buffer = apdu.getBuffer(); byte ins = buffer[ISO7816.OFFSET_INS]; switch (ins) { case INS_SET_KEY: setAESKey(apdu); break; case INS_GENERATE_MAC: generateMAC(apdu); break; case INS_VERIFY_MAC: verifyMAC(apdu); break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } /** * 设置AES密钥(从APDU数据段导入) */ private void setAESKey(APDU apdu) { byte[] buffer = apdu.getBuffer(); short len = apdu.setIncomingAndReceive(); // 验证密钥长度是否为128位(16字节) if (len != AES_KEY_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // 导入密钥材料 aesKey.setKey(buffer, ISO7816.OFFSET_CDATA); } /** * 生成消息MAC(签名) */ private void generateMAC(APDU apdu) { byte[] buffer = apdu.getBuffer(); short len = apdu.setIncomingAndReceive(); if (len <= 0) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // 初始化MAC,绑定密钥 mac.init(aesKey, MAC.MODE_ISO9797_M3); // 输入消息数据 mac.update(buffer, ISO7816.OFFSET_CDATA, len); // 生成MAC值(长度为16字节,AES-MAC默认长度) short macLen = mac.doFinal(buffer, ISO7816.OFFSET_CDATA, (short)0, buffer, (short)0); // 将MAC值返回给终端 apdu.setOutgoingAndSend((short)0, macLen); } /** * 验证MAC(验签) */ private void verifyMAC(APDU apdu) { byte[] buffer = apdu.getBuffer(); short len = apdu.setIncomingAndReceive(); // 消息长度 + MAC长度(16字节)必须大于16 if (len <= AES_KEY_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } short msgLen = (short)(len - AES_KEY_LENGTH); // 初始化MAC mac.init(aesKey, MAC.MODE_ISO9797_M3); // 输入消息部分 mac.update(buffer, ISO7816.OFFSET_CDATA, msgLen); // 验证MAC:将收到的MAC与计算出的MAC比对 boolean isValid = mac.verify(buffer, (short)(ISO7816.OFFSET_CDATA + msgLen), AES_KEY_LENGTH); if (isValid) { ISOException.throwIt(ISO7816.SW_NO_ERROR); } else { ISOException.throwIt((short)0x6982); // 验证失败状态码 } } }
代码核心部分讲解
密钥与MAC初始化
- 在Applet构造函数中,用
KeyBuilder创建AES密钥对象,指定密钥类型为AES、长度128位; - 用
MAC.getInstance创建AES-MAC实例,选用ALG_AES_MAC_128_ISO9797_3算法,这是符合标准的AES-CBC-MAC实现。
- 在Applet构造函数中,用
APDU指令处理
INS_SET_KEY:从终端导入16字节的AES密钥,注意Java Card中密钥一旦设置完成就不可导出(除非密钥对象创建时允许);INS_GENERATE_MAC:接收终端发送的消息,用密钥生成MAC并返回;INS_VERIFY_MAC:接收终端发送的「消息+MAC」数据,重新计算消息的MAC并与收到的MAC比对,返回验证结果。
DES/3DES适配说明
如果要改用DES或3DES,只需修改几个参数:- 密钥类型改为
KeyBuilder.TYPE_DES或KeyBuilder.TYPE_DES3,长度对应KeyBuilder.LENGTH_DES_64(DES)或KeyBuilder.LENGTH_DES3_192(3DES); - MAC算法改为
MAC.ALG_DES_MAC_ISO9797_1(DES-MAC)或MAC.ALG_DES3_MAC_ISO9797_1(3DES-MAC)。
- 密钥类型改为
基础使用流程
- 安装小程序:用JCDK工具将编译后的CAP文件安装到Java Card模拟器或实体卡;
- 设置密钥:发送
INS_SET_KEY指令,传入16字节的AES密钥; - 生成签名(MAC):发送
INS_GENERATE_MAC指令,传入要签名的消息,接收返回的MAC值; - 验证签名:发送
INS_VERIFY_MAC指令,传入「消息+MAC」的拼接数据,根据返回的状态码判断验证是否通过。
注意事项
- Java Card内存有限,避免使用过大的数组,处理长消息时可分多次调用
mac.update; - 密钥的安全性至关重要,建议通过安全通道导入密钥,避免明文传输;
- 不同的MAC算法可能有不同的输出长度,需根据算法调整代码中的长度参数。
内容的提问来源于stack exchange,提问作者bujji
相关产品推荐
相关产品推荐

