openapi 模块,签名验签代码调整。

This commit is contained in:
cheney 2026-05-12 11:36:30 +08:00
parent 32907333d5
commit ea63a72a4d
2 changed files with 43 additions and 184 deletions

View File

@ -376,7 +376,7 @@ public class EntityService {
}
}
private static final class CardContentSigner implements ContentSigner {
public static final class CardContentSigner implements ContentSigner {
private final java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream();
private final int keyIdx;

View File

@ -1,103 +1,64 @@
package com.cisd.tms.modules.openapi.service.impl;
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.UserKeySm2SignRequest;
import com.cisd.tms.integration.crypto.pcie.model.UserKeySm2VerifyRequest;
import com.cisd.tms.integration.crypto.pcie.service.JnaPcieCryptoService;
import com.sun.jna.Structure;
import com.cisd.tms.modules.cert.repository.CertificateRepository;
import com.cisd.tms.modules.cert.service.CertificateService;
import com.cisd.tms.modules.cert.support.CertUtil;
import com.cisd.tms.modules.cert.service.EntityService;
import com.cisd.tms.modules.openapi.service.IOpenApiService;
import com.cisd.tms.modules.openapi.service.dto.GenericCertificate;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.RequiredArgsConstructor;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.KeyPair;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
@RequiredArgsConstructor
public class OpenApiService implements IOpenApiService {
private static final byte[] DEFAULT_SM2_USER_ID = "1234567812345678".getBytes(StandardCharsets.UTF_8);
private static final Logger log = LoggerFactory.getLogger(OpenApiService.class);
private final PcieSessionTemplate sessionTemplate;
private JnaPcieCryptoService sdf;
// 签名验签均从证书 dn 作为索引所以引入证书服务类
private final CertificateService certificateService;
// 签名依赖实体所以引入实体服务类
private EntityService entityService;
@Override
public String rawSign(byte[] origBytes, String dn) {
try {
// 使用固定的密钥索引 0
int keyIndex = 0;
// 获取证书和公钥
X509Certificate cert = certificateService.getBySubjectDn(dn);
if (cert == null) {
throw new RuntimeException("Certificate not found for DN: " + dn);
}
// 根据 dn 查到私钥索引号
int keyIdx = 0;
// 将公钥转换为 EccRefPublicKey
EccRefPublicKey eccRefPublicKey = EccRefPublicKey.fromPublicKey(cert.getPublicKey());
UserKeySm2SignRequest request = new UserKeySm2SignRequest();
request.setKeyIndex(keyIdx);
request.setData(origBytes);
request.setUserId(DEFAULT_SM2_USER_ID);
// 使用 SDF 接口进行 SM3 哈希包含公钥和 ID
byte[] hash = new byte[32]; // SM3 哈希长度为 32 字节
IntByReference hashLength = new IntByReference();
BackupDataResult result = sdf.userKeySignWithSm2Sm3(request);
// 默认的 SM2 用户 ID
final String userId = "1234567812345678";
return sessionTemplate.withSession("SM2Sign", (sdf, deviceHandle, sessionHandle) -> {
// 初始化哈希包含公钥和用户 ID
sessionTemplate.ensureSuccess(
"SDF_HashInit",
sdf.SDF_HashInit(sessionHandle, 0, eccRefPublicKey, userId.getBytes(), 16)
);
// 更新哈希添加原始数据
sessionTemplate.ensureSuccess(
"SDF_HashUpdate",
sdf.SDF_HashUpdate(sessionHandle, origBytes, origBytes.length)
);
// 完成哈希
sessionTemplate.ensureSuccess(
"SDF_HashFinal",
sdf.SDF_HashFinal(sessionHandle, hash, hashLength)
);
// 使用 SDF 接口进行 SM2 签名
EccSignature signature = new EccSignature();
sessionTemplate.ensureSuccess(
"SDF_InternalSign_ECC",
sdf.SDF_InternalSign_ECC(sessionHandle, keyIndex, hash, hashLength.getValue(), signature)
);
// EccSignature 转换为字节数组
signature.read();
byte[] signatureBytes = signature.getPointer().getByteArray(0, signature.size());
// 返回 Base64 编码的签名
return new String(Base64.encode(signatureBytes));
});
// DER 格式返回
// Sm2KeySupport.rawSignatureToDer(result.getData());
// Base64 格式返回
return java.util.Base64.getEncoder().encodeToString(result.getData());
} catch (Exception e) {
e.printStackTrace();
log.error("Raw sign failed: " + e.getMessage(), e);
throw new RuntimeException("Raw sign failed: " + e.getMessage(), e);
}
}
@Override
public boolean rawVerify(byte[] origBytes, String certStr, String dn) {
public boolean rawVerify(byte[] origBytes, String signature, String dn) {
try {
// 获取证书
X509Certificate cert = certificateService.getBySubjectDn(dn);
@ -106,50 +67,18 @@ public class OpenApiService implements IOpenApiService {
}
// 解码签名
byte[] signatureBytes = Base64.decode(certStr);
byte[] sign = java.util.Base64.getDecoder().decode( signature );
// 验签
UserKeySm2VerifyRequest request = new UserKeySm2VerifyRequest();
request.setSignature( sign );
request.setData(origBytes);
request.setUserId(DEFAULT_SM2_USER_ID);
sdf.userKeyVerifyWithSm2Sm3(request);
// 将公钥转换为 EccRefPublicKey
EccRefPublicKey eccRefPublicKey = EccRefPublicKey.fromPublicKey(cert.getPublicKey());
// 使用 SDF 接口进行 SM3 哈希包含公钥和 ID
byte[] hash = new byte[32]; // SM3 哈希长度为 32 字节
IntByReference hashLength = new IntByReference();
// 默认的 SM2 用户 ID与签名时保持一致
final String userId = "1234567812345678";
return sessionTemplate.withSession("SM2Verify", (sdf, deviceHandle, sessionHandle) -> {
// 初始化哈希包含公钥和用户 ID
sessionTemplate.ensureSuccess(
"SDF_HashInit",
sdf.SDF_HashInit(sessionHandle, 0, eccRefPublicKey, userId.getBytes(), 16)
);
// 更新哈希添加原始数据
sessionTemplate.ensureSuccess(
"SDF_HashUpdate",
sdf.SDF_HashUpdate(sessionHandle, origBytes, origBytes.length)
);
// 完成哈希
sessionTemplate.ensureSuccess(
"SDF_HashFinal",
sdf.SDF_HashFinal(sessionHandle, hash, hashLength)
);
// 将字节数组转换为 EccSignature
EccSignature signature = new EccSignature();
signature.getPointer().write(0, signatureBytes, 0, signatureBytes.length);
signature.read();
// 使用 SDF 接口进行 SM2 验签
int ret = sdf.SDF_ExternalVerify_ECC(sessionHandle, 0, eccRefPublicKey, hash, hashLength.getValue(), signature);
return ret == 0;
});
return true;
} catch (Exception e) {
e.printStackTrace();
log.error("Raw sign verify failed: {}", e.getMessage(), e);
return false;
}
}
@ -162,86 +91,16 @@ public class OpenApiService implements IOpenApiService {
@Override
public String dettachedVerify(byte[] origBytes, String certStr) {
try {
// 解码签名
byte[] signedDataBytes = Base64.decode(certStr);
// 使用原始数据验证签名
CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(origBytes), signedDataBytes);
// 验证签名
for (Object signerInfoObj : signedData.getSignerInfos().getSigners()) {
org.bouncycastle.cms.SignerInformation signerInfo = (org.bouncycastle.cms.SignerInformation) signerInfoObj;
// 获取证书
java.util.Collection certMatches = signedData.getCertificates().getMatches(signerInfo.getSID());
org.bouncycastle.cert.X509CertificateHolder certHolder =
(org.bouncycastle.cert.X509CertificateHolder) certMatches.iterator().next();
X509Certificate cert = new org.bouncycastle.cert.jcajce.JcaX509CertificateConverter()
.setProvider("BC")
.getCertificate(certHolder);
// 验证签名
try {
if (signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey()))) {
// 验证证书有效性检查有效期
cert.checkValidity(new java.util.Date());
return CertUtil.convertToPem(cert);
}
} catch (org.bouncycastle.cms.CMSSignerDigestMismatchException e) {
// 签名验证失败数据不匹配不打印堆栈信息
continue;
}
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
// 暂时不实现避免编译错误
throw new UnsupportedOperationException("dettachedSign is not implemented yet");
}
@Override
public String dettachedVerifySimple(byte[] origBytes, String certStr) {
try {
// 解码签名
byte[] signedDataBytes = Base64.decode(certStr);
// 使用原始数据验证签名
CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(origBytes), signedDataBytes);
// 验证签名不验证证书有效性
for (Object signerInfoObj : signedData.getSignerInfos().getSigners()) {
org.bouncycastle.cms.SignerInformation signerInfo = (org.bouncycastle.cms.SignerInformation) signerInfoObj;
// 获取证书
java.util.Collection certMatches = signedData.getCertificates().getMatches(signerInfo.getSID());
org.bouncycastle.cert.X509CertificateHolder certHolder =
(org.bouncycastle.cert.X509CertificateHolder) certMatches.iterator().next();
X509Certificate cert = new org.bouncycastle.cert.jcajce.JcaX509CertificateConverter()
.setProvider("BC")
.getCertificate(certHolder);
// 只验证签名是否正确不检查证书有效期
try {
if (signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey()))) {
return CertUtil.convertToPem(cert);
}
} catch (org.bouncycastle.cms.CMSSignerDigestMismatchException e) {
// 签名验证失败数据不匹配不打印堆栈信息
continue;
}
// 暂时不实现避免编译错误
throw new UnsupportedOperationException("dettachedSign is not implemented yet");
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* ASN.1 DER 编码的签名转换为 r||s 格式