fix:p10生成签名问题

This commit is contained in:
waner 2026-04-28 15:21:58 +08:00
parent bba2d5f259
commit cd647e8b00
12 changed files with 508 additions and 16 deletions

View File

@ -0,0 +1,32 @@
package com.cisd.tms.integration.crypto.pcie.model;
public class UserKeySm2SignRequest {
private int keyIndex;
private byte[] data;
private byte[] userId;
public int getKeyIndex() {
return keyIndex;
}
public void setKeyIndex(int keyIndex) {
this.keyIndex = keyIndex;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public byte[] getUserId() {
return userId;
}
public void setUserId(byte[] userId) {
this.userId = userId;
}
}

View File

@ -0,0 +1,41 @@
package com.cisd.tms.integration.crypto.pcie.model;
public class UserKeySm2VerifyRequest {
private int keyIndex;
private byte[] data;
private byte[] userId;
private byte[] signature;
public int getKeyIndex() {
return keyIndex;
}
public void setKeyIndex(int keyIndex) {
this.keyIndex = keyIndex;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public byte[] getUserId() {
return userId;
}
public void setUserId(byte[] userId) {
this.userId = userId;
}
public byte[] getSignature() {
return signature;
}
public void setSignature(byte[] signature) {
this.signature = signature;
}
}

View File

@ -81,6 +81,8 @@ import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult;
import com.cisd.tms.integration.crypto.pcie.model.UpdateBackedKeyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2SignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2VerifyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest;
import com.sun.jna.Memory;
@ -102,6 +104,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
private static final int HMAC_MAX_LENGTH = 64;
private static final int ECC_CIPHER_PREFIX_LENGTH = (EccRefPublicKey.MAX_LEN * 2) + 32 + Integer.BYTES;
private static final String DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD = "88888888";
private static final byte[] DEFAULT_SM2_USER_ID = "1234567812345678".getBytes(StandardCharsets.UTF_8);
private final PcieSessionTemplate sessionTemplate;
private final boolean strictAlgIdValidation;
@ -1697,6 +1700,29 @@ public class JnaPcieCryptoService implements PcieCryptoService {
eccInternalVerify(verifyRequest);
}
@Override
public BackupDataResult userKeySignWithSm2Sm3(UserKeySm2SignRequest request) {
UserKeySm2SignRequest req = requireRequest("request", request);
int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex());
byte[] digest = digestWithSm2Sm3(keyIndex, requireBytes("data", req.getData()), normalizeSm2UserId(req.getUserId()));
EccInternalSignRequest signRequest = new EccInternalSignRequest();
signRequest.setKeyIndex(keyIndex);
signRequest.setData(digest);
return withDefaultPrivateKeyAccess(keyIndex, () -> eccInternalSign(signRequest));
}
@Override
public void userKeyVerifyWithSm2Sm3(UserKeySm2VerifyRequest request) {
UserKeySm2VerifyRequest req = requireRequest("request", request);
int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex());
EccInternalVerifyRequest verifyRequest = new EccInternalVerifyRequest();
verifyRequest.setKeyIndex(keyIndex);
verifyRequest.setData(digestWithSm2Sm3(keyIndex, requireBytes("data", req.getData()), normalizeSm2UserId(req.getUserId())));
verifyRequest.setSignature(requireNonEmptyBytes("signature", req.getSignature()));
eccInternalVerify(verifyRequest);
}
@Override
public HmacResult hmac(HmacRequest request) {
HmacRequest req = requireRequest("request", request);
@ -2203,4 +2229,29 @@ public class JnaPcieCryptoService implements PcieCryptoService {
digestRequest.setOutBufferSize(32);
return requireNonEmptyBytes("sm3Digest", digest(digestRequest).getData());
}
private byte[] digestWithSm2Sm3(int keyIndex, byte[] data, byte[] userId) {
BackupDataResult publicKeyResult = exportSignPublicKeyEcc(keyIndex);
EccRefPublicKey publicKey = toEccRefPublicKey("publicKey", publicKeyResult.getData());
return sessionTemplate.withSession("SDF_HashFinal_SM2", (lib, deviceHandle, sessionHandle) -> {
sessionTemplate.ensureSuccess(
"SDF_HashInit",
lib.SDF_HashInit(sessionHandle, Gm0018AlgorithmIds.SM3, publicKey, userId, userId.length)
);
if (data.length > 0) {
sessionTemplate.ensureSuccess("SDF_HashUpdate", lib.SDF_HashUpdate(sessionHandle, data, data.length));
}
byte[] out = new byte[32];
IntByReference outLen = new IntByReference(out.length);
sessionTemplate.ensureSuccess("SDF_HashFinal", lib.SDF_HashFinal(sessionHandle, out, outLen));
return requireNonEmptyBytes("sm2Sm3Digest", toBackupResult("SDF_HashFinal_SM2", out, outLen.getValue()).getData());
});
}
private static byte[] normalizeSm2UserId(byte[] userId) {
if (userId == null || userId.length == 0) {
return Arrays.copyOf(DEFAULT_SM2_USER_ID, DEFAULT_SM2_USER_ID.length);
}
return Arrays.copyOf(userId, userId.length);
}
}

