206 lines
7.4 KiB
Java
206 lines
7.4 KiB
Java
package com.cscn;
|
||
|
||
import javacard.framework.APDU;
|
||
import javacard.framework.ISO7816;
|
||
import javacard.framework.ISOException;
|
||
import javacard.framework.JCSystem;
|
||
import javacard.framework.Util;
|
||
|
||
|
||
/**
|
||
* 仅做一次“自检调用”:
|
||
* 1) ctx.init(KEY, IV); Enc(Input) == EncExpected ?
|
||
* 2) ctx.init(KEY, IV); Enc(Enc(Input)) == Input ?
|
||
* Response: 2字节 [encMatch, dblEncRestored],1=真,0=假
|
||
*/
|
||
public final class Method {
|
||
// ======= 已按你提供的数据填充 =======
|
||
|
||
// Key: 32字节
|
||
private static final byte[] KEY32 = {
|
||
(byte)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,(byte)0x36,(byte)0x37,
|
||
(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x62,(byte)0x63,(byte)0x64,(byte)0x65,(byte)0x66,
|
||
(byte)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,(byte)0x36,(byte)0x37,
|
||
(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x62,(byte)0x63,(byte)0x64,(byte)0x65,(byte)0x66
|
||
};
|
||
|
||
// IV
|
||
private static final byte[] IV25 = {//todo 23 -> 25
|
||
(byte)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,(byte)0x36,(byte)0x37,
|
||
(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x62,(byte)0x63,(byte)0x64,(byte)0x65,(byte)0x66,
|
||
(byte)0x67,(byte)0xC3,(byte)0x1C,(byte)0xB3,(byte)0xD3,(byte)0x5D,(byte)0xB7
|
||
};
|
||
|
||
// Input: 明文(38字节)
|
||
private static final byte[] INPUT = {
|
||
(byte)0x5A,(byte)0x55,(byte)0x43,(byte)0x32,(byte)0x35,(byte)0x36,(byte)0xE5,(byte)0xAF,
|
||
(byte)0xB9,(byte)0xE7,(byte)0xA7,(byte)0xB0,(byte)0xE5,(byte)0x8A,(byte)0xA0,(byte)0xE8,
|
||
(byte)0xA7,(byte)0xA3,(byte)0xE5,(byte)0xAF,(byte)0x86,(byte)0xE6,(byte)0xB5,(byte)0x8B,
|
||
(byte)0xE8,(byte)0xAF,(byte)0x95,(byte)0x3A,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,
|
||
(byte)0x35,(byte)0x36,(byte)0x37,(byte)0x38,(byte)0x39,(byte)0x30
|
||
};
|
||
|
||
// EncResult: 期望密文(38字节)
|
||
private static final byte[] ENC_EXPECTED = {
|
||
(byte)0x6C,(byte)0xEE,(byte)0x3C,(byte)0xFA,(byte)0xDE,(byte)0xBB,(byte)0xCB,(byte)0xE5,
|
||
(byte)0x33,(byte)0x51,(byte)0x07,(byte)0x07,(byte)0x90,(byte)0x25,(byte)0x93,(byte)0x27,
|
||
(byte)0x94,(byte)0xF5,(byte)0x18,(byte)0x70,(byte)0xEF,(byte)0x71,(byte)0x72,(byte)0x7D,
|
||
(byte)0xBA,(byte)0x8D,(byte)0xBF,(byte)0x4F,(byte)0x61,(byte)0xC9,(byte)0xA8,(byte)0xE9,
|
||
(byte)0xFF,(byte)0x19,(byte)0xF9,(byte)0xF9,(byte)0xE2,(byte)0xD2
|
||
};
|
||
|
||
// ======================================
|
||
|
||
// 运行时缓冲:放RAM,避免写EEPROM
|
||
private final Zuc256EncryptCtx ctx;
|
||
|
||
public Method() {
|
||
ctx = new Zuc256EncryptCtx(); // 仅创建一次
|
||
}
|
||
|
||
|
||
public void processData(APDU apdu) {
|
||
short L = (short) INPUT.length;
|
||
byte[] buf1; // Enc(Input)
|
||
byte[] buf2; // Enc(Enc(Input)) -> 应为 Input
|
||
buf1 = JCSystem.makeTransientByteArray(L, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
|
||
buf2 = JCSystem.makeTransientByteArray(L, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
|
||
|
||
byte[] apduBuf = apdu.getBuffer();
|
||
|
||
// 第一次:Enc(Input)
|
||
ctx.init(KEY32, IV25);
|
||
Zuc256State tmpState = ctx.state;
|
||
ctx.update(INPUT, (short) INPUT.length, buf1);
|
||
ctx.finish(buf1); // 若 Input 长度为 4 的倍数则通常无副作用,留着更稳妥
|
||
|
||
Zuc256State tmpState2 = ctx.state;
|
||
boolean encMatch = (Util.arrayCompare(buf1, (short)0, ENC_EXPECTED, (short)0, (short)INPUT.length) == 0);
|
||
|
||
// 第二次:Enc(Enc(Input)) 应还原 Input
|
||
ctx.init(KEY32, IV25);
|
||
ctx.update(buf1, (short) INPUT.length, buf2);
|
||
ctx.finish(buf2);
|
||
boolean dblOk = (Util.arrayCompare(buf2, (short)0, INPUT, (short)0, (short)INPUT.length) == 0);
|
||
|
||
// 返回 2 字节结果:[encMatch, dblOk],1=真, 0=假
|
||
apduBuf[0] = (byte)(encMatch ? 1 : 0);
|
||
apduBuf[1] = (byte)(dblOk ? 1 : 0);
|
||
short outLen = 2;
|
||
|
||
apdu.setOutgoing();
|
||
apdu.setOutgoingLength(outLen);
|
||
apdu.sendBytes((short)0, outLen);
|
||
}
|
||
|
||
/**
|
||
* fake数据生成函数
|
||
* 输入: stmsi[6], data[5]
|
||
* 输出: fakedata[5]
|
||
*/
|
||
private void makeFakeData(byte[] stmsi, byte[] data, byte[] fakedata) {
|
||
// 示例:逐字节异或 stmsi 前5字节
|
||
for (short i = 0; i < (short)5; i++) {
|
||
fakedata[i] = (byte)(data[i]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* APDU处理函数
|
||
*/
|
||
public short processDataFake(byte[] buffer, short off, short len, byte[] key_store) {
|
||
// 至少要有 12 个字节:1+1+1+6+5
|
||
if (len < (short)12) {
|
||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
}
|
||
|
||
// === 解析入参 ===
|
||
byte inType = buffer[(short)(off + 0)]; // 类型 A1
|
||
byte inLen = buffer[(short)(off + 1)]; // 长度 0x0C
|
||
byte secParam = buffer[(short)(off + 2)]; // 安全参数
|
||
|
||
// STMSI
|
||
byte[] stmsi = JCSystem.makeTransientByteArray((short)6, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
|
||
Util.arrayCopyNonAtomic(buffer, (short)(off + 3), stmsi, (short)0, (short)6);
|
||
|
||
// data
|
||
byte[] data = JCSystem.makeTransientByteArray((short)5, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
|
||
Util.arrayCopyNonAtomic(buffer, (short)(off + 9), data, (short)0, (short)5);
|
||
|
||
// === 生成 fakedata ===
|
||
byte[] fakedata = JCSystem.makeTransientByteArray((short)5, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
|
||
makeFakeData(stmsi, data, fakedata);
|
||
|
||
// === 出参组装 ===
|
||
buffer[(short)(off + 0)] = (byte)0xA1; // 类型
|
||
buffer[(short)(off + 1)] = (byte)0x06; // 长度
|
||
buffer[(short)(off + 2)] = (byte)0x01; // 密钥选取
|
||
Util.arrayCopyNonAtomic(fakedata, (short)0, buffer, (short)(off + 3), (short)5);
|
||
|
||
return (short)8; // 出参总长度
|
||
}
|
||
|
||
|
||
|
||
public short updateKey(byte[] buffer, short off, short len, byte[] key_store) {
|
||
final short SLOT_SIZE = 40; // 每个槽固定40字节
|
||
final short HDR_LEN = 4; // 报文头长度: [0]=keyLenField, [1]=alg, [2]=keyId, [3]=ver
|
||
|
||
// === 解析头 ===
|
||
byte keyLenField = buffer[(short)(off + 0)]; // 包含头部+密钥的总长度
|
||
byte alg = buffer[(short)(off + 1)];
|
||
byte keyId = buffer[(short)(off + 2)];
|
||
byte ver = buffer[(short)(off + 3)];
|
||
|
||
// 计算实际密钥长度
|
||
short realKeyLen = (short)(keyLenField - (byte)3); // 长度字段 = key长度 + 3字节头
|
||
if (realKeyLen <= 0) {
|
||
ISOException.throwIt(ISO7816.SW_DATA_INVALID); // key长度不合法
|
||
}
|
||
|
||
// 检查APDU长度和key长度是否匹配
|
||
if (len != (short)(HDR_LEN + realKeyLen)) {
|
||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
}
|
||
if (len > SLOT_SIZE) {
|
||
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
}
|
||
|
||
// === 将原有 key_store 拷贝到 RAM,避免直接覆盖 EEPROM (可选) ===
|
||
byte[] key_buf = JCSystem.makeTransientByteArray((short)80, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
|
||
Util.arrayCopyNonAtomic(key_store, (short)0, key_buf, (short)0, (short)key_store.length);
|
||
|
||
// === 遍历槽,找到匹配的 (alg, keyId, ver),否则找空槽 ===
|
||
short slots = (short)(key_store.length / SLOT_SIZE);
|
||
short target = -1;
|
||
for (short i = 0; i < slots; i++) {
|
||
short base = (short)(i * SLOT_SIZE);
|
||
byte algOld = key_store[(short)(base + 1)];
|
||
byte keyIdOld = key_store[(short)(base + 2)];
|
||
byte verOld = key_store[(short)(base + 3)];
|
||
|
||
if (algOld == alg && keyId == keyIdOld && ver == verOld) {
|
||
target = i; // 找到已存在的,覆盖它
|
||
break;
|
||
}
|
||
if (key_store[base] == (byte)0x00 && target == -1) {
|
||
target = i; // 找到第一个空槽
|
||
}
|
||
}
|
||
|
||
if (target == -1) {
|
||
ISOException.throwIt(ISO7816.SW_FILE_FULL); // 没有空位
|
||
}
|
||
|
||
// === 覆盖写入本次APDU的头+密钥数据 ===
|
||
short base = (short)(target * SLOT_SIZE);
|
||
// 先清空槽
|
||
Util.arrayFillNonAtomic(key_store, base, SLOT_SIZE, (byte)0x00);
|
||
// 再写入APDU传入的头+数据 (len 个字节)
|
||
Util.arrayCopyNonAtomic(buffer, off, key_store, base, len);
|
||
|
||
return 0; // 无返回数据
|
||
}
|
||
|
||
}
|