签名调试通过
This commit is contained in:
parent
cee2a74770
commit
90ed811c1d
@ -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();
|
||||
// 入参检查
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 签名 =====");
|
||||
|
||||
// 根据 dn 查到私钥索引号
|
||||
int keyIdx = 0;
|
||||
// 入参检查
|
||||
if (origBytes == null || origBytes.length == 0) {
|
||||
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 {
|
||||
// 根据 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);
|
||||
log.info("签名数据长度: {} bytes", sign.length);
|
||||
|
||||
// 验签
|
||||
UserKeySm2VerifyRequest request = new UserKeySm2VerifyRequest();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user