From 42b4ed1c3884a8cfe3fff2638cee4d4c5194872d Mon Sep 17 00:00:00 2001 From: waner Date: Thu, 7 May 2026 15:04:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:p10=E5=A2=9E=E5=8A=A0ou?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cisd/tms/modules/cert/dto/Subject.java | 33 +++++++++++++++++-- .../modules/cert/service/EntityService.java | 1 + .../cert/service/EntityServiceTest.java | 22 +++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/cisd/tms/modules/cert/dto/Subject.java b/src/main/java/com/cisd/tms/modules/cert/dto/Subject.java index 628ae10..5a45405 100644 --- a/src/main/java/com/cisd/tms/modules/cert/dto/Subject.java +++ b/src/main/java/com/cisd/tms/modules/cert/dto/Subject.java @@ -8,6 +8,9 @@ import org.bouncycastle.asn1.x500.style.BCStyle; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import java.util.ArrayList; +import java.util.List; + @Data @Schema(description = "证书主题信息") public class Subject { @@ -30,6 +33,9 @@ public class Subject { @Schema(description = "组织单元 OU", example = "Cert") private String orgUnit; + @Schema(description = "组织单元 OU 列表,最多支持两个;传入后优先于 orgUnit", example = "[\"Organizational-1\",\"CCFCCB\"]") + private List orgUnits; + public String getDN() { return toX500Name().toString(); } @@ -40,8 +46,8 @@ public class Subject { if (StringUtils.hasText(commonName)) { builder.addRDN(BCStyle.CN, commonName); } - if (StringUtils.hasText(orgUnit)) { - builder.addRDN(BCStyle.OU, orgUnit); + for (String unit : effectiveOrgUnits()) { + builder.addRDN(BCStyle.OU, unit); } if (StringUtils.hasText(org)) { builder.addRDN(BCStyle.O, org); @@ -59,9 +65,26 @@ public class Subject { return builder.build(); } + private List effectiveOrgUnits() { + List units = new ArrayList<>(); + if (orgUnits != null) { + for (String unit : orgUnits) { + if (StringUtils.hasText(unit)) { + units.add(unit.trim()); + } + } + } + if (units.isEmpty() && StringUtils.hasText(orgUnit)) { + units.add(orgUnit.trim()); + } + Assert.isTrue(units.size() <= 2, "组织单元最多支持2个"); + return units; + } + public static Subject fromDN(String dn) { Assert.hasText(dn, "DN不能为空"); Subject subject = new Subject(); + List orgUnits = new ArrayList<>(); String[] splits = dn.trim().split(","); for (String split : splits) { String trim = split.trim(); @@ -78,12 +101,16 @@ public class Subject { subject.setOrg(trim.substring(2)); } if (trim.startsWith("OU=")) { - subject.setOrgUnit(trim.substring(3)); + orgUnits.add(trim.substring(3)); } if (trim.startsWith("CN=")) { subject.setCommonName(trim.substring(3)); } } + if (!orgUnits.isEmpty()) { + subject.setOrgUnit(orgUnits.get(0)); + subject.setOrgUnits(orgUnits); + } Assert.hasText(subject.commonName, "通用名称不能为空"); return subject; } diff --git a/src/main/java/com/cisd/tms/modules/cert/service/EntityService.java b/src/main/java/com/cisd/tms/modules/cert/service/EntityService.java index fcc07a1..62daed4 100644 --- a/src/main/java/com/cisd/tms/modules/cert/service/EntityService.java +++ b/src/main/java/com/cisd/tms/modules/cert/service/EntityService.java @@ -109,6 +109,7 @@ public class EntityService { .setOrgName(request.getOrgName()) .setAlgoType(algoType) .setKeyLen(keyLen) + //todo 后续增加密钥类型字段/增加加密密钥对字段 .setKeyIdx(keyIdx) .setBackupData(Base64.getEncoder().encodeToString(backup.getData())) .setPublicKey(Base64.getEncoder().encodeToString(publicKey.getData())) diff --git a/src/test/java/com/cisd/tms/modules/cert/service/EntityServiceTest.java b/src/test/java/com/cisd/tms/modules/cert/service/EntityServiceTest.java index 808178c..ffe1617 100644 --- a/src/test/java/com/cisd/tms/modules/cert/service/EntityServiceTest.java +++ b/src/test/java/com/cisd/tms/modules/cert/service/EntityServiceTest.java @@ -278,6 +278,28 @@ class EntityServiceTest { assertEquals("TMS,Entity+001", commonName); } + @Test + void shouldGenerateP10WithTwoOrganizationalUnits() throws Exception { + KeyEntityRepository keyEntityRepository = Mockito.mock(KeyEntityRepository.class); + CertificateRepository certificateRepository = Mockito.mock(CertificateRepository.class); + PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class); + EntityService service = new EntityService(keyEntityRepository, certificateRepository, pcieCryptoService, new ObjectMapper()); + Mockito.when(keyEntityRepository.findById(10L)).thenReturn(Optional.of(keyEntity(10L, 5))); + Mockito.when(keyEntityRepository.save(any())).thenAnswer(invocation -> invocation.getArgument(0)); + Mockito.when(pcieCryptoService.userKeySignWithSm3(any())).thenReturn(signatureResult()); + Mockito.when(pcieCryptoService.userKeySignWithSm2Sm3(any())).thenReturn(signatureResult()); + + EntityGenP10Request request = genP10Request("TMS-001"); + request.getSubject().setOrgUnits(List.of("Organizational-1", "CCFCCB")); + + String pem = service.generateP10(request); + + PKCS10CertificationRequest csr = (PKCS10CertificationRequest) new PEMParser(new StringReader(pem)).readObject(); + assertEquals(2, csr.getSubject().getRDNs(BCStyle.OU).length); + assertEquals("Organizational-1", csr.getSubject().getRDNs(BCStyle.OU)[0].getFirst().getValue().toString()); + assertEquals("CCFCCB", csr.getSubject().getRDNs(BCStyle.OU)[1].getFirst().getValue().toString()); + } + private EntityGenP10Request genP10Request(String commonName) { Subject subject = new Subject(); subject.setCommonName(commonName);