签名调试通过

This commit is contained in:
cheney 2026-05-12 16:02:56 +08:00
parent cee2a74770
commit 90ed811c1d
3 changed files with 179 additions and 26 deletions

View File

@ -1,36 +1,110 @@
package com.cisd.tms.modules.openapi.service.controller;
import com.cisd.tms.common.api.ApiResponse;
import com.cisd.tms.modules.auth.enums.AuthLevel;
import com.cisd.tms.modules.auth.enums.RoleCode;
import com.cisd.tms.modules.auth.security.RequireInternalAuth;
import com.cisd.tms.modules.cert.dto.CrlImportTaskDetailRequest;
import com.cisd.tms.modules.cert.dto.CrlImportTaskResponse;
import com.cisd.tms.modules.openapi.service.dto.RawSignRequest;
import com.cisd.tms.modules.openapi.service.dto.RawVerifyRequest;
import com.cisd.tms.modules.openapi.service.dto.RawVerifyResponse;
import com.cisd.tms.modules.openapi.service.impl.OpenApiService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 java.util.Base64;
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1/openapi")
@Tag(name = "OpenApi 管理", description = "对外开放接口")
public class OpenApiController {
private OpenApiService openApiService;
private static final Logger log = LoggerFactory.getLogger(OpenApiController.class);
private final OpenApiService openApiService;
@PostMapping("/rawSign")
@Operation(summary = "裸签名", description = "基于 SM3 SM2 算法编制罗签名")
@Operation(summary = "裸签名", description = "基于 SM3 SM2 算法进行签名,返回 Base64 编码的签名值")
public ApiResponse<String> rawSign(@Valid @RequestBody RawSignRequest request) {
log.info("收到裸签名请求, DN: {}", request.getDn());
String signature = openApiService.rawSign();
return ApiResponse.success(signature);
// 入参检查
if (request.getOrigBytes() == null || request.getOrigBytes().trim().isEmpty()) {
log.error("签名请求失败:待签名数据为空");
return ApiResponse.fail(400, "待签名数据不能为空", null);
}
if (request.getDn() == null || request.getDn().trim().isEmpty()) {
log.error("签名请求失败证书DN为空");
return ApiResponse.fail(400, "证书DN不能为空", null);
}
try {
// Base64 解码原始数据
byte[] origBytes = Base64.getDecoder().decode(request.getOrigBytes());
log.info("待签名数据长度: {} bytes", origBytes.length);
// 调用签名服务
String signature = openApiService.rawSign(origBytes, request.getDn());
log.info("签名成功");
return ApiResponse.success(signature);
} catch (IllegalArgumentException e) {
log.error("签名请求参数错误: {}", e.getMessage());
return ApiResponse.fail(400, e.getMessage(), null);
} catch (Exception e) {
log.error("签名失败: {}", e.getMessage(), e);
return ApiResponse.fail(500, "签名失败: " + e.getMessage(), null);
}
}
@PostMapping("/rawVerify")
@Operation(summary = "裸验签", description = "基于 SM3 SM2 算法进行验签,验证签名的有效性")
public ApiResponse<RawVerifyResponse> rawVerify(@Valid @RequestBody RawVerifyRequest request) {
log.info("收到裸验签请求, DN: {}", request.getDn());
// 入参检查
if (request.getOrigBytes() == null || request.getOrigBytes().trim().isEmpty()) {
log.error("验签请求失败:待验签数据为空");
return ApiResponse.fail(400, "待验签数据不能为空", null);
}
if (request.getSignature() == null || request.getSignature().trim().isEmpty()) {
log.error("验签请求失败:签名值为空");
return ApiResponse.fail(400, "签名值不能为空", null);
}
if (request.getDn() == null || request.getDn().trim().isEmpty()) {
log.error("验签请求失败证书DN为空");
return ApiResponse.fail(400, "证书DN不能为空", null);
}
try {
// Base64 解码原始数据
byte[] origBytes = Base64.getDecoder().decode(request.getOrigBytes());
log.info("待验签数据长度: {} bytes", origBytes.length);
log.info("签名数据长度: {} bytes", request.getSignature().length());
// 调用验签服务
boolean verified = openApiService.rawVerify(origBytes, request.getSignature(), request.getDn());
if (verified) {
log.info("验签成功");
return ApiResponse.success(RawVerifyResponse.success(request.getDn()));
} else {
log.warn("验签失败");
return ApiResponse.success(RawVerifyResponse.failure(request.getDn(), "签名验证失败"));
}
} catch (IllegalArgumentException e) {
log.error("验签请求参数错误: {}", e.getMessage());
return ApiResponse.fail(400, e.getMessage(), null);
} catch (Exception e) {
log.error("验签失败: {}", e.getMessage(), e);
return ApiResponse.success(RawVerifyResponse.failure(request.getDn(), e.getMessage()));
}
}
}

View File

@ -1,19 +1,33 @@
package com.cisd.tms.modules.openapi.service.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* RawSignRequest
* 签名请求参数
*/
@Data
@Schema(description = "签名请求")
public class RawSignRequest {
/**
* 证书 DNDistinguished Name
*/
@NotBlank(message = "证书DN不能为空")
@Schema(description = "证书DN", example = "CN=Initial Entity,OU=Initial OU,O=Initial Org,L=Beijing,ST=Beijing,C=CN")
private String dn;
/**
* 待签名原文的 Base64 编码
*/
@NotBlank(message = "待签名原文不能为空")
@Schema(description = "待签名原文的Base64编码", example = "dGVzdCBtZXNzYWdl")
private String origBytes;
/**
* 会话ID可选
*/
@Schema(description = "会话ID", example = "session-12345")
private String sessionId;
}

