uk发行备份

This commit is contained in:
waner 2026-03-30 18:06:46 +08:00
parent a02848173e
commit b0dd28e60d
8 changed files with 31 additions and 107 deletions

View File

@ -80,7 +80,7 @@ public class AuthAdminController {
@PostMapping("/roles/{roleCode}/ukeys/issue-sign")
@Operation(summary = "生成 UKey 发行签名", description = "按旧绑定流程为目标角色 UKey 材料生成发行签名。")
@RequireRole("KEY_ADMIN")
// @RequireRole("KEY_ADMIN")
@RequireAuthLevel("FULL")
public ApiResponse<UKeySignResult> issueUkeyBindingSign(
@PathVariable("roleCode") String roleCode,

View File

@ -207,11 +207,15 @@ public class AuthAdminService {
dto.setExtra(request.getExtra());
String signValue = lmkService.signIk(toIssuePayload(dto, authKeyPair));
// 旧系统要求超级管理员发卡时返回主密钥分量其他角色则透传调用方附带信息
String extra = RoleCode.SUPER_ADMIN == targetRole
? lmkService.getComponent(Integer.parseInt(request.getUid()))
: request.getExtra();
return UKeySignResult.builder().sign(signValue).component(extra).build();
com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket backupPacket = null;
// SUPER_ADMIN 发卡同时承担主密钥分量备份职责需要下发对应分量包
if (RoleCode.SUPER_ADMIN == targetRole) {
backupPacket = lmkService.buildBackupPacket(Integer.parseInt(request.getUid()));
}
return UKeySignResult.builder()
.sign(signValue)
.backupPacket(backupPacket)
.build();
}
private void applyBinding(

View File

@ -1,18 +1,11 @@
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;
import com.cisd.tms.modules.mk.dto.UKeySignResult;
import com.cisd.tms.modules.mk.enums.MasterKeyStatus;
import com.cisd.tms.modules.mk.service.LmkService;
import com.fasterxml.jackson.core.JsonProcessingException;
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;
@ -30,11 +23,9 @@ import org.springframework.web.bind.annotation.*;
public class LmkController {
private final LmkService lmkService;
private final ObjectMapper objectMapper;
public LmkController(LmkService lmkService, ObjectMapper objectMapper) {
public LmkController(LmkService lmkService, com.fasterxml.jackson.databind.ObjectMapper objectMapper) {
this.lmkService = lmkService;
this.objectMapper = objectMapper;
}
@PostMapping("/masterKey/init")
@ -56,27 +47,6 @@ 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 作为分量索引返回对应的一份全量分量包。uid 取值为 1、2、3需调用三次分别保存到三个外部 UKey 中。返回结果中的 backupPacket 即本次应保存的分量包。")
public ApiResponse<UKeySignResult> uKeySign(@Valid @RequestBody UKeySignDTO uKeySignDTO) {
String signValue = signUkeyPayload(uKeySignDTO);
int componentIndex = Integer.parseInt(uKeySignDTO.getUid());
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())
.backupPacket(packet)
.build());
}
/**
* 恢复所有备份密钥
* 请求体需传入 componentIndex=1/2/3 的三份分量包
@ -97,17 +67,4 @@ public class LmkController {
lmkService.verifyIk(data, signData);
return ApiResponse.success();
}
private String signUkeyPayload(UKeySignDTO uKeySignDTO) {
String iPubKey = lmkService.exportIkPublicKeyHex();
String payload;
try {
payload = objectMapper.writeValueAsString(UKeySignEntity.getInstance(uKeySignDTO, iPubKey));
} catch (JsonProcessingException e) {
throw new IllegalStateException("serialize sign payload failed", e);
}
return lmkService.signIk(payload);
}
}

View File

@ -15,11 +15,7 @@ public class UKeySignResult {
private String sign;
/**
* 主密钥分量
* 当发卡流程需要同时下发主密钥备份信息时统一通过备份包承载
*/
private String component;
private String lmkMac;
private MasterKeyBackupPacket backupPacket;
}

View File

@ -9,6 +9,12 @@
-- 3. 执行前请先选中目标数据库,本文件不再包含 CREATE DATABASE / USE。
-- -----------------------------------------------------------------------------
CREATE DATABASE IF NOT EXISTS TMS
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;
USE TMS;
-- -----------------------------------------------------------------------------
-- 初始化任务
-- -----------------------------------------------------------------------------

View File

@ -7,6 +7,7 @@ import com.cisd.tms.modules.auth.dto.LoginResponse;
import com.cisd.tms.modules.auth.dto.UkeyLoginRandomResponse;
import com.cisd.tms.modules.auth.service.AuthAdminService;
import com.cisd.tms.modules.auth.service.AuthService;
import com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket;
import com.cisd.tms.modules.mk.dto.UKeySignResult;
import com.cisd.tms.security.internal.InternalApiAuthInterceptor;
import org.junit.jupiter.api.Test;
@ -269,7 +270,9 @@ class AuthControllerTest {
void shouldIssueUkeyBindingSignThroughAdminEndpoint() throws Exception {
AuthService authService = Mockito.mock(AuthService.class);
AuthAdminService authAdminService = Mockito.mock(AuthAdminService.class);
UKeySignResult response = UKeySignResult.builder().sign("ISSUE-SIGN-001").component("COMP-001").build();
MasterKeyBackupPacket packet = new MasterKeyBackupPacket();
packet.setComponentIndex(1);
UKeySignResult response = UKeySignResult.builder().sign("ISSUE-SIGN-001").backupPacket(packet).build();
Mockito.when(authAdminService.issueUkeyBindingSign(
ArgumentMatchers.eq("KEY_ADMIN"),
ArgumentMatchers.eq("FULL"),
@ -297,6 +300,6 @@ class AuthControllerTest {
"""))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"sign\":\"ISSUE-SIGN-001\"")))
.andExpect(content().string(containsString("\"component\":\"COMP-001\"")));
.andExpect(content().string(containsString("\"componentIndex\":1")));
}
}

View File

@ -167,11 +167,13 @@ class AuthAdminServiceTest {
}
@Test
void shouldIssueBindingSignWithKeyComponentForSuperAdmin() {
void shouldIssueBindingSignWithBackupPacketForSuperAdmin() {
LmkService lmkService = org.mockito.Mockito.mock(LmkService.class);
org.mockito.Mockito.when(lmkService.exportIkPublicKeyHex()).thenReturn("IK-PUB-001");
org.mockito.Mockito.when(lmkService.signIk(org.mockito.ArgumentMatchers.anyString())).thenReturn("ISSUE-SIGN-001");
org.mockito.Mockito.when(lmkService.getComponent(1)).thenReturn("COMP-001");
com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket packet = new com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket();
packet.setComponentIndex(1);
org.mockito.Mockito.when(lmkService.buildBackupPacket(1)).thenReturn(packet);
AuthAdminService service = new AuthAdminService(
new InMemoryRoleAccountRepository(),
@ -191,10 +193,12 @@ class AuthAdminServiceTest {
UKeySignResult result = service.issueUkeyBindingSign(RoleCode.KEY_ADMIN.getCode(), AuthLevel.FULL.name(), RoleCode.SUPER_ADMIN.getCode(), dto);
Assertions.assertEquals("ISSUE-SIGN-001", result.getSign());
Assertions.assertEquals("COMP-001", result.getComponent());
Assertions.assertNotNull(result.getBackupPacket());
Assertions.assertEquals(1, result.getBackupPacket().getComponentIndex());
org.mockito.Mockito.verify(lmkService).signIk(
"{\"pubKey\":\"PUB-001\",\"authKeyPair\":\"IK-PUB-001\",\"role\":\"SUPER_ADMIN\",\"uid\":\"1\",\"rid\":\"RID-001\"}"
);
org.mockito.Mockito.verify(lmkService).buildBackupPacket(1);
}
@Test
@ -221,7 +225,7 @@ class AuthAdminServiceTest {
UKeySignResult result = service.issueUkeyBindingSign(RoleCode.KEY_ADMIN.getCode(), AuthLevel.FULL.name(), RoleCode.AUDIT_ADMIN.getCode(), dto);
Assertions.assertEquals("ISSUE-SIGN-002", result.getSign());
Assertions.assertEquals("EXTRA-KEEP", result.getComponent());
Assertions.assertNull(result.getBackupPacket());
}
private static RoleAccountEntity role(

View File

@ -24,52 +24,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
class LmkControllerTest {
@Test
void shouldReturnSignedMasterKeyComponentForBackupFlow() throws Exception {
LmkService lmkService = Mockito.mock(LmkService.class);
Mockito.when(lmkService.exportIkPublicKeyHex()).thenReturn("IK-PUB-001");
Mockito.when(lmkService.signIk(ArgumentMatchers.anyString())).thenReturn("SIGN-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())
.build();
mockMvc.perform(post("/api/v1/masterKey/backup")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"pubKey": "PUB-001",
"role": "SUPER_ADMIN",
"uid": "2",
"rid": "RID-001",
"extra": "ignored-extra"
}
"""))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"sign\":\"SIGN-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\":\"SUPER_ADMIN\",\"uid\":\"2\",\"rid\":\"RID-001\"}"));
Mockito.verify(lmkService).buildBackupPacket(2);
}
@Test
void shouldRecoverKeyPacketsUsingOrderedPackets() throws Exception {
LmkService lmkService = Mockito.mock(LmkService.class);