diff --git a/src/main/java/com/cisd/tms/modules/openapi/service/controller/OpenApiController.java b/src/main/java/com/cisd/tms/modules/openapi/service/controller/OpenApiController.java index 664d41d..4520c07 100644 --- a/src/main/java/com/cisd/tms/modules/openapi/service/controller/OpenApiController.java +++ b/src/main/java/com/cisd/tms/modules/openapi/service/controller/OpenApiController.java @@ -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 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 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())); + } } } diff --git a/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawSignRequest.java b/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawSignRequest.java index 7248293..8fbf51a 100644 --- a/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawSignRequest.java +++ b/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawSignRequest.java @@ -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 { /** * 证书 DN(Distinguished 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; } diff --git a/src/main/java/com/cisd/tms/modules/openapi/service/impl/OpenApiService.java b/src/main/java/com/cisd/tms/modules/openapi/service/impl/OpenApiService.java index 6aeced1..b2ca414 100644 --- a/src/main/java/com/cisd/tms/modules/openapi/service/impl/OpenApiService.java +++ b/src/main/java/com/cisd/tms/modules/openapi/service/impl/OpenApiService.java @@ -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; } }