View File

@ -1,21 +1,25 @@
package com.cisd.tms.modules.openapi.service.impl;
import com.cisd.tms.common.enums.ErrorCode;
import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.integration.crypto.pcie.PcieSessionTemplate;
import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult;
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.service.JnaPcieCryptoService;
import com.cisd.tms.modules.cert.entity.KeyEntity;
import com.cisd.tms.modules.cert.service.CertificateService;
import com.cisd.tms.modules.cert.service.EntityService;
import com.cisd.tms.modules.openapi.service.IOpenApiService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
@Service
@RequiredArgsConstructor
public class OpenApiService implements IOpenApiService {
@ -23,62 +27,123 @@ public class OpenApiService implements IOpenApiService {
private static final Logger log = LoggerFactory.getLogger(OpenApiService.class);
private final PcieSessionTemplate sessionTemplate;
private JnaPcieCryptoService sdf;
private final JnaPcieCryptoService sdf;
// 签名验签均从证书 dn 作为索引所以引入证书服务类
private final CertificateService certificateService;
// 签名依赖实体所以引入实体服务类
private EntityService entityService;
private final EntityService entityService;
@Override
public String rawSign(byte[] origBytes, String dn) {
try {
log.info("===== 开始执行 SM2 签名 =====");
// 入参检查
if (origBytes == null || origBytes.length == 0) {
log.error("签名失败:待签名数据为空");
throw new IllegalArgumentException("待签名数据不能为空");
}
if (dn == null || dn.trim().isEmpty()) {
log.error("签名失败证书DN为空");
throw new IllegalArgumentException("证书DN不能为空");
}
// 根据 dn 查到私钥索引号
int keyIdx = 0;
log.info("待签名数据长度: {} bytes", origBytes.length);
log.info("证书DN: {}", dn);
try {
// 根据 dn 查找证书
X509Certificate cert = certificateService.getBySubjectDn(dn);
log.info("找到证书: {}", cert.getSubjectDN());
// 根据公钥查找实体
byte[] publicKeyBytes = cert.getPublicKey().getEncoded();
var entityIdOpt = entityService.findByCertificatePublicKey(publicKeyBytes);
if (entityIdOpt.isEmpty()) {
log.error("签名失败:证书 {} 未绑定实体", dn);
throw new BizException(ErrorCode.BAD_REQUEST.getCode(), "证书未绑定实体,无法进行签名");
}
Long entityId = entityIdOpt.get();
KeyEntity entity = entityService.getById(entityId);
int keyIdx = entity.getKeyIdx();
log.info("找到实体密钥索引: {}", keyIdx);
UserKeySm2SignRequest request = new UserKeySm2SignRequest();
request.setKeyIndex(keyIdx);
request.setData(origBytes);
request.setUserId(DEFAULT_SM2_USER_ID);
log.info("调用密码卡 SM2 签名接口...");
BackupDataResult result = sdf.userKeySignWithSm2Sm3(request);
// DER 格式返回
// Sm2KeySupport.rawSignatureToDer(result.getData());
// Base64 格式返回
return java.util.Base64.getEncoder().encodeToString(result.getData());
String signature = java.util.Base64.getEncoder().encodeToString(result.getData());
log.info("签名成功,签名长度: {} bytes", result.getLength());
log.info("===== SM2 签名完成 =====");
return signature;
} catch (BizException e) {
log.error("签名业务异常: {}", e.getMessage());
throw e;
} catch (Exception e) {
log.error("Raw sign failed: " + e.getMessage(), e);
throw new RuntimeException("Raw sign failed: " + e.getMessage(), e);
log.error("签名失败: {}", e.getMessage(), e);
throw new RuntimeException("签名失败: " + e.getMessage(), e);
}
}
@Override
public boolean rawVerify(byte[] origBytes, String signature, String dn) {
log.info("===== 开始执行 SM2 验签 =====");
// 入参检查
if (origBytes == null || origBytes.length == 0) {
log.error("验签失败:待验签数据为空");
throw new IllegalArgumentException("待验签数据不能为空");
}
if (signature == null || signature.trim().isEmpty()) {
log.error("验签失败:签名值为空");
throw new IllegalArgumentException("签名值不能为空");
}
if (dn == null || dn.trim().isEmpty()) {
log.error("验签失败证书DN为空");
throw new IllegalArgumentException("证书DN不能为空");
}
log.info("待验签数据长度: {} bytes", origBytes.length);
log.info("证书DN: {}", dn);
try {
// 获取证书
X509Certificate cert = certificateService.getBySubjectDn(dn);
if (cert == null) {
log.error("验签失败未找到DN为 {} 的证书", dn);
return false;
}
log.info("找到证书: {}", cert.getSubjectDN());
// 解码签名
byte[] sign = java.util.Base64.getDecoder().decode( signature );
byte[] sign = java.util.Base64.getDecoder().decode(signature);
log.info("签名数据长度: {} bytes", sign.length);
// 验签
UserKeySm2VerifyRequest request = new UserKeySm2VerifyRequest();
request.setSignature( sign );
request.setSignature(sign);
request.setData(origBytes);
request.setUserId(DEFAULT_SM2_USER_ID);
log.info("调用密码卡 SM2 验签接口...");
sdf.userKeyVerifyWithSm2Sm3(request);
log.info("验签成功");
log.info("===== SM2 验签完成 =====");
return true;
} catch (Exception e) {
log.error("Raw sign verify failed: {}", e.getMessage(), e);
log.error("验签失败: {}", e.getMessage(), e);
return false;
}
}