This commit is contained in:
waner
2026-04-02 13:59:29 +08:00
parent f8673ec1fb
commit 738c2d4ae6
9 changed files with 414 additions and 42 deletions
@@ -5,6 +5,8 @@ public class RecoverUserKeyRequest {
private int keyIndex;
private int keyType;
private byte[] encryptedKey;
//1:掉电保存;0:掉电不保存(防止flash写坏)
private int storeFlag = 1;
public int getKeyIndex() {
@@ -205,7 +205,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
public byte[] initializeMasterKey(byte[] oldPin, byte[] newPin) {
byte[] oldPinValue = requireNonEmptyBytes("oldPin", oldPin);
byte[] newPinValue = requireNonEmptyBytes("newPin", newPin);
//lmk初始化包含主密钥生成、加载;内部密钥生成;用户密钥生成;返回主密钥校验值。
// 一次会话内完成初始化识别、LMK 生成加载、IK 生成和默认用户密钥生成,避免中途切换状态。
try {
return sessionTemplate.withSession("SDFE_InitializeMasterKey", (lib, deviceHandle, sessionHandle) -> {
sessionTemplate.ensureSuccess(
@@ -1837,6 +1837,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
return sessionTemplate.withSession("SDFE_BackupLMK_EX", (lib, deviceHandle, sessionHandle) -> {
SdfeLmkComponent output = new SdfeLmkComponent();
sessionTemplate.ensureSuccess("SDFE_BackupLMK_EX", lib.SDFE_BackupLMK_EX(sessionHandle, output));
// 这里返回的是完整的 LMK 扩展结构:8 字节校验值 + 3 个 16 字节分量。
byte[] outputBytes = toBytes(output);
return toBackupResult("SDFE_BackupLMK_EX", outputBytes, outputBytes.length);
});
@@ -1847,6 +1848,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
SdfeLmkComponent input = toSdfeLmkComponent("lmkComponent", lmkComponent);
sessionTemplate.withSession("SDFE_RecoverLMK_EX", (lib, deviceHandle, sessionHandle) -> {
// 输入必须是同一轮导出的 lmkMac + component1 + component2 + component3,否则底层会报 MACERR。
sessionTemplate.ensureSuccess("SDFE_RecoverLMK_EX", lib.SDFE_RecoverLMK_EX(sessionHandle, input));
return null;
});
@@ -423,7 +423,7 @@ public interface PcieCryptoService {
BackupDataResult digest(DigestRequest request);
//----------------------------------------SD FE 专用算法扩展接口------------------------------------------
//----------------------------------------SDFE 专用算法扩展接口------------------------------------------
/**
* 读取固件版本信息。
@@ -140,6 +140,7 @@ public class AuthAdminServiceImpl implements AuthAdminService {
String signValue = lmkService.signIk(toIssuePayload(dto, authKeyPair));
MasterKeyBackupPacket backupPacket = null;
if (RoleCode.SUPER_ADMIN == targetRole) {
// 超级管理员需要 3 把 UKey;这里按 uid=1/2/3 取出同一轮备份中的对应分量包。
backupPacket = lmkService.buildBackupPacket(Integer.parseInt(request.getUid()));
}
return UKeySignResult.builder()
@@ -22,6 +22,7 @@ import com.cisd.tms.integration.crypto.pcie.model.FileReadRequest;
import com.cisd.tms.integration.crypto.pcie.model.FileWriteRequest;
import com.cisd.tms.integration.crypto.pcie.model.GenerateAgreementDataAndKeyEccRequest;
import com.cisd.tms.integration.crypto.pcie.model.GenerateKekRequest;
import com.cisd.tms.integration.crypto.pcie.model.GenerateKeyPairRequest;
import com.cisd.tms.integration.crypto.pcie.model.GenerateStandardEccKeyPairRequest;
import com.cisd.tms.integration.crypto.pcie.model.GenerateStandardRsaKeyPairRequest;
import com.cisd.tms.integration.crypto.pcie.model.HmacRequest;
@@ -29,6 +30,7 @@ import com.cisd.tms.integration.crypto.pcie.model.HmacResult;
import com.cisd.tms.integration.crypto.pcie.model.KeyStatusResult;
import com.cisd.tms.integration.crypto.pcie.model.LoginStatusResult;
import com.cisd.tms.integration.crypto.pcie.model.MacCalcRequest;
import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest;
import com.cisd.tms.integration.crypto.pcie.model.SymmetricCryptoRequest;
import com.cisd.tms.integration.crypto.pcie.model.SymmetricKekCryptoRequest;
import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult;
@@ -39,10 +41,13 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import java.util.Arrays;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -52,6 +57,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Validated
@Slf4j
@RestController
@RequestMapping("/api/v1/device/crypto")
@RequiredArgsConstructor
@@ -517,6 +523,48 @@ public class CryptoCardController {
return ApiResponse.success(hexPayload("dataHex", result.getData(), result.getLength()));
}
@PostMapping("/user-key/self-test")
@Operation(summary = "执行用户密钥全流程自测", description = "依次执行 ECC 用户密钥生成、备份、删除、重建槽位、恢复和状态校验,返回备份 HEX 与关键状态。")
public ApiResponse<Map<String, Object>> selfTestUserKey(
@Valid @RequestBody CryptoUserKeySelfTestInternalRequest request
) {
GenerateKeyPairRequest generateRequest = new GenerateKeyPairRequest();
generateRequest.setKeyBits(256);
generateRequest.setKeyIndex(request.getKeyIndex());
generateRequest.setKeyType(request.getKeyType());
pcieCryptoService.generateKeyPairEcc(generateRequest);
BackupDataResult backupResult = pcieCryptoService.backupUserKey(
request.getKeyIndex(),
request.getKeyType(),
request.getOutBufferSize()
);
log.info("backupResult:{}", backupResult);
pcieCryptoService.deleteUserKey(request.getKeyIndex());
KeyStatusResult deleteStatus = pcieCryptoService.statusUserKeyEcc(request.getKeyIndex(), request.getKeyType());
// pcieCryptoService.generateKeyPairEcc(generateRequest);
RecoverUserKeyRequest recoverRequest = new RecoverUserKeyRequest();
int backupLength = backupResult.getLength() > 0 && backupResult.getLength() <= backupResult.getData().length
? backupResult.getLength()
: backupResult.getData().length;
recoverRequest.setKeyIndex(request.getKeyIndex());
recoverRequest.setKeyType(request.getKeyType());
recoverRequest.setEncryptedKey(Arrays.copyOf(backupResult.getData(), backupLength));
recoverRequest.setStoreFlag(request.getStoreFlag());
pcieCryptoService.recoverUserKey(recoverRequest);
KeyStatusResult recoverStatus = pcieCryptoService.statusUserKeyEcc(request.getKeyIndex(), request.getKeyType());
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("keyIndex", request.getKeyIndex());
payload.put("keyType", request.getKeyType());
payload.put("backupLength", backupLength);
payload.put("backupHex", java.util.HexFormat.of().formatHex(Arrays.copyOf(backupResult.getData(), backupLength)));
payload.put("deleteStatus", deleteStatus);
payload.put("recoverStatus", recoverStatus);
return ApiResponse.success(payload);
}
@PostMapping("/user-key/recover")
@Operation(summary = "恢复用户密钥", description = "从备份数据恢复用户密钥。")
public ApiResponse<Void> recoverUserKey(
@@ -0,0 +1,50 @@
package com.cisd.tms.modules.device.dto.crypto.request;
import jakarta.validation.constraints.Min;
public class CryptoUserKeySelfTestInternalRequest {
@Min(0)
private int keyIndex;
@Min(0)
private int keyType;
@Min(1)
private int outBufferSize = 120;
@Min(0)
private int storeFlag = 1;
public int getKeyIndex() {
return keyIndex;
}
public void setKeyIndex(int keyIndex) {
this.keyIndex = keyIndex;
}
public int getKeyType() {
return keyType;
}
public void setKeyType(int keyType) {
this.keyType = keyType;
}
public int getOutBufferSize() {
return outBufferSize;
}
public void setOutBufferSize(int outBufferSize) {
this.outBufferSize = outBufferSize;
}
public int getStoreFlag() {
return storeFlag;
}
public void setStoreFlag(int storeFlag) {
this.storeFlag = storeFlag;
}
}
@@ -2,13 +2,10 @@ package com.cisd.tms.modules.mk.service.impl;
import com.cisd.tms.common.util.DivisionUtils;
import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds;
import com.cisd.tms.integration.crypto.pcie.PcieUserKeyType;
import com.cisd.tms.integration.crypto.pcie.jna.EccRefPublicKey;
import com.cisd.tms.integration.crypto.pcie.jna.EccSignature;
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
import com.cisd.tms.integration.crypto.pcie.model.DigestRequest;
import com.cisd.tms.integration.crypto.pcie.model.IkSignRequest;
import com.cisd.tms.integration.crypto.pcie.model.IkVerifyRequest;
import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest;
import com.cisd.tms.integration.crypto.pcie.model.*;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.mk.common.LMKConstant;
import com.cisd.tms.modules.mk.config.MasterKeyInitProperties;
@@ -25,9 +22,11 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
@@ -56,9 +55,13 @@ public class LmkServiceImpl implements LmkService {
private final PcieCryptoService pcieCryptoService;
private final MasterKeyInitProperties masterKeyInitProperties;
private final Object backupRoundLock = new Object();
private CachedBackupRound cachedBackupRound;
@Override
public MasterKeyStateResult initMasterKey() {
// 密钥体系一旦重建,之前导出的三份备份包就全部失效,必须清空当前轮次缓存。
clearCachedBackupRound();
byte[] oldPin = decodeConfiguredPin("tms.mk.init-identify.old-pin-base64", masterKeyInitProperties.getOldPinBase64());
byte[] newPin = decodeConfiguredPin("tms.mk.init-identify.new-pin-base64", masterKeyInitProperties.getNewPinBase64());
@@ -70,7 +73,13 @@ public class LmkServiceImpl implements LmkService {
@Override
public void destroyMasterKey() {
// 销毁后也不能再继续复用上一轮备份材料。
clearCachedBackupRound();
pcieCryptoService.destroyLmk();
pcieCryptoService.destroyIk(IKEnums.KEY_TYPE_AUTH.getCode());
pcieCryptoService.destroyIk(IKEnums.KEY_TYPE_DEVICE.getCode());
pcieCryptoService.deleteUserKey(USER_KEY_INDEX);
}
@Override
@@ -115,35 +124,27 @@ public class LmkServiceImpl implements LmkService {
@Override
public MasterKeyBackupPacket buildBackupPacket(int componentIndex) {
int index = requireComponentIndex(componentIndex);
ParsedMasterKeyBackup lmkBackup = parseBackupMaterial();
ParsedIkBackup authIkBackup = parseIkBackupMaterial(IKEnums.KEY_TYPE_AUTH.getCode());
ParsedIkBackup deviceIkBackup = parseIkBackupMaterial(IKEnums.KEY_TYPE_DEVICE.getCode());
synchronized (backupRoundLock) {
CachedBackupRound round = cachedBackupRound;
if (round == null) {
// 三次发给外部 UKey 的分量必须来自同一轮导出,否则恢复 LMK 时会出现 MACERR。
round = new CachedBackupRound(
parseBackupMaterial(),
parseIkBackupMaterial(IKEnums.KEY_TYPE_AUTH.getCode()),
parseIkBackupMaterial(IKEnums.KEY_TYPE_DEVICE.getCode()),
parseUserKeyBackup(USER_KEY_INDEX, USER_KEY_TYPE_SIGN),
parseUserKeyBackup(USER_KEY_INDEX, USER_KEY_TYPE_ENC)
);
cachedBackupRound = round;
}
MasterKeyBackupPacket packet = new MasterKeyBackupPacket();
packet.setComponentIndex(index);
packet.setLmk(new MasterKeyPacketLmkPart(
lmkBackup.lmkMacHex(),
requiredComponent(lmkBackup.componentMap(), index, "lmk")
));
packet.setIkAuth(new MasterKeyPacketIkPart(
IKEnums.KEY_TYPE_AUTH.getCode(),
authIkBackup.macHex(),
requiredComponent(authIkBackup.componentMap(), index, "ikAuth"),
authIkBackup.publicKeyHex(),
authIkBackup.publicKeyMacHex()
));
packet.setIkDevice(new MasterKeyPacketIkPart(
IKEnums.KEY_TYPE_DEVICE.getCode(),
deviceIkBackup.macHex(),
requiredComponent(deviceIkBackup.componentMap(), index, "ikDevice"),
deviceIkBackup.publicKeyHex(),
deviceIkBackup.publicKeyMacHex()
));
packet.setUserKeys(List.of(
buildUserKeyPacket(USER_KEY_INDEX, USER_KEY_TYPE_SIGN, index),
buildUserKeyPacket(USER_KEY_INDEX, USER_KEY_TYPE_ENC, index)
));
return packet;
MasterKeyBackupPacket packet = round.buildPacket(index);
round.markServed(index);
if (round.isComplete()) {
cachedBackupRound = null;
}
return packet;
}
}
@Override
@@ -159,21 +160,29 @@ public class LmkServiceImpl implements LmkService {
@Override
public void recoverKeyPackets(List<MasterKeyBackupPacket> packets) {
// 进入恢复流程后,旧的备份轮次缓存不再可信,避免和恢复后的新密钥体系混用。
clearCachedBackupRound();
List<MasterKeyBackupPacket> orderedPackets = sortPackets(packets);
byte[] oldPin = decodeConfiguredPin("tms.mk.init-identify.old-pin-base64", masterKeyInitProperties.getOldPinBase64());
byte[] newPin = decodeConfiguredPin("tms.mk.init-identify.new-pin-base64", masterKeyInitProperties.getNewPinBase64());
// 恢复顺序必须先 LMK、再 LoadLMK、再 IK、最后用户密钥;后面的密钥材料都依赖前面的密钥体系。
pcieCryptoService.initIdentify(oldPin, newPin);
pcieCryptoService.recoverLmkEx(Hex.decode(composeLmkHex(orderedPackets)));
pcieCryptoService.loadLmk();
pcieCryptoService.recoverIkComponent(IKEnums.KEY_TYPE_AUTH.getCode(), Hex.decode(composeIkHex(orderedPackets, true)));
pcieCryptoService.recoverIkComponent(IKEnums.KEY_TYPE_DEVICE.getCode(), Hex.decode(composeIkHex(orderedPackets, false)));
//务必先查询是否是就绪状态
DeviceStatusResult result = pcieCryptoService.getDeviceStatus();;
log.info("device status:{}", result.getFsmState());
pcieCryptoService.recoverUserKey(toRecoverUserKeyRequest(orderedPackets, USER_KEY_INDEX, USER_KEY_TYPE_SIGN));
pcieCryptoService.recoverUserKey(toRecoverUserKeyRequest(orderedPackets, USER_KEY_INDEX, USER_KEY_TYPE_ENC));
}
@Override
public void recoverLmkEx(String lmkMacHex, List<String> components) {
// 兼容旧的“仅恢复 LMK”入口;一旦恢复成功,旧缓存同样需要废弃。
clearCachedBackupRound();
byte[] oldPin = decodeConfiguredPin("tms.mk.init-identify.old-pin-base64", masterKeyInitProperties.getOldPinBase64());
byte[] newPin = decodeConfiguredPin("tms.mk.init-identify.new-pin-base64", masterKeyInitProperties.getNewPinBase64());
String fullLmkHex = masterKeyCompose(lmkMacHex, components);
@@ -215,6 +224,7 @@ public class LmkServiceImpl implements LmkService {
String lmkMac = requireConsistentLmkMac(packets);
StringBuilder builder = new StringBuilder(lmkMac);
for (MasterKeyBackupPacket packet : packets) {
// 恢复扩展结构时必须按 1/2/3 顺序拼回 3 个分量。
builder.append(packet.getLmk().getComponent());
}
return builder.toString();
@@ -245,10 +255,19 @@ public class LmkServiceImpl implements LmkService {
return request;
}
private GenerateKeyPairRequest newUserKeyRequest(int keyType) {
GenerateKeyPairRequest request = new GenerateKeyPairRequest();
request.setKeyBits(256);
request.setKeyIndex(USER_KEY_INDEX);
request.setKeyType(keyType);
return request;
}
private String composeUserKeyHex(List<MasterKeyBackupPacket> packets, int keyIndex, int keyType) {
StringBuilder builder = new StringBuilder();
for (MasterKeyBackupPacket packet : packets) {
MasterKeyPacketUserKeyPart part = findUserKeyPart(packet, keyIndex, keyType);
// 用户密钥没有原生三分量结构,这里是把单把备份 blob 按 1/2/3 切开后再按顺序拼回。
builder.append(part.getComponent());
}
return builder.toString();
@@ -296,11 +315,15 @@ public class LmkServiceImpl implements LmkService {
return new ParsedIkBackup(Hex.toHexString(mac), componentMap, Hex.toHexString(publicKey), Hex.toHexString(publicKeyMac));
}
private MasterKeyPacketUserKeyPart buildUserKeyPacket(int keyIndex, int keyType, int componentIndex) {
private ParsedUserKeyBackup parseUserKeyBackup(int keyIndex, int keyType) {
BackupDataResult backupData = pcieCryptoService.backupUserKey(keyIndex, keyType, USER_KEY_BACKUP_BUFFER_SIZE);
byte[] normalized = normalizeBackupData(backupData);
String[] components = DivisionUtils.equalDivision(Hex.toHexString(normalized), LMKConstant.COMPONENT_NUM);
return new MasterKeyPacketUserKeyPart(keyIndex, keyType, components[componentIndex - 1]);
Map<Integer, String> componentMap = new HashMap<>();
for (int i = 0; i < LMKConstant.COMPONENT_NUM; i++) {
componentMap.put(LMKConstant.KEY_ORDER[i], components[i]);
}
return new ParsedUserKeyBackup(keyIndex, keyType, componentMap);
}
private byte[] normalizeBackupData(BackupDataResult backupData) {
@@ -443,9 +466,86 @@ public class LmkServiceImpl implements LmkService {
return sorted;
}
private void clearCachedBackupRound() {
synchronized (backupRoundLock) {
cachedBackupRound = null;
}
}
private record ParsedMasterKeyBackup(String lmkMacHex, Map<Integer, String> componentMap) {
}
private record ParsedIkBackup(String macHex, Map<Integer, String> componentMap, String publicKeyHex, String publicKeyMacHex) {
}
private record ParsedUserKeyBackup(int keyIndex, int keyType, Map<Integer, String> componentMap) {
}
private final class CachedBackupRound {
private final ParsedMasterKeyBackup lmkBackup;
private final ParsedIkBackup authIkBackup;
private final ParsedIkBackup deviceIkBackup;
private final ParsedUserKeyBackup signUserKeyBackup;
private final ParsedUserKeyBackup encUserKeyBackup;
private final Set<Integer> servedIndices = new HashSet<>();
private CachedBackupRound(
ParsedMasterKeyBackup lmkBackup,
ParsedIkBackup authIkBackup,
ParsedIkBackup deviceIkBackup,
ParsedUserKeyBackup signUserKeyBackup,
ParsedUserKeyBackup encUserKeyBackup
) {
this.lmkBackup = lmkBackup;
this.authIkBackup = authIkBackup;
this.deviceIkBackup = deviceIkBackup;
this.signUserKeyBackup = signUserKeyBackup;
this.encUserKeyBackup = encUserKeyBackup;
}
private MasterKeyBackupPacket buildPacket(int componentIndex) {
MasterKeyBackupPacket packet = new MasterKeyBackupPacket();
packet.setComponentIndex(componentIndex);
packet.setLmk(new MasterKeyPacketLmkPart(
lmkBackup.lmkMacHex(),
requiredComponent(lmkBackup.componentMap(), componentIndex, "lmk")
));
packet.setIkAuth(new MasterKeyPacketIkPart(
IKEnums.KEY_TYPE_AUTH.getCode(),
authIkBackup.macHex(),
requiredComponent(authIkBackup.componentMap(), componentIndex, "ikAuth"),
authIkBackup.publicKeyHex(),
authIkBackup.publicKeyMacHex()
));
packet.setIkDevice(new MasterKeyPacketIkPart(
IKEnums.KEY_TYPE_DEVICE.getCode(),
deviceIkBackup.macHex(),
requiredComponent(deviceIkBackup.componentMap(), componentIndex, "ikDevice"),
deviceIkBackup.publicKeyHex(),
deviceIkBackup.publicKeyMacHex()
));
packet.setUserKeys(List.of(
new MasterKeyPacketUserKeyPart(
signUserKeyBackup.keyIndex(),
signUserKeyBackup.keyType(),
requiredComponent(signUserKeyBackup.componentMap(), componentIndex, "userSign")
),
new MasterKeyPacketUserKeyPart(
encUserKeyBackup.keyIndex(),
encUserKeyBackup.keyType(),
requiredComponent(encUserKeyBackup.componentMap(), componentIndex, "userEnc")
)
));
return packet;
}
private void markServed(int componentIndex) {
servedIndices.add(componentIndex);
}
private boolean isComplete() {
return servedIndices.size() == LMKConstant.COMPONENT_NUM;
}
}
}