View File

@ -69,6 +69,8 @@ import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult;
import com.cisd.tms.integration.crypto.pcie.model.UpdateBackedKeyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2SignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2VerifyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest;
import java.nio.charset.StandardCharsets;
@ -748,6 +750,24 @@ public class MockPcieCryptoService implements PcieCryptoService {
eccInternalVerify(verifyRequest);
}
@Override
public BackupDataResult userKeySignWithSm2Sm3(UserKeySm2SignRequest request) {
UserKeySm2SignRequest req = requireRequest("request", request);
requireNonNegative("keyIndex", req.getKeyIndex());
requireBytes("data", req.getData());
requireBytes("userId", req.getUserId());
return mockBackup(new EccSignature().size(), 0x4A);
}
@Override
public void userKeyVerifyWithSm2Sm3(UserKeySm2VerifyRequest request) {
UserKeySm2VerifyRequest req = requireRequest("request", request);
requireNonNegative("keyIndex", req.getKeyIndex());
requireBytes("data", req.getData());
requireBytes("userId", req.getUserId());
requireNonEmptyBytes("signature", req.getSignature());
}
@Override
public HmacResult hmac(HmacRequest request) {
HmacRequest req = requireRequest("request", request);

View File

@ -59,6 +59,8 @@ import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult;
import com.cisd.tms.integration.crypto.pcie.model.UpdateBackedKeyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2SignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2VerifyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest;
@ -479,6 +481,16 @@ public interface PcieCryptoService {
*/
void userKeyVerifyWithSm3(UserKeyVerifyRequest request);
/**
* 按标准 SM2-with-SM3 流程计算 ZA || 原文摘要再使用用户签名密钥完成内部 ECC 签名
*/
BackupDataResult userKeySignWithSm2Sm3(UserKeySm2SignRequest request);
/**
* 按标准 SM2-with-SM3 流程计算 ZA || 原文摘要再使用用户签名公钥完成内部 ECC 验签
*/
void userKeyVerifyWithSm2Sm3(UserKeySm2VerifyRequest request);
/**
* 使用 SD FE HMAC 流程计算 HMAC
*/

View File

@ -1,14 +1,15 @@
package com.cisd.tms.modules.cert.dto;
import lombok.Data;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@Data
public class Subject {
private static final String COMMA = ",";
private String commonName;
private String country;
private String province;
@ -17,28 +18,32 @@ public class Subject {
private String orgUnit;
public String getDN() {
return toX500Name().toString();
}
public X500Name toX500Name() {
Assert.hasText(commonName, "commonName must not be blank");
StringBuilder builder = new StringBuilder();
X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
if (StringUtils.hasText(commonName)) {
builder.append("CN=").append(commonName).append(COMMA);
builder.addRDN(BCStyle.CN, commonName);
}
if (StringUtils.hasText(orgUnit)) {
builder.append("OU=").append(orgUnit).append(COMMA);
builder.addRDN(BCStyle.OU, orgUnit);
}
if (StringUtils.hasText(org)) {
builder.append("O=").append(org).append(COMMA);
builder.addRDN(BCStyle.O, org);
}
if (StringUtils.hasText(city)) {
builder.append("L=").append(city).append(COMMA);
builder.addRDN(BCStyle.L, city);
}
if (StringUtils.hasText(province)) {
builder.append("ST=").append(province).append(COMMA);
builder.addRDN(BCStyle.ST, province);
}
if (StringUtils.hasText(country)) {
builder.append("C=").append(country).append(COMMA);
Assert.isTrue(country.length() == 2, "country must be a 2-letter country code");
builder.addRDN(BCStyle.C, country);
}
builder.deleteCharAt(builder.length() - 1);
return builder.toString();
return builder.build();
}
public static Subject fromDN(String dn) {

View File

@ -6,7 +6,7 @@ import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.integration.crypto.pcie.PcieUserKeyType;
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
import com.cisd.tms.integration.crypto.pcie.model.GenerateKeyPairRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2SignRequest;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.cert.dto.EntityCreateRequest;
import com.cisd.tms.modules.cert.dto.EntityCreateResponse;
@ -44,6 +44,7 @@ import java.util.List;
public class EntityService {
private static final String SM2 = "SM2";
private static final byte[] DEFAULT_SM2_USER_ID = "1234567812345678".getBytes(StandardCharsets.UTF_8);
private final KeyEntityRepository keyEntityRepository;
private final CertificateRepository certificateRepository;
@ -132,10 +133,11 @@ public class EntityService {
public String generateP10(EntityGenP10Request request) {
KeyEntity entity = getById(request.getId());
Subject subject = request.getSubject();
String subjectDn = subject.getDN();
X500Name subjectName = subject.toX500Name();
String subjectDn = subjectName.toString();
PublicKey publicKey = Sm2KeySupport.toSm2PublicKey(Base64.getDecoder().decode(entity.getPublicKey()));
PKCS10CertificationRequestBuilder builder =
new JcaPKCS10CertificationRequestBuilder(new X500Name(subjectDn), publicKey);
new JcaPKCS10CertificationRequestBuilder(subjectName, publicKey);
PKCS10CertificationRequest csr = builder.build(new CardContentSigner(entity.getKeyIdx(), pcieCryptoService));
String pem = encodePkcs10Pem(csr);
entity.setSubjectDn(subjectDn);
@ -229,10 +231,11 @@ public class EntityService {
@Override
public byte[] getSignature() {
UserKeySignRequest request = new UserKeySignRequest();
UserKeySm2SignRequest request = new UserKeySm2SignRequest();
request.setKeyIndex(keyIdx);
request.setData(output.toByteArray());
return Sm2KeySupport.rawSignatureToDer(pcieCryptoService.userKeySignWithSm3(request).getData());
request.setUserId(DEFAULT_SM2_USER_ID);
return Sm2KeySupport.rawSignatureToDer(pcieCryptoService.userKeySignWithSm2Sm3(request).getData());
}
}
}

View File

@ -41,6 +41,8 @@ import com.cisd.tms.integration.crypto.pcie.model.SymmetricKekCryptoRequest;
import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2SignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2VerifyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest;
import com.cisd.tms.integration.crypto.pcie.service.PcieDeviceStatusFormatter;
@ -78,6 +80,7 @@ import org.springframework.web.bind.annotation.RestController;
public class CryptoCardController {
private static final String DEFAULT_USER_KEY_ACCESS_PASSWORD = "88888888";
private static final byte[] DEFAULT_SM2_USER_ID = "1234567812345678".getBytes(StandardCharsets.UTF_8);
private final PcieCryptoService pcieCryptoService;
@ -598,6 +601,44 @@ public class CryptoCardController {
return ApiResponse.success(Map.of("passed", true));
}
@PostMapping("/user-key/sm2-sign")
@Operation(summary = "用户密钥 SM2-with-SM3 签名", description = "使用用户签名密钥按标准 SM2 ZA + SM3 流程完成内部 ECC 签名。")
public ApiResponse<Map<String, Object>> signUserKeyWithSm2Sm3(
@Valid @RequestBody CryptoUserKeySm2SignInternalRequest request
) {
UserKeySm2SignRequest signRequest = new UserKeySm2SignRequest();
signRequest.setKeyIndex(request.getKeyIndex());
signRequest.setData(Base64.getDecoder().decode(request.getDataBase64()));
signRequest.setUserId(request.getUserIdBase64() == null || request.getUserIdBase64().isBlank()
? DEFAULT_SM2_USER_ID
: Base64.getDecoder().decode(request.getUserIdBase64()));
BackupDataResult result = pcieCryptoService.userKeySignWithSm2Sm3(signRequest);
int length = result.getLength() > 0 && result.getLength() <= result.getData().length
? result.getLength()
: result.getData().length;
byte[] signature = Arrays.copyOf(result.getData(), length);
return ApiResponse.success(Map.of(
"length", length,
"signatureBase64", Base64.getEncoder().encodeToString(signature)
));
}
@PostMapping("/user-key/sm2-verify")
@Operation(summary = "用户密钥 SM2-with-SM3 验签", description = "使用用户签名公钥按标准 SM2 ZA + SM3 流程完成内部 ECC 验签。")
public ApiResponse<Map<String, Object>> verifyUserKeyWithSm2Sm3(
@Valid @RequestBody CryptoUserKeySm2VerifyInternalRequest request
) {
UserKeySm2VerifyRequest verifyRequest = new UserKeySm2VerifyRequest();
verifyRequest.setKeyIndex(request.getKeyIndex());
verifyRequest.setData(Base64.getDecoder().decode(request.getDataBase64()));
verifyRequest.setUserId(request.getUserIdBase64() == null || request.getUserIdBase64().isBlank()
? DEFAULT_SM2_USER_ID
: Base64.getDecoder().decode(request.getUserIdBase64()));
verifyRequest.setSignature(Base64.getDecoder().decode(request.getSignatureBase64()));
pcieCryptoService.userKeyVerifyWithSm2Sm3(verifyRequest);
return ApiResponse.success(Map.of("passed", true));
}
@PostMapping("/user-key/self-test")
@Operation(summary = "执行用户密钥全流程自测", description = "依次执行 ECC 用户密钥生成、备份、删除、重建槽位、恢复和状态校验,返回备份 HEX 与关键状态。")
public ApiResponse<Map<String, Object>> selfTestUserKey(

View File

@ -0,0 +1,39 @@
package com.cisd.tms.modules.device.dto.crypto.request;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
public class CryptoUserKeySm2SignInternalRequest {
@Min(0)
private int keyIndex;
@NotBlank
private String dataBase64;
private String userIdBase64;
public int getKeyIndex() {
return keyIndex;
}
public void setKeyIndex(int keyIndex) {
this.keyIndex = keyIndex;
}
public String getDataBase64() {
return dataBase64;
}
public void setDataBase64(String dataBase64) {
this.dataBase64 = dataBase64;
}
public String getUserIdBase64() {
return userIdBase64;
}
public void setUserIdBase64(String userIdBase64) {
this.userIdBase64 = userIdBase64;
}
}

View File

@ -0,0 +1,50 @@
package com.cisd.tms.modules.device.dto.crypto.request;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
public class CryptoUserKeySm2VerifyInternalRequest {
@Min(0)
private int keyIndex;
@NotBlank
private String dataBase64;
private String userIdBase64;
@NotBlank
private String signatureBase64;
public int getKeyIndex() {
return keyIndex;
}
public void setKeyIndex(int keyIndex) {
this.keyIndex = keyIndex;
}
public String getDataBase64() {
return dataBase64;
}
public void setDataBase64(String dataBase64) {
this.dataBase64 = dataBase64;
}
public String getUserIdBase64() {
return userIdBase64;
}
public void setUserIdBase64(String userIdBase64) {
this.userIdBase64 = userIdBase64;
}
public String getSignatureBase64() {
return signatureBase64;
}
public void setSignatureBase64(String signatureBase64) {
this.signatureBase64 = signatureBase64;
}
}

View File

@ -0,0 +1,136 @@
package com.cisd.tms.modules.cert.service;
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.UserKeySignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2SignRequest;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
import com.cisd.tms.modules.cert.dto.EntityGenP10Request;
import com.cisd.tms.modules.cert.dto.Subject;
import com.cisd.tms.modules.cert.entity.KeyEntity;
import com.cisd.tms.modules.cert.repository.CertificateRepository;
import com.cisd.tms.modules.cert.repository.KeyEntityRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.io.StringReader;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
class EntityServiceTest {
@Test
void shouldGenerateP10WithStandardSm2Sm3Signer() 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());
service.generateP10(genP10Request("TMS-001"));
ArgumentCaptor<UserKeySm2SignRequest> sm2Captor = ArgumentCaptor.forClass(UserKeySm2SignRequest.class);
Mockito.verify(pcieCryptoService).userKeySignWithSm2Sm3(sm2Captor.capture());
Mockito.verify(pcieCryptoService, Mockito.never()).userKeySignWithSm3(any(UserKeySignRequest.class));
assertEquals(5, sm2Captor.getValue().getKeyIndex());
assertEquals("1234567812345678", new String(sm2Captor.getValue().getUserId()));
}
@Test
void shouldPreserveSpecialCharactersInP10Subject() 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());
String pem = service.generateP10(genP10Request("TMS,Entity+001"));
PKCS10CertificationRequest csr = (PKCS10CertificationRequest) new PEMParser(new StringReader(pem)).readObject();
String commonName = csr.getSubject().getRDNs(BCStyle.CN)[0].getFirst().getValue().toString();
assertEquals("TMS,Entity+001", commonName);
}
private EntityGenP10Request genP10Request(String commonName) {
Subject subject = new Subject();
subject.setCommonName(commonName);
subject.setOrgUnit("Cert");
subject.setOrg("CISD");
subject.setCity("南南市");
subject.setProvince("陕西省");
subject.setCountry("CN");
EntityGenP10Request request = new EntityGenP10Request();
request.setId(10L);
request.setSubject(subject);
return request;
}
private KeyEntity keyEntity(Long id, int keyIdx) throws Exception {
KeyEntity entity = new KeyEntity();
entity.setId(id);
entity.setKeyIdx(keyIdx);
entity.setPublicKey(Base64.getEncoder().encodeToString(gm0018EccRefPublicKey(generateSm2PublicKey())));
return entity;
}
private BackupDataResult signatureResult() {
EccSignature signature = new EccSignature();
signature.r[EccRefPublicKey.MAX_LEN - 1] = 0x01;
signature.s[EccRefPublicKey.MAX_LEN - 1] = 0x02;
signature.write();
BackupDataResult result = new BackupDataResult();
result.setLength(signature.size());
result.setData(signature.getPointer().getByteArray(0, signature.size()));
return result;
}
private ECPublicKey generateSm2PublicKey() throws Exception {
if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
}
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
keyPairGenerator.initialize(new ECGenParameterSpec("sm2p256v1"));
return (ECPublicKey) keyPairGenerator.generateKeyPair().getPublic();
}
private byte[] gm0018EccRefPublicKey(ECPublicKey publicKey) {
byte[] x = toFixed32(publicKey.getW().getAffineX().toByteArray());
byte[] y = toFixed32(publicKey.getW().getAffineY().toByteArray());
byte[] result = new byte[new EccRefPublicKey().size()];
result[0] = 0x00;
result[1] = 0x01;
result[2] = 0x00;
result[3] = 0x00;
System.arraycopy(x, 0, result, 4 + 32, 32);
System.arraycopy(y, 0, result, 4 + 64 + 32, 32);
return result;
}
private byte[] toFixed32(byte[] source) {
byte[] result = new byte[32];
int copyLen = Math.min(source.length, 32);
System.arraycopy(source, source.length - copyLen, result, 32 - copyLen, copyLen);
return result;
}
}

View File

@ -15,6 +15,8 @@ import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest;
import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2SignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2VerifyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest;
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
@ -393,6 +395,66 @@ class CryptoCardControllerTest {
assertArrayEquals(new byte[] {0x11, 0x22, 0x33, 0x44}, captor.getValue().getSignature());
}
@Test
void shouldSignUserKeyWithStandardSm2Sm3Endpoint() throws Exception {
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
BackupDataResult result = new BackupDataResult();
result.setLength(4);
result.setData(new byte[] {0x11, 0x22, 0x33, 0x44});
Mockito.when(pcieCryptoService.userKeySignWithSm2Sm3(Mockito.any())).thenReturn(result);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService))
.setControllerAdvice(new GlobalExceptionHandler())
.build();
mockMvc.perform(post("/api/v1/device/crypto/user-key/sm2-sign")
.contentType("application/json")
.content("""
{
"keyIndex": 7,
"dataBase64": "Y3Ny",
"userIdBase64": "MTIzNDU2NzgxMjM0NTY3OA=="
}
"""))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"length\":4")))
.andExpect(content().string(containsString("\"signatureBase64\":\"ESIzRA==\"")));
ArgumentCaptor<UserKeySm2SignRequest> captor = ArgumentCaptor.forClass(UserKeySm2SignRequest.class);
Mockito.verify(pcieCryptoService).userKeySignWithSm2Sm3(captor.capture());
assertEquals(7, captor.getValue().getKeyIndex());
assertArrayEquals("csr".getBytes(java.nio.charset.StandardCharsets.UTF_8), captor.getValue().getData());
assertArrayEquals("1234567812345678".getBytes(java.nio.charset.StandardCharsets.UTF_8), captor.getValue().getUserId());
}
@Test
void shouldVerifyUserKeyWithStandardSm2Sm3Endpoint() throws Exception {
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService))
.setControllerAdvice(new GlobalExceptionHandler())
.build();
mockMvc.perform(post("/api/v1/device/crypto/user-key/sm2-verify")
.contentType("application/json")
.content("""
{
"keyIndex": 7,
"dataBase64": "Y3Ny",
"signatureBase64": "ESIzRA=="
}
"""))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"passed\":true")));
ArgumentCaptor<UserKeySm2VerifyRequest> captor = ArgumentCaptor.forClass(UserKeySm2VerifyRequest.class);
Mockito.verify(pcieCryptoService).userKeyVerifyWithSm2Sm3(captor.capture());
assertEquals(7, captor.getValue().getKeyIndex());
assertArrayEquals("csr".getBytes(java.nio.charset.StandardCharsets.UTF_8), captor.getValue().getData());
assertArrayEquals("1234567812345678".getBytes(java.nio.charset.StandardCharsets.UTF_8), captor.getValue().getUserId());
assertArrayEquals(new byte[] {0x11, 0x22, 0x33, 0x44}, captor.getValue().getSignature());
}
@Test
void shouldRunUserKeyCryptoSelfTestFlow() throws Exception {
PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class);