验签通过

This commit is contained in:
cheney 2026-05-13 09:51:22 +08:00
parent aa538ee1d7
commit e15ccf5973
7 changed files with 199 additions and 6 deletions

43
qodana.yaml Normal file
View File

@ -0,0 +1,43 @@
#-------------------------------------------------------------------------------#
# Qodana analysis is configured by qodana.yaml file #
# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
#-------------------------------------------------------------------------------#
version: "1.0"
#Specify inspection profile for code analysis
profile:
name: qodana.starter
#Enable inspections
#include:
# - name: <SomeEnabledInspectionId>
#Disable inspections
#exclude:
# - name: <SomeDisabledInspectionId>
# paths:
# - <path/where/not/run/inspection>
projectJDK: "17" #(Applied in CI/CD pipeline)
#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
#bootstrap: sh ./prepare-qodana.sh
#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
#plugins:
# - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
# Quality gate. Will fail the CI/CD pipeline if any condition is not met
# severityThresholds - configures maximum thresholds for different problem severities
# testCoverageThresholds - configures minimum code coverage on a whole project and newly added code
# Code Coverage is available in Ultimate and Ultimate Plus plans
#failureConditions:
# severityThresholds:
# any: 15
# critical: 5
# testCoverageThresholds:
# fresh: 70
# total: 50
#Specify Qodana linter for analysis (Applied in CI/CD pipeline)
linter: jetbrains/qodana-jvm:2025.2

View File

@ -33,7 +33,7 @@ public class EccRefPublicKey extends Structure {
}
private static byte[] toBytes(Structure structure) {
structure.read();
structure.write();
return structure.getPointer().getByteArray(0, structure.size());
}

View File

@ -0,0 +1,61 @@
package com.cisd.tms.integration.crypto.pcie.model;
/**
* ExternalSm2VerifyRequest
* 外部 SM2 验签请求参数
* 包含数据的 SM3 哈希处理带公钥和验签逻辑
*/
public class ExternalSm2VerifyRequest {
/**
* 外部公钥原始字节如从证书中获取的 PublicKey.getEncoded()
*/
private byte[] publicKey;
/**
* 待验签的数据
*/
private byte[] data;
/**
* 签名值DER 格式或原始格式的签名
*/
private byte[] signature;
/**
* SM2 用户 ID可选默认 "1234567812345678"
*/
private byte[] userId;
public byte[] getPublicKey() {
return publicKey;
}
public void setPublicKey(byte[] publicKey) {
this.publicKey = publicKey;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public byte[] getSignature() {
return signature;
}
public void setSignature(byte[] signature) {
this.signature = signature;
}
public byte[] getUserId() {
return userId;
}
public void setUserId(byte[] userId) {
this.userId = userId;
}
}

View File

@ -2620,4 +2620,4 @@ public class JnaPcieCryptoService implements PcieCryptoService {
}
return Arrays.copyOf(userId, userId.length);
}
}
}

View File

@ -0,0 +1,19 @@
package com.cisd.tms.modules.openapi.service.dto;
import lombok.Data;
@Data
public class RawVerifyRequest {
/**
* 证书 DN 或机构号
*/
private String dn;
private String origBytes;
private String sessionId;
/**
* 裸签名串Base64
*/
private String signature;
}

View File

@ -0,0 +1,42 @@
package com.cisd.tms.modules.openapi.service.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* RawVerifyResponse
* 验签响应结果
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "验签响应")
public class RawVerifyResponse {
/**
* 验签结果true表示验签通过false表示验签失败
*/
@Schema(description = "验签结果", example = "true")
private boolean verified;
/**
* 结果描述信息
*/
@Schema(description = "结果描述", example = "验签成功")
private String message;
/**
* 证书DN
*/
@Schema(description = "证书DN", example = "CN=Initial Entity,OU=Initial OU,O=Initial Org,L=Beijing,ST=Beijing,C=CN")
private String dn;
public static RawVerifyResponse success(String dn) {
return new RawVerifyResponse(true, "验签成功", dn);
}
public static RawVerifyResponse failure(String dn, String reason) {
return new RawVerifyResponse(false, "验签失败: " + reason, dn);
}
}

View File

@ -5,6 +5,7 @@ import com.cisd.tms.common.exception.BizException;
import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds;
import com.cisd.tms.integration.crypto.pcie.PcieSessionTemplate;
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.EccExternalVerifyRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2SignRequest;
@ -13,6 +14,7 @@ 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 com.sun.jna.ptr.IntByReference;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -94,7 +96,13 @@ public class OpenApiService implements IOpenApiService {
throw new RuntimeException("签名失败: " + e.getMessage(), e);
}
}
private static String toHex(byte[] bytes) {
StringBuilder builder = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
builder.append(String.format("%02x", b));
}
return builder.toString();
}
@Override
public boolean rawVerify(byte[] origBytes, String signature, String dn) {
@ -128,18 +136,38 @@ public class OpenApiService implements IOpenApiService {
log.info("找到证书: {}", cert.getSubjectDN());
// 将证书中的公钥转换为 EccRefPublicKey 结构
EccRefPublicKey publicKey = EccRefPublicKey.fromPublicKey(cert.getPublicKey());
log.info("公钥 x: {}, y: {}", toHex(publicKey.x), toHex(publicKey.y));
byte[] publicKeyBlob = EccRefPublicKey.fromPublicKeyToBlob(cert.getPublicKey());
log.info("公钥Blob长度: {} bytes", publicKeyBlob.length);
log.info("公钥Blob {} 长度: {} bytes", toHex(publicKeyBlob), publicKeyBlob.length);
// 解码签名
byte[] sign = java.util.Base64.getDecoder().decode(signature);
log.info("签名数据长度: {} bytes", sign.length);
// 使用外部公钥验签
// 使用带公钥的 SM3 计算数据哈希
log.info("使用带公钥的 SM3 计算数据哈希...");
byte[] digest = sessionTemplate.withSession("SDF_HashFinal_SM2", (lib, deviceHandle, sessionHandle) -> {
sessionTemplate.ensureSuccess(
"SDF_HashInit",
lib.SDF_HashInit(sessionHandle, Gm0018AlgorithmIds.SM3, publicKey, DEFAULT_SM2_USER_ID, DEFAULT_SM2_USER_ID.length)
);
if (origBytes.length > 0) {
sessionTemplate.ensureSuccess("SDF_HashUpdate", lib.SDF_HashUpdate(sessionHandle, origBytes, origBytes.length));
}
byte[] out = new byte[32];
IntByReference outLen = new IntByReference(out.length);
sessionTemplate.ensureSuccess("SDF_HashFinal", lib.SDF_HashFinal(sessionHandle, out, outLen));
return out;
});
log.info("SM3 哈希计算完成,哈希{} 长度: {} bytes", toHex(digest), digest.length);
// 使用外部公钥验签注意传递的是哈希值而不是原始数据
EccExternalVerifyRequest request = new EccExternalVerifyRequest();
request.setAlgId(Gm0018AlgorithmIds.SM2_SIGN_1);
request.setPublicKeyBlob(publicKeyBlob);
request.setData(origBytes);
request.setData(digest);
request.setSignature(sign);
log.info("调用密码卡 SM2 外部公钥验签接口...");