主密钥初始化

This commit is contained in:
waner 2026-03-30 16:23:56 +08:00
parent fbe23b8ecb
commit 7a7503c35e
17 changed files with 753 additions and 57 deletions

View File

@ -235,6 +235,10 @@ public interface PcieNativeLibrary extends Library {
int SDFE_StatusIK(Pointer hSessionHandle, int keyType, IntByReference keyState);
int SYD_IK_BackUp(Pointer hDeviceHandle, int keyType, int exFlag, SydIkComponent ikComponent);
int SYD_IK_Recover(Pointer hDeviceHandle, int keyType, int exFlag, SydIkComponent ikComponent);
int SDFE_GenerateUkey(Pointer hSessionHandle, byte adminIndex,
byte[] ukeyPin, int ukeyPinLen,
byte[] loginPin, int loginPinLen);

View File

@ -0,0 +1,20 @@
package com.cisd.tms.integration.crypto.pcie.jna;
import com.sun.jna.Structure;
import java.util.List;
@Structure.FieldOrder({"component", "mac"})
public class SydIkComponent extends Structure {
public static final int COMPONENT_COUNT = 3;
public static final int COMPONENT_LENGTH = 64;
public byte[] component = new byte[COMPONENT_COUNT * COMPONENT_LENGTH];
public byte[] mac = new byte[8];
@Override
protected List<String> getFieldOrder() {
return List.of("component", "mac");
}
}

View File

@ -20,6 +20,7 @@ import com.cisd.tms.integration.crypto.pcie.jna.SdfeDeviceStatus;
import com.cisd.tms.integration.crypto.pcie.jna.SdfeKekComponent;
import com.cisd.tms.integration.crypto.pcie.jna.SdfeLmkComponent;
import com.cisd.tms.integration.crypto.pcie.jna.SdfeLoginState;
import com.cisd.tms.integration.crypto.pcie.jna.SydIkComponent;
import com.cisd.tms.integration.crypto.pcie.model.AgreementDataAndKeyEccResult;
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
import com.cisd.tms.integration.crypto.pcie.model.ChangePrivateKeyPasswordRequest;
@ -199,7 +200,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
public byte[] initializeMasterKey(byte[] oldPin, byte[] newPin) {
byte[] oldPinValue = requireNonEmptyBytes("oldPin", oldPin);
byte[] newPinValue = requireNonEmptyBytes("newPin", newPin);
//lmk初始化包含主密钥生成加载内部密钥生成用户密钥生成返回主密钥校验值
try {
return sessionTemplate.withSession("SDFE_InitializeMasterKey", (lib, deviceHandle, sessionHandle) -> {
sessionTemplate.ensureSuccess(
@ -210,7 +211,14 @@ public class JnaPcieCryptoService implements PcieCryptoService {
sessionTemplate.ensureSuccess("SDFE_LoadLMK", lib.SDFE_LoadLMK(sessionHandle));
sessionTemplate.ensureSuccess("SDFE_GenerateIK", lib.SDFE_GenerateIK(sessionHandle, IK_KEY_TYPE_DEVICE));
sessionTemplate.ensureSuccess("SDFE_GenerateIK", lib.SDFE_GenerateIK(sessionHandle, IK_KEY_TYPE_AUTH));
sessionTemplate.ensureSuccess(
"SDFE_GenerateKeyPair_ECC",
lib.SDFE_GenerateKeyPair_ECC(sessionHandle, 256, 1, PcieUserKeyType.SIGN.getCode())
);
sessionTemplate.ensureSuccess(
"SDFE_GenerateKeyPair_ECC",
lib.SDFE_GenerateKeyPair_ECC(sessionHandle, 256, 1, PcieUserKeyType.ENC.getCode())
);
int retCode = lib.SDFE_CheckLMK(sessionHandle);
if (retCode != 0) {
if ((retCode & 0xFF) == SDFE_INIT_STATUS_LOW_BYTE) {
@ -331,7 +339,8 @@ public class JnaPcieCryptoService implements PcieCryptoService {
int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex());
int keyType = requireUserKeyType(req.getKeyType());
//keyType: 1-加密密钥对0-签名密钥对
//keyIndex[18]
sessionTemplate.withSession("SDFE_GenerateKeyPair_ECC", (lib, deviceHandle, sessionHandle) -> {
sessionTemplate.ensureSuccess(
"SDFE_GenerateKeyPair_ECC",
@ -467,6 +476,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
public BackupDataResult backupUserKey(int keyIndex, int keyType, int outBufferSize) {
int safeKeyIndex = requireNonNegative("keyIndex", keyIndex);
int safeKeyType = requireUserKeyType(keyType);
//size: 120
int safeOutBufferSize = requirePositive("outBufferSize", outBufferSize);
return sessionTemplate.withSession("SDFE_BackupUserKey", (lib, deviceHandle, sessionHandle) -> {
@ -1679,6 +1689,30 @@ public class JnaPcieCryptoService implements PcieCryptoService {
});
}
@Override
public BackupDataResult backupIkComponent(int keyType) {
int safeKeyType = requireNonNegative("keyType", keyType);
//todo 对接真实接口
return sessionTemplate.withSession("SYD_IK_BackUp", (lib, deviceHandle, sessionHandle) -> {
SydIkComponent output = new SydIkComponent();
sessionTemplate.ensureSuccess("SYD_IK_BackUp", lib.SYD_IK_BackUp(deviceHandle, safeKeyType, 1, output));
byte[] outputBytes = toBytes(output);
return toBackupResult("SYD_IK_BackUp", outputBytes, outputBytes.length);
});
}
@Override
public void recoverIkComponent(int keyType, byte[] ikComponent) {
int safeKeyType = requireNonNegative("keyType", keyType);
SydIkComponent input = toSydIkComponent("ikComponent", ikComponent);
sessionTemplate.withSession("SYD_IK_Recover", (lib, deviceHandle, sessionHandle) -> {
sessionTemplate.ensureSuccess("SYD_IK_Recover", lib.SYD_IK_Recover(deviceHandle, safeKeyType, 1, input));
return null;
});
}
@Override
public void backupIk(int keyType) {
int safeKeyType = requireNonNegative("keyType", keyType);
@ -2129,6 +2163,10 @@ public class JnaPcieCryptoService implements PcieCryptoService {
return toStructure(name, value, new SdfeLmkComponent(), false);
}
private static SydIkComponent toSydIkComponent(String name, byte[] value) {
return toStructure(name, value, new SydIkComponent(), false);
}
private static EnvelopedKeyBlob toEnvelopedKeyBlob(String name, byte[] value) {
return toStructure(name, value, new EnvelopedKeyBlob(), true);
}

View File

@ -707,6 +707,18 @@ public class MockPcieCryptoService implements PcieCryptoService {
requireNonNegative("keyType", keyType);
}
@Override
public BackupDataResult backupIkComponent(int keyType) {
requireNonNegative("keyType", keyType);
return mockBackup(new com.cisd.tms.integration.crypto.pcie.jna.SydIkComponent().size(), keyType);
}
@Override
public void recoverIkComponent(int keyType, byte[] ikComponent) {
requireNonNegative("keyType", keyType);
requireNonEmptyBytes("ikComponent", ikComponent);
}
@Override
public void backupIk(int keyType) {
requireNonNegative("keyType", keyType);

View File

@ -217,6 +217,10 @@ public interface PcieCryptoService {
void generateIk(int keyType);
BackupDataResult backupIkComponent(int keyType);
void recoverIkComponent(int keyType, byte[] ikComponent);
void backupIk(int keyType);
void recoverIk(int keyType);

View File

@ -28,7 +28,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/init")
@Tag(name = "CISD初始化任务", description = "初始化模板、预检、任务创建、执行与日志查询接口")
@Tag(name = "CISD配置初始化和重置任务", description = "初始化和重置任务创建、执行与日志查询接口")
public class InitController {
private final InitService initService;

View File

@ -162,6 +162,7 @@ public class InitService {
}
String productType = resolvePresetProductType();
//todo 验证是否已经初始化
validateCommon(request);
validateByProductType(productType, request);

View File

@ -3,6 +3,8 @@ package com.cisd.tms.modules.mk.controller;
import com.cisd.tms.common.api.ApiResponse;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupMaterial;
import com.cisd.tms.modules.mk.dto.MasterKeyRecoverRequest;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket;
import com.cisd.tms.modules.mk.dto.MasterKeyRecoverPacketsRequest;
import com.cisd.tms.modules.mk.dto.MasterKeyStateResult;
import com.cisd.tms.modules.mk.dto.UKeySignDTO;
import com.cisd.tms.modules.mk.dto.UKeySignEntity;
@ -14,16 +16,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* @author liulu
* @since 2026/3/4
*/
@Slf4j
@RestController
@RequestMapping("/api/v1")
@Tag(name = "主密钥管理", description = "LMK 生成、恢复、状态查询和销毁接口")
@ -56,31 +56,49 @@ public class LmkController {
return ApiResponse.success(lmkService.queryMasterKeyStatus());
}
/**
* 备份全部密钥中的单份分量包
* 该接口需要按 uid=123 分别调用三次
* uid=1 返回第 1 份分量包uid=2 返回第 2 份分量包uid=3 返回第 3 份分量包
* 每份分量包中同时包含 LMKIK_AUTHIK_DEVICEUSER_SIGNUSER_ENC 的对应分量
*/
@PostMapping("/masterKey/backup")
@Operation(summary = "备份主密钥分量至UKey", description = "根据请求中的 UKey 身份信息生成签名,并按照 uid 作为分量索引返回对应的主密钥分量,同时返回恢复 LMK 必需的 lmkMac。uid 取值为 1、2、3。")
@Operation(summary = "备份密钥分量至UKey", description = "根据请求中的 UKey 身份信息生成签名,并按照 uid 作为分量索引返回对应的一份全量分量包。uid 取值为 1、2、3需调用三次分别保存到三个外部 UKey 中。返回结果中的 backupPacket 即本次应保存的分量包")
public ApiResponse<UKeySignResult> uKeySign(@Valid @RequestBody UKeySignDTO uKeySignDTO) {
String signValue = signUkeyPayload(uKeySignDTO);
int componentIndex = Integer.parseInt(uKeySignDTO.getUid());
MasterKeyBackupMaterial material = lmkService.getBackupMaterial(componentIndex);
MasterKeyBackupPacket packet = lmkService.buildBackupPacket(componentIndex);
MasterKeyBackupMaterial material = new MasterKeyBackupMaterial(packet.getLmk().getComponent(), packet.getLmk().getLmkMac());
return ApiResponse.success(UKeySignResult.builder()
.sign(signValue)
.component(material.getComponent())
.lmkMac(material.getLmkMac())
// .component(material.getComponent())
// .lmkMac(material.getLmkMac())
.backupPacket(packet)
.build());
}
/**
* 恢复密钥
* 请求体需同时传入 lmkMac 3 个按顺序排列的主密钥分量字符串
* 1 个元素对应分量 1 2 个元素对应分量 2 3 个元素对应分量 3
* 恢复所有备份密钥
* 请求体需传入 componentIndex=1/2/3 的三份分量包
* 服务端会先执行 InitIdentify然后依次恢复 LMK加载 LMK恢复 IK恢复用户密钥
*/
@PostMapping("/masterKey/recover")
@Operation(summary = "恢复 LMK", description = "请求体需传入 8 字节校验值 lmkMac 和 3 个按顺序排列的主密钥分量字符串,列表下标 0、1、2 分别对应分量 1、2、3。服务端按 SDFE_LmkComponent 结构拼接后执行恢复并加载 LMK")
public ApiResponse<Void> recoverLmkEx(@Valid @RequestBody MasterKeyRecoverRequest request) {
lmkService.recoverLmkEx(request.getLmkMac(), request.getComponents());
@Operation(summary = "恢复全部密钥", description = "请求体需传入三份按 componentIndex 标识的分量包。三份分量包必须分别对应 componentIndex=1、2、3。服务端会依次恢复 LMK、IK 和用户密钥")
public ApiResponse<Void> recoverLmkEx(@Valid @RequestBody MasterKeyRecoverPacketsRequest request) {
lmkService.recoverKeyPackets(request.getPackets());
return ApiResponse.success();
}
@GetMapping("/masterKey/sign")
@Operation(summary = "签名测试", description = "")
public ApiResponse<Void> signIk(@RequestParam String data) {
String signData = lmkService.signIk(data);
log.info("signData:{}", signData);
lmkService.verifyIk(data, signData);
return ApiResponse.success();
}
private String signUkeyPayload(UKeySignDTO uKeySignDTO) {
String iPubKey = lmkService.exportIkPublicKeyHex();
String payload;

View File

@ -0,0 +1,53 @@
package com.cisd.tms.modules.mk.dto;
import java.util.ArrayList;
import java.util.List;
public class MasterKeyBackupPacket {
private int componentIndex;
private MasterKeyPacketLmkPart lmk;
private MasterKeyPacketIkPart ikAuth;
private MasterKeyPacketIkPart ikDevice;
private List<MasterKeyPacketUserKeyPart> userKeys = new ArrayList<>();
public int getComponentIndex() {
return componentIndex;
}
public void setComponentIndex(int componentIndex) {
this.componentIndex = componentIndex;
}
public MasterKeyPacketLmkPart getLmk() {
return lmk;
}
public void setLmk(MasterKeyPacketLmkPart lmk) {
this.lmk = lmk;
}
public MasterKeyPacketIkPart getIkAuth() {
return ikAuth;
}
public void setIkAuth(MasterKeyPacketIkPart ikAuth) {
this.ikAuth = ikAuth;
}
public MasterKeyPacketIkPart getIkDevice() {
return ikDevice;
}
public void setIkDevice(MasterKeyPacketIkPart ikDevice) {
this.ikDevice = ikDevice;
}
public List<MasterKeyPacketUserKeyPart> getUserKeys() {
return userKeys;
}
public void setUserKeys(List<MasterKeyPacketUserKeyPart> userKeys) {
this.userKeys = userKeys;
}
}

View File

@ -0,0 +1,41 @@
package com.cisd.tms.modules.mk.dto;
public class MasterKeyPacketIkPart {
private int keyType;
private String mac;
private String component;
public MasterKeyPacketIkPart() {
}
public MasterKeyPacketIkPart(int keyType, String mac, String component) {
this.keyType = keyType;
this.mac = mac;
this.component = component;
}
public int getKeyType() {
return keyType;
}
public void setKeyType(int keyType) {
this.keyType = keyType;
}
public String getMac() {
return mac;
}
public void setMac(String mac) {
this.mac = mac;
}
public String getComponent() {
return component;
}
public void setComponent(String component) {
this.component = component;
}
}

View File

@ -0,0 +1,31 @@
package com.cisd.tms.modules.mk.dto;
public class MasterKeyPacketLmkPart {
private String lmkMac;
private String component;
public MasterKeyPacketLmkPart() {
}
public MasterKeyPacketLmkPart(String lmkMac, String component) {
this.lmkMac = lmkMac;
this.component = component;
}
public String getLmkMac() {
return lmkMac;
}
public void setLmkMac(String lmkMac) {
this.lmkMac = lmkMac;
}
public String getComponent() {
return component;
}
public void setComponent(String component) {
this.component = component;
}
}

View File

@ -0,0 +1,41 @@
package com.cisd.tms.modules.mk.dto;
public class MasterKeyPacketUserKeyPart {
private int keyIndex;
private int keyType;
private String component;
public MasterKeyPacketUserKeyPart() {
}
public MasterKeyPacketUserKeyPart(int keyIndex, int keyType, String component) {
this.keyIndex = keyIndex;
this.keyType = keyType;
this.component = component;
}
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 String getComponent() {
return component;
}
public void setComponent(String component) {
this.component = component;
}
}

View File

@ -0,0 +1,22 @@
package com.cisd.tms.modules.mk.dto;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import java.util.List;
public class MasterKeyRecoverPacketsRequest {
@NotEmpty(message = "packets must not be empty")
@Size(min = 3, max = 3, message = "packets size must be 3")
private List<@Valid MasterKeyBackupPacket> packets;
public List<MasterKeyBackupPacket> getPackets() {
return packets;
}
public void setPackets(List<MasterKeyBackupPacket> packets) {
this.packets = packets;
}
}

View File

@ -20,4 +20,6 @@ public class UKeySignResult {
private String component;
private String lmkMac;
private MasterKeyBackupPacket backupPacket;
}

View File

@ -1,15 +1,22 @@
package com.cisd.tms.modules.mk.service;
import com.cisd.tms.common.util.DivisionUtils;
import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds;
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.EccInternalSignRequest;
import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest;
import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.mk.config.MasterKeyInitProperties;
import com.cisd.tms.modules.mk.common.LMKConstant;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupMaterial;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketIkPart;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketLmkPart;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketUserKeyPart;
import com.cisd.tms.modules.mk.dto.MasterKeyStateResult;
import com.cisd.tms.modules.mk.enums.IKEnums;
import com.cisd.tms.modules.mk.enums.MasterKeyStatus;
@ -18,6 +25,7 @@ import org.bouncycastle.util.encoders.Hex;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
@ -34,6 +42,12 @@ public class LmkService {
private static final int AUTH_SIGN_KEY_INDEX = IKEnums.KEY_TYPE_AUTH.getCode();
private static final int LMK_MAC_BYTE_LENGTH = 8;
private static final int IK_COMPONENT_MAC_BYTE_LENGTH = 8;
private static final int IK_COMPONENT_DATA_LENGTH = 3 * 64;
private static final int USER_KEY_INDEX = 1;
private static final int USER_KEY_TYPE_SIGN = 0;
private static final int USER_KEY_TYPE_ENC = 1;
private static final int USER_KEY_BACKUP_BUFFER_SIZE = 120;
private final PcieCryptoService pcieCryptoService;
private final MasterKeyInitProperties masterKeyInitProperties;
@ -74,35 +88,76 @@ public class LmkService {
}
public String signIk(String rawData) {
byte[] digest = digestForInternalSign(rawData);
EccInternalSignRequest request = new EccInternalSignRequest();
request.setKeyIndex(AUTH_SIGN_KEY_INDEX);
request.setData(rawData.getBytes(StandardCharsets.UTF_8));
request.setData(digest);
request.setSignatureBufferSize(new EccSignature().size());
BackupDataResult result = pcieCryptoService.eccInternalSign(request);
return Hex.toHexString(result.getData());
}
public void verifyIk(String rawData, String signatureHex) {
byte[] digest = digestForInternalSign(rawData);
EccInternalVerifyRequest request = new EccInternalVerifyRequest();
request.setKeyIndex(AUTH_SIGN_KEY_INDEX);
request.setData(rawData.getBytes(StandardCharsets.UTF_8));
request.setData(digest);
request.setSignature(Hex.decode(signatureHex));
pcieCryptoService.eccInternalVerify(request);
}
public MasterKeyBackupMaterial getBackupMaterial(int uid) {
ParsedMasterKeyBackup parsedBackup = parseBackupMaterial();
String component = parsedBackup.componentMap().get(uid);
if (component == null) {
throw new IllegalArgumentException("invalid component index: " + uid);
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());
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")
));
packet.setIkDevice(new MasterKeyPacketIkPart(
IKEnums.KEY_TYPE_DEVICE.getCode(),
deviceIkBackup.macHex(),
requiredComponent(deviceIkBackup.componentMap(), index, "ikDevice")
));
packet.setUserKeys(List.of(
buildUserKeyPacket(USER_KEY_INDEX, USER_KEY_TYPE_SIGN, index),
buildUserKeyPacket(USER_KEY_INDEX, USER_KEY_TYPE_ENC, index)
));
return packet;
}
return new MasterKeyBackupMaterial(component, parsedBackup.lmkMacHex());
public MasterKeyBackupMaterial getBackupMaterial(int uid) {
MasterKeyBackupPacket packet = buildBackupPacket(uid);
return new MasterKeyBackupMaterial(packet.getLmk().getComponent(), packet.getLmk().getLmkMac());
}
public String getComponent(int uid) {
return getBackupMaterial(uid).getComponent();
}
public void recoverKeyPackets(List<MasterKeyBackupPacket> packets) {
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());
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)));
pcieCryptoService.recoverUserKey(toRecoverUserKeyRequest(orderedPackets, USER_KEY_INDEX, USER_KEY_TYPE_SIGN));
pcieCryptoService.recoverUserKey(toRecoverUserKeyRequest(orderedPackets, USER_KEY_INDEX, USER_KEY_TYPE_ENC));
}
public void recoverLmkEx(String lmkMacHex, List<String> components) {
byte[] oldPin = decodeConfiguredPin("tms.mk.init-identify.old-pin-base64", masterKeyInitProperties.getOldPinBase64());
byte[] newPin = decodeConfiguredPin("tms.mk.init-identify.new-pin-base64", masterKeyInitProperties.getNewPinBase64());
@ -141,8 +196,48 @@ public class LmkService {
return lmk.toString();
}
private String composeLmkHex(List<MasterKeyBackupPacket> packets) {
String lmkMac = requireConsistentLmkMac(packets);
StringBuilder builder = new StringBuilder(lmkMac);
for (MasterKeyBackupPacket packet : packets) {
builder.append(packet.getLmk().getComponent());
}
return builder.toString();
}
private String composeIkHex(List<MasterKeyBackupPacket> packets, boolean authKey) {
int keyType = authKey ? IKEnums.KEY_TYPE_AUTH.getCode() : IKEnums.KEY_TYPE_DEVICE.getCode();
String mac = requireConsistentIkMac(packets, keyType);
StringBuilder builder = new StringBuilder();
for (MasterKeyBackupPacket packet : packets) {
MasterKeyPacketIkPart part = authKey ? packet.getIkAuth() : packet.getIkDevice();
if (part == null || part.getComponent() == null || part.getComponent().isBlank()) {
throw new IllegalArgumentException((authKey ? "ikAuth" : "ikDevice") + " component missing in packet " + packet.getComponentIndex());
}
builder.append(part.getComponent());
}
builder.append(mac);
return builder.toString();
}
private RecoverUserKeyRequest toRecoverUserKeyRequest(List<MasterKeyBackupPacket> packets, int keyIndex, int keyType) {
RecoverUserKeyRequest request = new RecoverUserKeyRequest();
request.setKeyIndex(keyIndex);
request.setKeyType(keyType);
request.setEncryptedKey(Hex.decode(composeUserKeyHex(packets, keyIndex, 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);
builder.append(part.getComponent());
}
return builder.toString();
}
private ParsedMasterKeyBackup parseBackupMaterial() {
//todo 需要将主密钥分量内部密钥分量一起拆分后进行备份密码卡暂时没有导出内部密钥分量接口
BackupDataResult backupData = pcieCryptoService.backupLmkEx();
byte[] normalized = normalizeBackupData(backupData);
if (normalized.length < LMK_MAC_BYTE_LENGTH) {
@ -158,6 +253,29 @@ public class LmkService {
return new ParsedMasterKeyBackup(Hex.toHexString(lmkMac), componentMap);
}
private ParsedIkBackup parseIkBackupMaterial(int keyType) {
BackupDataResult backupData = pcieCryptoService.backupIkComponent(keyType);
byte[] normalized = normalizeBackupData(backupData);
if (normalized.length < IK_COMPONENT_DATA_LENGTH + IK_COMPONENT_MAC_BYTE_LENGTH) {
throw new IllegalStateException("backup ik data length is too short: " + normalized.length);
}
byte[] componentBytes = Arrays.copyOfRange(normalized, 0, IK_COMPONENT_DATA_LENGTH);
byte[] mac = Arrays.copyOfRange(normalized, IK_COMPONENT_DATA_LENGTH, IK_COMPONENT_DATA_LENGTH + IK_COMPONENT_MAC_BYTE_LENGTH);
String[] splitComponents = DivisionUtils.equalDivision(Hex.toHexString(componentBytes), LMKConstant.COMPONENT_NUM);
Map<Integer, String> componentMap = new HashMap<>();
for (int i = 0; i < LMKConstant.COMPONENT_NUM; i++) {
componentMap.put(LMKConstant.KEY_ORDER[i], splitComponents[i]);
}
return new ParsedIkBackup(Hex.toHexString(mac), componentMap);
}
private MasterKeyPacketUserKeyPart buildUserKeyPacket(int keyIndex, int keyType, int componentIndex) {
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]);
}
private byte[] normalizeBackupData(BackupDataResult backupData) {
byte[] data = backupData.getData();
if (data == null || data.length == 0) {
@ -179,7 +297,104 @@ public class LmkService {
}
}
private byte[] digestForInternalSign(String rawData) {
DigestRequest request = new DigestRequest();
request.setAlgId(Gm0018AlgorithmIds.SM3);
request.setData(rawData.getBytes(StandardCharsets.UTF_8));
BackupDataResult result = pcieCryptoService.digest(request);
return normalizeBackupData(result);
}
private int requireComponentIndex(int componentIndex) {
if (componentIndex < 1 || componentIndex > LMKConstant.COMPONENT_NUM) {
throw new IllegalArgumentException("componentIndex must be 1, 2 or 3");
}
return componentIndex;
}
private String requiredComponent(Map<Integer, String> componentMap, int componentIndex, String name) {
String component = componentMap.get(componentIndex);
if (component == null || component.isBlank()) {
throw new IllegalArgumentException(name + " component missing: " + componentIndex);
}
return component;
}
private List<MasterKeyBackupPacket> sortPackets(List<MasterKeyBackupPacket> packets) {
if (packets == null || packets.size() != LMKConstant.COMPONENT_NUM) {
throw new IllegalArgumentException("packets size must be " + LMKConstant.COMPONENT_NUM);
}
Map<Integer, MasterKeyBackupPacket> packetMap = new HashMap<>();
for (MasterKeyBackupPacket packet : packets) {
if (packet == null) {
throw new IllegalArgumentException("packet must not be null");
}
int componentIndex = requireComponentIndex(packet.getComponentIndex());
if (packetMap.put(componentIndex, packet) != null) {
throw new IllegalArgumentException("duplicate packet for componentIndex " + componentIndex);
}
}
List<MasterKeyBackupPacket> ordered = new ArrayList<>();
for (int componentIndex : LMKConstant.KEY_ORDER) {
MasterKeyBackupPacket packet = packetMap.get(componentIndex);
if (packet == null) {
throw new IllegalArgumentException("packet missing for componentIndex " + componentIndex);
}
ordered.add(packet);
}
return ordered;
}
private String requireConsistentLmkMac(List<MasterKeyBackupPacket> packets) {
String expected = null;
for (MasterKeyBackupPacket packet : packets) {
if (packet.getLmk() == null || packet.getLmk().getLmkMac() == null || packet.getLmk().getLmkMac().isBlank()) {
throw new IllegalArgumentException("lmk mac missing in packet " + packet.getComponentIndex());
}
String current = packet.getLmk().getLmkMac();
if (expected == null) {
expected = current;
continue;
}
if (!expected.equalsIgnoreCase(current)) {
throw new IllegalArgumentException("lmk mac is inconsistent across packets");
}
}
return expected;
}
private String requireConsistentIkMac(List<MasterKeyBackupPacket> packets, int keyType) {
String expected = null;
for (MasterKeyBackupPacket packet : packets) {
MasterKeyPacketIkPart part = keyType == IKEnums.KEY_TYPE_AUTH.getCode() ? packet.getIkAuth() : packet.getIkDevice();
if (part == null || part.getMac() == null || part.getMac().isBlank()) {
throw new IllegalArgumentException("ik mac missing for keyType " + keyType);
}
if (expected == null) {
expected = part.getMac();
continue;
}
if (!expected.equalsIgnoreCase(part.getMac())) {
throw new IllegalArgumentException("ik mac is inconsistent across packets for keyType " + keyType);
}
}
return expected;
}
private MasterKeyPacketUserKeyPart findUserKeyPart(MasterKeyBackupPacket packet, int keyIndex, int keyType) {
if (packet.getUserKeys() == null) {
throw new IllegalArgumentException("userKeys missing in packet " + packet.getComponentIndex());
}
return packet.getUserKeys().stream()
.filter(part -> part.getKeyIndex() == keyIndex && part.getKeyType() == keyType)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("user key component missing in packet " + packet.getComponentIndex()));
}
private record ParsedMasterKeyBackup(String lmkMacHex, Map<Integer, String> componentMap) {
}
private record ParsedIkBackup(String macHex, Map<Integer, String> componentMap) {
}
}

View File

@ -1,7 +1,10 @@
package com.cisd.tms.modules.mk.controller;
import com.cisd.tms.common.exception.GlobalExceptionHandler;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupMaterial;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketIkPart;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketLmkPart;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketUserKeyPart;
import com.cisd.tms.modules.mk.dto.MasterKeyStateResult;
import com.cisd.tms.modules.mk.service.LmkService;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -26,8 +29,16 @@ class LmkControllerTest {
LmkService lmkService = Mockito.mock(LmkService.class);
Mockito.when(lmkService.exportIkPublicKeyHex()).thenReturn("IK-PUB-001");
Mockito.when(lmkService.signIk(ArgumentMatchers.anyString())).thenReturn("SIGN-001");
Mockito.when(lmkService.getBackupMaterial(2))
.thenReturn(new MasterKeyBackupMaterial("COMP-002", "LMK-MAC-001"));
MasterKeyBackupPacket packet = new MasterKeyBackupPacket();
packet.setComponentIndex(2);
packet.setLmk(new MasterKeyPacketLmkPart("LMK-MAC-001", "LMK-COMP-002"));
packet.setIkAuth(new MasterKeyPacketIkPart(2, "IK-AUTH-MAC-001", "IK-AUTH-COMP-002"));
packet.setIkDevice(new MasterKeyPacketIkPart(1, "IK-DEVICE-MAC-001", "IK-DEVICE-COMP-002"));
packet.setUserKeys(java.util.List.of(
new MasterKeyPacketUserKeyPart(1, 0, "USER-SIGN-COMP-002"),
new MasterKeyPacketUserKeyPart(1, 1, "USER-ENC-COMP-002")
));
Mockito.when(lmkService.buildBackupPacket(2)).thenReturn(packet);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new LmkController(lmkService, new ObjectMapper()))
.setControllerAdvice(new GlobalExceptionHandler())
@ -46,15 +57,21 @@ class LmkControllerTest {
"""))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"sign\":\"SIGN-001\"")))
.andExpect(content().string(containsString("\"component\":\"COMP-002\"")))
.andExpect(content().string(containsString("\"lmkMac\":\"LMK-MAC-001\"")));
.andExpect(content().string(containsString("\"componentIndex\":2")))
.andExpect(content().string(containsString("\"lmkMac\":\"LMK-MAC-001\"")))
.andExpect(content().string(containsString("\"component\":\"LMK-COMP-002\"")))
.andExpect(content().string(containsString("\"ikAuth\"")))
.andExpect(content().string(containsString("\"ikDevice\"")))
.andExpect(content().string(containsString("\"userKeys\"")))
.andExpect(content().string(containsString("\"USER-SIGN-COMP-002\"")))
.andExpect(content().string(containsString("\"USER-ENC-COMP-002\"")));
Mockito.verify(lmkService).signIk(eq("{\"pubKey\":\"PUB-001\",\"authKeyPair\":\"IK-PUB-001\",\"role\":\"superadmin\",\"uid\":\"2\",\"rid\":\"RID-001\"}"));
Mockito.verify(lmkService).getBackupMaterial(2);
Mockito.verify(lmkService).buildBackupPacket(2);
}
@Test
void shouldRecoverLmkExUsingLmkMacAndOrderedComponents() throws Exception {
void shouldRecoverKeyPacketsUsingOrderedPackets() throws Exception {
LmkService lmkService = Mockito.mock(LmkService.class);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new LmkController(lmkService, new ObjectMapper()))
@ -65,17 +82,41 @@ class LmkControllerTest {
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"packets": [
{
"componentIndex": 1,
"lmk": {
"lmkMac": "a1a2a3a4a5a6a7a8",
"components": [
"0102",
"0304",
"0506"
"component": "0102"
},
"ikAuth": {
"mac": "1111111111111111",
"component": "0304"
},
"ikDevice": {
"mac": "2222222222222222",
"component": "0506"
},
"userKeys": [
{
"keyIndex": 1,
"keyType": 0,
"component": "0708"
}
]
},
{
"componentIndex": 2
},
{
"componentIndex": 3
}
]
}
"""))
.andExpect(status().isOk());
Mockito.verify(lmkService).recoverLmkEx(eq("a1a2a3a4a5a6a7a8"), eq(java.util.List.of("0102", "0304", "0506")));
Mockito.verify(lmkService).recoverKeyPackets(ArgumentMatchers.argThat(packets -> packets != null && packets.size() == 3));
}
@Test

View File

@ -1,16 +1,24 @@
package com.cisd.tms.modules.mk.service;
import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds;
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.RecoverUserKeyRequest;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.mk.config.MasterKeyInitProperties;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupMaterial;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketIkPart;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketLmkPart;
import com.cisd.tms.modules.mk.dto.MasterKeyPacketUserKeyPart;
import com.cisd.tms.modules.mk.dto.MasterKeyStateResult;
import java.util.List;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;
class LmkServiceTest {
@ -79,48 +87,181 @@ class LmkServiceTest {
}
@Test
void shouldReturnIndexedMasterKeyComponentAndMacFromBackupBlob() {
void shouldBuildBackupPacketForSingleComponentIndex() {
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
MasterKeyInitProperties properties = configuredPins("MTIzNDU2Nzg=", "ODc2NTQzMjE=");
BackupDataResult backup = new BackupDataResult();
backup.setLength(20);
backup.setData(new byte[] {
BackupDataResult lmkBackup = new BackupDataResult();
lmkBackup.setLength(20);
lmkBackup.setData(new byte[] {
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C
});
Mockito.when(pcieCryptoService.backupLmkEx()).thenReturn(backup);
BackupDataResult ikAuthBackup = new BackupDataResult();
ikAuthBackup.setLength(200);
ikAuthBackup.setData(sequentialBytes(200, 0x21));
BackupDataResult ikDeviceBackup = new BackupDataResult();
ikDeviceBackup.setLength(200);
ikDeviceBackup.setData(sequentialBytes(200, 0x41));
BackupDataResult userSignBackup = new BackupDataResult();
userSignBackup.setLength(12);
userSignBackup.setData(new byte[] {0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C});
BackupDataResult userEncBackup = new BackupDataResult();
userEncBackup.setLength(12);
userEncBackup.setData(new byte[] {0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x31, 0x32});
Mockito.when(pcieCryptoService.backupLmkEx()).thenReturn(lmkBackup);
Mockito.when(pcieCryptoService.backupIkComponent(2)).thenReturn(ikAuthBackup);
Mockito.when(pcieCryptoService.backupIkComponent(1)).thenReturn(ikDeviceBackup);
Mockito.when(pcieCryptoService.backupUserKey(1, 0, 120)).thenReturn(userSignBackup);
Mockito.when(pcieCryptoService.backupUserKey(1, 1, 120)).thenReturn(userEncBackup);
LmkService service = new LmkService(pcieCryptoService, properties);
MasterKeyBackupMaterial material = service.getBackupMaterial(2);
MasterKeyBackupPacket packet = service.buildBackupPacket(2);
Assertions.assertEquals("05060708", material.getComponent());
Assertions.assertEquals("1112131415161718", material.getLmkMac());
Assertions.assertEquals(2, packet.getComponentIndex());
Assertions.assertEquals("1112131415161718", packet.getLmk().getLmkMac());
Assertions.assertEquals("05060708", packet.getLmk().getComponent());
Assertions.assertEquals(2, packet.getIkAuth().getKeyType());
Assertions.assertEquals(
org.bouncycastle.util.encoders.Hex.toHexString(java.util.Arrays.copyOfRange(ikAuthBackup.getData(), 64, 128)),
packet.getIkAuth().getComponent()
);
Assertions.assertEquals(1, packet.getIkDevice().getKeyType());
Assertions.assertEquals(
org.bouncycastle.util.encoders.Hex.toHexString(java.util.Arrays.copyOfRange(ikDeviceBackup.getData(), 64, 128)),
packet.getIkDevice().getComponent()
);
Assertions.assertEquals(2, packet.getUserKeys().size());
Assertions.assertEquals("65666768", packet.getUserKeys().get(0).getComponent());
Assertions.assertEquals("75767778", packet.getUserKeys().get(1).getComponent());
}
@Test
void shouldInitIdentifyBeforeRecoverLmkExAndLoadIt() {
void shouldRecoverAllKeyPacketsInOrder() {
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
MasterKeyInitProperties properties = configuredPins("MTIzNDU2Nzg=", "ODc2NTQzMjE=");
LmkService service = new LmkService(pcieCryptoService, properties);
service.recoverLmkEx("1112131415161718", List.of("0102", "0304", "0506"));
MasterKeyBackupPacket packet1 = new MasterKeyBackupPacket();
packet1.setComponentIndex(1);
packet1.setLmk(new MasterKeyPacketLmkPart("1112131415161718", "0102"));
packet1.setIkAuth(new MasterKeyPacketIkPart(2, "2122232425262728", "1112"));
packet1.setIkDevice(new MasterKeyPacketIkPart(1, "3132333435363738", "2122"));
packet1.setUserKeys(List.of(
new MasterKeyPacketUserKeyPart(1, 0, "0102"),
new MasterKeyPacketUserKeyPart(1, 1, "1112")
));
MasterKeyBackupPacket packet2 = new MasterKeyBackupPacket();
packet2.setComponentIndex(2);
packet2.setLmk(new MasterKeyPacketLmkPart("1112131415161718", "0304"));
packet2.setIkAuth(new MasterKeyPacketIkPart(2, "2122232425262728", "1314"));
packet2.setIkDevice(new MasterKeyPacketIkPart(1, "3132333435363738", "2324"));
packet2.setUserKeys(List.of(
new MasterKeyPacketUserKeyPart(1, 0, "0304"),
new MasterKeyPacketUserKeyPart(1, 1, "1314")
));
MasterKeyBackupPacket packet3 = new MasterKeyBackupPacket();
packet3.setComponentIndex(3);
packet3.setLmk(new MasterKeyPacketLmkPart("1112131415161718", "0506"));
packet3.setIkAuth(new MasterKeyPacketIkPart(2, "2122232425262728", "1516"));
packet3.setIkDevice(new MasterKeyPacketIkPart(1, "3132333435363738", "2526"));
packet3.setUserKeys(List.of(
new MasterKeyPacketUserKeyPart(1, 0, "0506"),
new MasterKeyPacketUserKeyPart(1, 1, "1516")
));
service.recoverKeyPackets(List.of(packet1, packet2, packet3));
ArgumentCaptor<byte[]> oldPinCaptor = ArgumentCaptor.forClass(byte[].class);
ArgumentCaptor<byte[]> newPinCaptor = ArgumentCaptor.forClass(byte[].class);
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
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);
InOrder inOrder = Mockito.inOrder(pcieCryptoService);
inOrder.verify(pcieCryptoService).initIdentify(oldPinCaptor.capture(), newPinCaptor.capture());
inOrder.verify(pcieCryptoService).recoverLmkEx(captor.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());
Assertions.assertArrayEquals("12345678".getBytes(StandardCharsets.UTF_8), oldPinCaptor.getValue());
Assertions.assertArrayEquals("87654321".getBytes(StandardCharsets.UTF_8), newPinCaptor.getValue());
Assertions.assertArrayEquals(new byte[] {
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06
}, captor.getValue());
}, lmkCaptor.getValue());
Assertions.assertArrayEquals(hex("1112131415162122232425262728"), ikAuthCaptor.getValue());
Assertions.assertArrayEquals(hex("2122232425263132333435363738"), 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());
}
@Test
void shouldHashRawDataBeforeInternalSign() {
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
MasterKeyInitProperties properties = configuredPins("MTIzNDU2Nzg=", "ODc2NTQzMjE=");
BackupDataResult digestResult = new BackupDataResult();
digestResult.setLength(32);
digestResult.setData(new byte[] {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20
});
BackupDataResult signResult = new BackupDataResult();
signResult.setLength(2);
signResult.setData(new byte[] {(byte) 0xAA, (byte) 0xBB});
Mockito.when(pcieCryptoService.digest(Mockito.any())).thenReturn(digestResult);
Mockito.when(pcieCryptoService.eccInternalSign(Mockito.any())).thenReturn(signResult);
LmkService service = new LmkService(pcieCryptoService, properties);
String signatureHex = service.signIk("1223");
Assertions.assertEquals("aabb", signatureHex);
ArgumentCaptor<DigestRequest> digestCaptor = ArgumentCaptor.forClass(DigestRequest.class);
ArgumentCaptor<EccInternalSignRequest> signCaptor = ArgumentCaptor.forClass(EccInternalSignRequest.class);
InOrder inOrder = Mockito.inOrder(pcieCryptoService);
inOrder.verify(pcieCryptoService).digest(digestCaptor.capture());
inOrder.verify(pcieCryptoService).eccInternalSign(signCaptor.capture());
Assertions.assertEquals(Gm0018AlgorithmIds.SM3, digestCaptor.getValue().getAlgId());
Assertions.assertArrayEquals("1223".getBytes(StandardCharsets.UTF_8), digestCaptor.getValue().getData());
Assertions.assertArrayEquals(digestResult.getData(), signCaptor.getValue().getData());
}
@Test
void shouldHashRawDataBeforeInternalVerify() {
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
MasterKeyInitProperties properties = configuredPins("MTIzNDU2Nzg=", "ODc2NTQzMjE=");
BackupDataResult digestResult = new BackupDataResult();
digestResult.setLength(32);
digestResult.setData(new byte[] {
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40
});
Mockito.when(pcieCryptoService.digest(Mockito.any())).thenReturn(digestResult);
LmkService service = new LmkService(pcieCryptoService, properties);
service.verifyIk("1223", "aabb");
ArgumentCaptor<DigestRequest> digestCaptor = ArgumentCaptor.forClass(DigestRequest.class);
ArgumentCaptor<EccInternalVerifyRequest> verifyCaptor = ArgumentCaptor.forClass(EccInternalVerifyRequest.class);
InOrder inOrder = Mockito.inOrder(pcieCryptoService);
inOrder.verify(pcieCryptoService).digest(digestCaptor.capture());
inOrder.verify(pcieCryptoService).eccInternalVerify(verifyCaptor.capture());
Assertions.assertEquals(Gm0018AlgorithmIds.SM3, digestCaptor.getValue().getAlgId());
Assertions.assertArrayEquals("1223".getBytes(StandardCharsets.UTF_8), digestCaptor.getValue().getData());
Assertions.assertArrayEquals(digestResult.getData(), verifyCaptor.getValue().getData());
Assertions.assertArrayEquals(new byte[] {(byte) 0xAA, (byte) 0xBB}, verifyCaptor.getValue().getSignature());
}
private static MasterKeyInitProperties configuredPins(String oldPinBase64, String newPinBase64) {
@ -129,4 +270,16 @@ class LmkServiceTest {
properties.setNewPinBase64(newPinBase64);
return properties;
}
private static byte[] sequentialBytes(int length, int start) {
byte[] result = new byte[length];
for (int i = 0; i < length; i++) {
result[i] = (byte) (start + i);
}
return result;
}
private static byte[] hex(String value) {
return org.bouncycastle.util.encoders.Hex.decode(value);
}
}