diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..84cc32f --- /dev/null +++ b/qodana.yaml @@ -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: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +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 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 diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/EccRefPublicKey.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/EccRefPublicKey.java index 4fd2e1a..4ae3726 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/EccRefPublicKey.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/jna/EccRefPublicKey.java @@ -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()); } diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/model/ExternalSm2VerifyRequest.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/ExternalSm2VerifyRequest.java new file mode 100644 index 0000000..3192b8b --- /dev/null +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/ExternalSm2VerifyRequest.java @@ -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; + } +} \ No newline at end of file diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java index 53af823..8aa3295 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java @@ -2620,4 +2620,4 @@ public class JnaPcieCryptoService implements PcieCryptoService { } return Arrays.copyOf(userId, userId.length); } -} +} \ No newline at end of file diff --git a/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawVerifyRequest.java b/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawVerifyRequest.java new file mode 100644 index 0000000..0a6f39b --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawVerifyRequest.java @@ -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; + +} \ No newline at end of file diff --git a/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawVerifyResponse.java b/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawVerifyResponse.java new file mode 100644 index 0000000..c3186d2 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/openapi/service/dto/RawVerifyResponse.java @@ -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); + } +} \ No newline at end of file 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 8b2ab9e..3da54c1 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 @@ -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 外部公钥验签接口...");