diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/PcieNativeLibrary.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/PcieNativeLibrary.java index 8ac0635..a2fd7da 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/PcieNativeLibrary.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/PcieNativeLibrary.java @@ -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); diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/SydIkComponent.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/SydIkComponent.java new file mode 100644 index 0000000..0adb6df --- /dev/null +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/SydIkComponent.java @@ -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 getFieldOrder() { + return List.of("component", "mac"); + } +} diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java index 5e2c40d..0baf0a4 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java @@ -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:[1,8] 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); } diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java index 9365c64..950a8ad 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java @@ -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); diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java index 66da9e9..44365c0 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java @@ -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); diff --git a/src/main/java/com/cisd/tms/modules/init/controller/InitController.java b/src/main/java/com/cisd/tms/modules/init/controller/InitController.java index 3028f89..c3daaa2 100644 --- a/src/main/java/com/cisd/tms/modules/init/controller/InitController.java +++ b/src/main/java/com/cisd/tms/modules/init/controller/InitController.java @@ -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; diff --git a/src/main/java/com/cisd/tms/modules/init/service/InitService.java b/src/main/java/com/cisd/tms/modules/init/service/InitService.java index 6e98cd7..78d3a6e 100644 --- a/src/main/java/com/cisd/tms/modules/init/service/InitService.java +++ b/src/main/java/com/cisd/tms/modules/init/service/InitService.java @@ -162,6 +162,7 @@ public class InitService { } String productType = resolvePresetProductType(); + //todo 验证是否已经初始化 validateCommon(request); validateByProductType(productType, request); diff --git a/src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java b/src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java index f8613a0..67c5ee5 100644 --- a/src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java +++ b/src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java @@ -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=1、2、3 分别调用三次: + * uid=1 返回第 1 份分量包,uid=2 返回第 2 份分量包,uid=3 返回第 3 份分量包。 + * 每份分量包中同时包含 LMK、IK_AUTH、IK_DEVICE、USER_SIGN、USER_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 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 recoverLmkEx(@Valid @RequestBody MasterKeyRecoverRequest request) { - lmkService.recoverLmkEx(request.getLmkMac(), request.getComponents()); + @Operation(summary = "恢复全部密钥", description = "请求体需传入三份按 componentIndex 标识的分量包。三份分量包必须分别对应 componentIndex=1、2、3。服务端会依次恢复 LMK、IK 和用户密钥。") + public ApiResponse recoverLmkEx(@Valid @RequestBody MasterKeyRecoverPacketsRequest request) { + lmkService.recoverKeyPackets(request.getPackets()); return ApiResponse.success(); } + @GetMapping("/masterKey/sign") + @Operation(summary = "签名测试", description = "") + public ApiResponse 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; diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyBackupPacket.java b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyBackupPacket.java new file mode 100644 index 0000000..12e2bd5 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyBackupPacket.java @@ -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 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 getUserKeys() { + return userKeys; + } + + public void setUserKeys(List userKeys) { + this.userKeys = userKeys; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketIkPart.java b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketIkPart.java new file mode 100644 index 0000000..bf6002c --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketIkPart.java @@ -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; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketLmkPart.java b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketLmkPart.java new file mode 100644 index 0000000..a9c8ad0 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketLmkPart.java @@ -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; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketUserKeyPart.java b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketUserKeyPart.java new file mode 100644 index 0000000..721661d --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyPacketUserKeyPart.java @@ -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; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyRecoverPacketsRequest.java b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyRecoverPacketsRequest.java new file mode 100644 index 0000000..649a24e --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyRecoverPacketsRequest.java @@ -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 getPackets() { + return packets; + } + + public void setPackets(List packets) { + this.packets = packets; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignResult.java b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignResult.java index b49fa47..1c17fe8 100644 --- a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignResult.java +++ b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignResult.java @@ -20,4 +20,6 @@ public class UKeySignResult { private String component; private String lmkMac; + + private MasterKeyBackupPacket backupPacket; } diff --git a/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java b/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java index c1a4dbf..e16ee8d 100644 --- a/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java +++ b/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java @@ -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 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; + } + public MasterKeyBackupMaterial getBackupMaterial(int uid) { - ParsedMasterKeyBackup parsedBackup = parseBackupMaterial(); - String component = parsedBackup.componentMap().get(uid); - if (component == null) { - throw new IllegalArgumentException("invalid component index: " + uid); - } - return new MasterKeyBackupMaterial(component, parsedBackup.lmkMacHex()); + 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 packets) { + List 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 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 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 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 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 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 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 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 sortPackets(List packets) { + if (packets == null || packets.size() != LMKConstant.COMPONENT_NUM) { + throw new IllegalArgumentException("packets size must be " + LMKConstant.COMPONENT_NUM); + } + Map 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 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 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 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 componentMap) { } + private record ParsedIkBackup(String macHex, Map componentMap) { + } + } diff --git a/src/test/java/com/cisd/tms/modules/mk/controller/LmkControllerTest.java b/src/test/java/com/cisd/tms/modules/mk/controller/LmkControllerTest.java index 5ae9cc8..521b947 100644 --- a/src/test/java/com/cisd/tms/modules/mk/controller/LmkControllerTest.java +++ b/src/test/java/com/cisd/tms/modules/mk/controller/LmkControllerTest.java @@ -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(""" { - "lmkMac": "a1a2a3a4a5a6a7a8", - "components": [ - "0102", - "0304", - "0506" + "packets": [ + { + "componentIndex": 1, + "lmk": { + "lmkMac": "a1a2a3a4a5a6a7a8", + "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 diff --git a/src/test/java/com/cisd/tms/modules/mk/service/LmkServiceTest.java b/src/test/java/com/cisd/tms/modules/mk/service/LmkServiceTest.java index bddbe5b..3ba0034 100644 --- a/src/test/java/com/cisd/tms/modules/mk/service/LmkServiceTest.java +++ b/src/test/java/com/cisd/tms/modules/mk/service/LmkServiceTest.java @@ -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 oldPinCaptor = ArgumentCaptor.forClass(byte[].class); ArgumentCaptor newPinCaptor = ArgumentCaptor.forClass(byte[].class); - ArgumentCaptor captor = ArgumentCaptor.forClass(byte[].class); + ArgumentCaptor lmkCaptor = ArgumentCaptor.forClass(byte[].class); + ArgumentCaptor ikAuthCaptor = ArgumentCaptor.forClass(byte[].class); + ArgumentCaptor ikDeviceCaptor = ArgumentCaptor.forClass(byte[].class); + ArgumentCaptor 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 digestCaptor = ArgumentCaptor.forClass(DigestRequest.class); + ArgumentCaptor 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 digestCaptor = ArgumentCaptor.forClass(DigestRequest.class); + ArgumentCaptor 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); + } }