MK管理
This commit is contained in:
parent
f8673ec1fb
commit
738c2d4ae6
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,14 @@ package com.cisd.tms.modules.device.controller;
|
||||
import com.cisd.tms.common.exception.GlobalExceptionHandler;
|
||||
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
|
||||
import com.cisd.tms.integration.crypto.pcie.model.DeviceStatusResult;
|
||||
import com.cisd.tms.integration.crypto.pcie.model.GenerateKeyPairRequest;
|
||||
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.RecoverUserKeyRequest;
|
||||
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
@ -207,4 +211,65 @@ class CryptoCardControllerTest {
|
||||
.andExpect(content().string(containsString("\"admin2\":0")))
|
||||
.andExpect(content().string(containsString("\"admin3\":1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRunFullUserKeySelfTestFlow() throws Exception {
|
||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||
BackupDataResult backup = new BackupDataResult();
|
||||
backup.setLength(4);
|
||||
backup.setData(new byte[] {0x01, 0x23, 0x45, 0x67});
|
||||
KeyStatusResult deleted = new KeyStatusResult();
|
||||
deleted.setKeyState(0);
|
||||
deleted.setKeyStateName("FREE");
|
||||
KeyStatusResult recovered = new KeyStatusResult();
|
||||
recovered.setKeyState(2);
|
||||
recovered.setKeyStateName("RIGHT");
|
||||
Mockito.when(pcieCryptoService.backupUserKey(1, 0, 120)).thenReturn(backup);
|
||||
Mockito.when(pcieCryptoService.statusUserKeyEcc(1, 0)).thenReturn(deleted, recovered);
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService))
|
||||
.setControllerAdvice(new GlobalExceptionHandler())
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/device/crypto/user-key/self-test")
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
{
|
||||
"keyIndex": 1,
|
||||
"keyType": 0,
|
||||
"outBufferSize": 120,
|
||||
"storeFlag": 1
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("\"keyIndex\":1")))
|
||||
.andExpect(content().string(containsString("\"keyType\":0")))
|
||||
.andExpect(content().string(containsString("\"backupLength\":4")))
|
||||
.andExpect(content().string(containsString("\"backupHex\":\"01234567\"")))
|
||||
.andExpect(content().string(containsString("\"deleteStatus\":{\"keyState\":0,\"keyStateName\":\"FREE\"}")))
|
||||
.andExpect(content().string(containsString("\"recoverStatus\":{\"keyState\":2,\"keyStateName\":\"RIGHT\"}")));
|
||||
|
||||
ArgumentCaptor<GenerateKeyPairRequest> generateCaptor = ArgumentCaptor.forClass(GenerateKeyPairRequest.class);
|
||||
ArgumentCaptor<RecoverUserKeyRequest> recoverCaptor = ArgumentCaptor.forClass(RecoverUserKeyRequest.class);
|
||||
InOrder inOrder = Mockito.inOrder(pcieCryptoService);
|
||||
inOrder.verify(pcieCryptoService).generateKeyPairEcc(generateCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).backupUserKey(1, 0, 120);
|
||||
inOrder.verify(pcieCryptoService).deleteUserKey(1);
|
||||
inOrder.verify(pcieCryptoService).statusUserKeyEcc(1, 0);
|
||||
inOrder.verify(pcieCryptoService).generateKeyPairEcc(generateCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).recoverUserKey(recoverCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).statusUserKeyEcc(1, 0);
|
||||
|
||||
assertEquals(2, generateCaptor.getAllValues().size());
|
||||
assertEquals(256, generateCaptor.getAllValues().get(0).getKeyBits());
|
||||
assertEquals(1, generateCaptor.getAllValues().get(0).getKeyIndex());
|
||||
assertEquals(0, generateCaptor.getAllValues().get(0).getKeyType());
|
||||
assertEquals(256, generateCaptor.getAllValues().get(1).getKeyBits());
|
||||
assertEquals(1, generateCaptor.getAllValues().get(1).getKeyIndex());
|
||||
assertEquals(0, generateCaptor.getAllValues().get(1).getKeyType());
|
||||
assertEquals(1, recoverCaptor.getValue().getKeyIndex());
|
||||
assertEquals(0, recoverCaptor.getValue().getKeyType());
|
||||
assertEquals(1, recoverCaptor.getValue().getStoreFlag());
|
||||
assertArrayEquals(new byte[] {0x01, 0x23, 0x45, 0x67}, recoverCaptor.getValue().getEncryptedKey());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
package com.cisd.tms.modules.mk.service;
|
||||
|
||||
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.SdfeIkComponent;
|
||||
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.EccInternalSignRequest;
|
||||
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
|
||||
import com.cisd.tms.integration.crypto.pcie.model.GenerateKeyPairRequest;
|
||||
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;
|
||||
@ -61,6 +63,21 @@ class LmkServiceTest {
|
||||
Assertions.assertEquals("master key initialization failed", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDestroyGeneratedUserKeysAndIksBeforeDestroyingLmk() {
|
||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||
MasterKeyInitProperties properties = configuredPins("MTIzNDU2Nzg=", "ODc2NTQzMjE=");
|
||||
LmkService service = new LmkServiceImpl(pcieCryptoService, properties);
|
||||
|
||||
service.destroyMasterKey();
|
||||
|
||||
InOrder inOrder = Mockito.inOrder(pcieCryptoService);
|
||||
inOrder.verify(pcieCryptoService).deleteUserKey(1);
|
||||
inOrder.verify(pcieCryptoService).destroyIk(2);
|
||||
inOrder.verify(pcieCryptoService).destroyIk(1);
|
||||
inOrder.verify(pcieCryptoService).destroyLmk();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnStatusFalseWhenMasterKeyDoesNotExist() {
|
||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||
@ -177,6 +194,82 @@ class LmkServiceTest {
|
||||
Assertions.assertEquals("75767778", packet.getUserKeys().get(1).getComponent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReuseSingleBackupRoundAcrossThreePacketBuilds() {
|
||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||
MasterKeyInitProperties properties = configuredPins("MTIzNDU2Nzg=", "ODc2NTQzMjE=");
|
||||
|
||||
BackupDataResult lmkBackupRound1 = new BackupDataResult();
|
||||
lmkBackupRound1.setLength(20);
|
||||
lmkBackupRound1.setData(new byte[] {
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x01, 0x02, 0x03, 0x04,
|
||||
0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0A, 0x0B, 0x0C
|
||||
});
|
||||
BackupDataResult lmkBackupRound2 = new BackupDataResult();
|
||||
lmkBackupRound2.setLength(20);
|
||||
lmkBackupRound2.setData(new byte[] {
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3A, 0x3B, 0x3C
|
||||
});
|
||||
|
||||
BackupDataResult ikAuthRound1 = new BackupDataResult();
|
||||
ikAuthRound1.setLength(new SdfeIkComponent().size());
|
||||
ikAuthRound1.setData(sequentialBytes(new SdfeIkComponent().size(), 0x21));
|
||||
BackupDataResult ikAuthRound2 = new BackupDataResult();
|
||||
ikAuthRound2.setLength(new SdfeIkComponent().size());
|
||||
ikAuthRound2.setData(sequentialBytes(new SdfeIkComponent().size(), 0x51));
|
||||
|
||||
BackupDataResult ikDeviceRound1 = new BackupDataResult();
|
||||
ikDeviceRound1.setLength(new SdfeIkComponent().size());
|
||||
ikDeviceRound1.setData(sequentialBytes(new SdfeIkComponent().size(), 0x41));
|
||||
BackupDataResult ikDeviceRound2 = new BackupDataResult();
|
||||
ikDeviceRound2.setLength(new SdfeIkComponent().size());
|
||||
ikDeviceRound2.setData(sequentialBytes(new SdfeIkComponent().size(), 0x71));
|
||||
|
||||
BackupDataResult userSignRound1 = new BackupDataResult();
|
||||
userSignRound1.setLength(12);
|
||||
userSignRound1.setData(new byte[] {0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C});
|
||||
BackupDataResult userSignRound2 = new BackupDataResult();
|
||||
userSignRound2.setLength(12);
|
||||
userSignRound2.setData(new byte[] {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C});
|
||||
|
||||
BackupDataResult userEncRound1 = new BackupDataResult();
|
||||
userEncRound1.setLength(12);
|
||||
userEncRound1.setData(new byte[] {0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x31, 0x32});
|
||||
BackupDataResult userEncRound2 = new BackupDataResult();
|
||||
userEncRound2.setLength(12);
|
||||
userEncRound2.setData(new byte[] {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x33, 0x34});
|
||||
|
||||
Mockito.when(pcieCryptoService.backupLmkEx()).thenReturn(lmkBackupRound1, lmkBackupRound2);
|
||||
Mockito.when(pcieCryptoService.backupIkComponent(2)).thenReturn(ikAuthRound1, ikAuthRound2);
|
||||
Mockito.when(pcieCryptoService.backupIkComponent(1)).thenReturn(ikDeviceRound1, ikDeviceRound2);
|
||||
Mockito.when(pcieCryptoService.backupUserKey(1, 0, 120)).thenReturn(userSignRound1, userSignRound2);
|
||||
Mockito.when(pcieCryptoService.backupUserKey(1, 1, 120)).thenReturn(userEncRound1, userEncRound2);
|
||||
|
||||
LmkService service = new LmkServiceImpl(pcieCryptoService, properties);
|
||||
|
||||
MasterKeyBackupPacket packet1 = service.buildBackupPacket(1);
|
||||
MasterKeyBackupPacket packet2 = service.buildBackupPacket(2);
|
||||
MasterKeyBackupPacket packet3 = service.buildBackupPacket(3);
|
||||
|
||||
Assertions.assertEquals("01020304", packet1.getLmk().getComponent());
|
||||
Assertions.assertEquals("05060708", packet2.getLmk().getComponent());
|
||||
Assertions.assertEquals("090a0b0c", packet3.getLmk().getComponent());
|
||||
Assertions.assertEquals("61626364", packet1.getUserKeys().get(0).getComponent());
|
||||
Assertions.assertEquals("65666768", packet2.getUserKeys().get(0).getComponent());
|
||||
Assertions.assertEquals("696a6b6c", packet3.getUserKeys().get(0).getComponent());
|
||||
|
||||
Mockito.verify(pcieCryptoService).backupLmkEx();
|
||||
Mockito.verify(pcieCryptoService).backupIkComponent(2);
|
||||
Mockito.verify(pcieCryptoService).backupIkComponent(1);
|
||||
Mockito.verify(pcieCryptoService).backupUserKey(1, 0, 120);
|
||||
Mockito.verify(pcieCryptoService).backupUserKey(1, 1, 120);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRecoverAllKeyPacketsInOrder() {
|
||||
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
|
||||
@ -220,14 +313,20 @@ class LmkServiceTest {
|
||||
ArgumentCaptor<byte[]> lmkCaptor = ArgumentCaptor.forClass(byte[].class);
|
||||
ArgumentCaptor<byte[]> ikAuthCaptor = ArgumentCaptor.forClass(byte[].class);
|
||||
ArgumentCaptor<byte[]> ikDeviceCaptor = ArgumentCaptor.forClass(byte[].class);
|
||||
ArgumentCaptor<RecoverUserKeyRequest> userKeyCaptor = ArgumentCaptor.forClass(RecoverUserKeyRequest.class);
|
||||
ArgumentCaptor<GenerateKeyPairRequest> generateSignCaptor = ArgumentCaptor.forClass(GenerateKeyPairRequest.class);
|
||||
ArgumentCaptor<RecoverUserKeyRequest> recoverSignCaptor = ArgumentCaptor.forClass(RecoverUserKeyRequest.class);
|
||||
ArgumentCaptor<GenerateKeyPairRequest> generateEncCaptor = ArgumentCaptor.forClass(GenerateKeyPairRequest.class);
|
||||
ArgumentCaptor<RecoverUserKeyRequest> recoverEncCaptor = ArgumentCaptor.forClass(RecoverUserKeyRequest.class);
|
||||
InOrder inOrder = Mockito.inOrder(pcieCryptoService);
|
||||
inOrder.verify(pcieCryptoService).initIdentify(oldPinCaptor.capture(), newPinCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).recoverLmkEx(lmkCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).loadLmk();
|
||||
inOrder.verify(pcieCryptoService).recoverIkComponent(Mockito.eq(2), ikAuthCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).recoverIkComponent(Mockito.eq(1), ikDeviceCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService, Mockito.times(2)).recoverUserKey(userKeyCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).generateKeyPairEcc(generateSignCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).recoverUserKey(recoverSignCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).generateKeyPairEcc(generateEncCaptor.capture());
|
||||
inOrder.verify(pcieCryptoService).recoverUserKey(recoverEncCaptor.capture());
|
||||
Assertions.assertArrayEquals("12345678".getBytes(StandardCharsets.UTF_8), oldPinCaptor.getValue());
|
||||
Assertions.assertArrayEquals("87654321".getBytes(StandardCharsets.UTF_8), newPinCaptor.getValue());
|
||||
Assertions.assertArrayEquals(new byte[] {
|
||||
@ -236,9 +335,14 @@ class LmkServiceTest {
|
||||
}, lmkCaptor.getValue());
|
||||
Assertions.assertArrayEquals(hex("1112131415162122232425262728313233344142434445464748"), ikAuthCaptor.getValue());
|
||||
Assertions.assertArrayEquals(hex("2122232425263132333435363738515253546162636465666768"), ikDeviceCaptor.getValue());
|
||||
Assertions.assertEquals(2, userKeyCaptor.getAllValues().size());
|
||||
Assertions.assertArrayEquals(hex("010203040506"), userKeyCaptor.getAllValues().get(0).getEncryptedKey());
|
||||
Assertions.assertArrayEquals(hex("111213141516"), userKeyCaptor.getAllValues().get(1).getEncryptedKey());
|
||||
Assertions.assertEquals(256, generateSignCaptor.getValue().getKeyBits());
|
||||
Assertions.assertEquals(1, generateSignCaptor.getValue().getKeyIndex());
|
||||
Assertions.assertEquals(PcieUserKeyType.SIGN.getCode(), generateSignCaptor.getValue().getKeyType());
|
||||
Assertions.assertEquals(256, generateEncCaptor.getValue().getKeyBits());
|
||||
Assertions.assertEquals(1, generateEncCaptor.getValue().getKeyIndex());
|
||||
Assertions.assertEquals(PcieUserKeyType.ENC.getCode(), generateEncCaptor.getValue().getKeyType());
|
||||
Assertions.assertArrayEquals(hex("010203040506"), recoverSignCaptor.getValue().getEncryptedKey());
|
||||
Assertions.assertArrayEquals(hex("111213141516"), recoverEncCaptor.getValue().getEncryptedKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Loading…
Reference in New Issue
Block a user