非对称签名验签

This commit is contained in:
liulu 2024-12-23 15:38:09 +08:00
parent 7fe6482983
commit 61f94f39cb
10 changed files with 555 additions and 3 deletions

View File

@ -2,6 +2,8 @@ package com.sunyard.chsm.mapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sunyard.chsm.enums.KeyUsage;
import com.sunyard.chsm.model.Subject;
import com.sunyard.chsm.model.entity.AppCert;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.util.Assert;
@ -28,5 +30,18 @@ public interface AppCertMapper extends BaseMapper<AppCert> {
return certs.iterator().next();
}
default AppCert selectSignBySubject(String dn) {
Assert.hasText(dn, "证书序列号不能为空");
String subject = Subject.fromDN(dn).getDN();
List<AppCert> certs = selectList(new LambdaQueryWrapper<AppCert>()
.eq(AppCert::getSubject, subject)
.eq(AppCert::getCertType, KeyUsage.SIGN_VERIFY.getCode())
);
if (CollectionUtils.isEmpty(certs)) {
return null;
}
return certs.iterator().next();
}
}

View File

@ -11,6 +11,7 @@ import com.sunyard.chsm.mapper.AppCertMapper;
import com.sunyard.chsm.mapper.ApplicationMapper;
import com.sunyard.chsm.mapper.KeyInfoMapper;
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
import com.sunyard.chsm.model.Subject;
import com.sunyard.chsm.model.dto.CertDTO;
import com.sunyard.chsm.model.entity.AppCert;
import com.sunyard.chsm.model.entity.Application;
@ -229,7 +230,8 @@ public class AppCertServiceImpl implements AppCertService {
cert.setCertText(importCert.getCertText());
cert.setVersion(String.valueOf(x509Cert.getVersion()));
cert.setSubject(x509Cert.getSubjectX500Principal().getName());
Subject subject = Subject.fromDN(x509Cert.getSubjectX500Principal().getName());
cert.setSubject(subject.getDN());
cert.setSerialNumber(x509Cert.getSerialNumber().toString());
cert.setIssuerDn(x509Cert.getIssuerX500Principal().getName());
cert.setNotBefore(x509Cert.getNotBefore());

View File

@ -0,0 +1,283 @@
package com.sunyard.chsm.utils.gm.cert;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
public class SM2X509CertMaker {
private static enum CertLevel {
RootCA,
SubCA,
EndEntity
} // class CertLevel
public static final String SIGN_ALGO_SM3WITHSM2 = "SM3withSM2";
private long certExpire;
private X500Name issuerDN;
// private CertSNAllocator snAllocator;
private KeyPair issuerKeyPair;
/**
* @param issuerKeyPair 证书颁发者的密钥对
* 其实一般的CA的私钥都是要严格保护的
* 一般CA的私钥都会放在加密卡/加密机里证书的签名由加密卡/加密机完成
* 这里仅是为了演示BC库签发证书的用法所以暂时不作太多要求
* @param certExpire 证书有效时间单位毫秒
* @param issuer 证书颁发者信息
* @param snAllocator 维护/分配证书序列号的实例证书序列号应该递增且不重复
*/
public SM2X509CertMaker(KeyPair issuerKeyPair, long certExpire, X500Name issuer)
// , CertSNAllocator snAllocator)
{
this.issuerKeyPair = issuerKeyPair;
this.certExpire = certExpire;
this.issuerDN = issuer;
// this.snAllocator = snAllocator;
}
/**
* 生成根CA证书
*
* @param csr CSR
* @return 新的证书
* @throws Exception 如果错误发生
*/
public X509Certificate makeRootCACert(byte[] csr)
throws Exception {
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign);
return makeCertificate(CertLevel.RootCA, null, csr, usage, null);
}
/**
* 生成SubCA证书
*
* @param csr CSR
* @return 新的证书
* @throws Exception 如果错误发生
*/
public X509Certificate makeSubCACert(byte[] csr)
throws Exception {
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign);
return makeCertificate(CertLevel.SubCA, 0, csr, usage, null);
}
/**
* 生成SSL用户证书
*
* @param csr CSR
* @return 新的证书
* @throws Exception 如果错误发生
*/
public X509Certificate makeSSLEndEntityCert(byte[] csr)
throws Exception {
return makeEndEntityCert(csr,
new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth});
}
/**
* 生成用户证书
*
* @param csr CSR
* @param extendedKeyUsages 扩展指数用途
* @return 新的证书
* @throws Exception 如果错误发生
*/
public X509Certificate makeEndEntityCert(byte[] csr,
KeyPurposeId[] extendedKeyUsages)
throws Exception {
KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyAgreement
| KeyUsage.dataEncipherment | KeyUsage.keyEncipherment);
return makeCertificate(CertLevel.EndEntity, null, csr, usage, extendedKeyUsages);
}
/**
* @param isCA 是否是颁发给CA的证书
* @param keyUsage 证书用途
* @param csr CSR
* @return
* @throws Exception
*/
private X509Certificate makeCertificate(CertLevel certLevel, Integer pathLenConstrain,
byte[] csr, KeyUsage keyUsage, KeyPurposeId[] extendedKeyUsages)
throws Exception {
if (certLevel == CertLevel.EndEntity) {
if (keyUsage.hasUsages(KeyUsage.keyCertSign)) {
throw new IllegalArgumentException(
"keyusage keyCertSign is not allowed in EndEntity Certificate");
}
}
PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr);
SubjectPublicKeyInfo subPub = request.getSubjectPublicKeyInfo();
PrivateKey issPriv = issuerKeyPair.getPrivate();
PublicKey issPub = issuerKeyPair.getPublic();
X500Name subject = request.getSubject();
String email = null;
String commonName = null;
/*
* RFC 5280 §4.2.1.6 Subject
* Conforming implementations generating new certificates with
* electronic mail addresses MUST use the rfc822Name in the subject
* alternative name extension (Section 4.2.1.6) to describe such
* identities. Simultaneous inclusion of the emailAddress attribute in
* the subject distinguished name to support legacy implementations is
* deprecated but permitted.
*/
RDN[] rdns = subject.getRDNs();
List<RDN> newRdns = new ArrayList<>(rdns.length);
for (int i = 0; i < rdns.length; i++) {
RDN rdn = rdns[i];
AttributeTypeAndValue atv = rdn.getFirst();
ASN1ObjectIdentifier type = atv.getType();
if (BCStyle.EmailAddress.equals(type)) {
email = IETFUtils.valueToString(atv.getValue());
} else {
if (BCStyle.CN.equals(type)) {
commonName = IETFUtils.valueToString(atv.getValue());
}
newRdns.add(rdn);
}
}
List<GeneralName> subjectAltNames = new LinkedList<>();
if (email != null) {
subject = new X500Name(newRdns.toArray(new RDN[0]));
subjectAltNames.add(
new GeneralName(GeneralName.rfc822Name,
new DERIA5String(email, true)));
}
boolean selfSignedEECert = false;
switch (certLevel) {
case RootCA:
if (issuerDN.equals(subject)) {
subject = issuerDN;
} else {
throw new IllegalArgumentException("subject != issuer for certLevel " + CertLevel.RootCA);
}
break;
case SubCA:
if (issuerDN.equals(subject)) {
throw new IllegalArgumentException(
"subject MUST not equals issuer for certLevel " + certLevel);
}
break;
default:
if (issuerDN.equals(subject)) {
selfSignedEECert = true;
subject = issuerDN;
}
}
// BigInteger serialNumber = snAllocator.nextSerialNumber();
BigInteger serialNumber = new BigInteger(IdWorker.getIdStr());
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + certExpire);
X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
issuerDN, serialNumber,
notBefore, notAfter,
subject, subPub);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
v3CertGen.addExtension(Extension.subjectKeyIdentifier, false,
extUtils.createSubjectKeyIdentifier(subPub));
if (certLevel != CertLevel.RootCA && !selfSignedEECert) {
v3CertGen.addExtension(Extension.authorityKeyIdentifier, false,
extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(issPub.getEncoded())));
}
// RFC 5280 §4.2.1.9 Basic Constraints:
// Conforming CAs MUST include this extension in all CA certificates
// that contain public keys used to validate digital signatures on
// certificates and MUST mark the extension as critical in such
// certificates.
BasicConstraints basicConstraints;
if (certLevel == CertLevel.EndEntity) {
basicConstraints = new BasicConstraints(false);
} else {
basicConstraints = pathLenConstrain == null
? new BasicConstraints(true) : new BasicConstraints(pathLenConstrain.intValue());
}
v3CertGen.addExtension(Extension.basicConstraints, true, basicConstraints);
// RFC 5280 §4.2.1.3 Key Usage: When present, conforming CAs SHOULD mark this extension as critical.
v3CertGen.addExtension(Extension.keyUsage, true, keyUsage);
if (extendedKeyUsages != null) {
ExtendedKeyUsage xku = new ExtendedKeyUsage(extendedKeyUsages);
v3CertGen.addExtension(Extension.extendedKeyUsage, false, xku);
boolean forSSLServer = false;
for (KeyPurposeId purposeId : extendedKeyUsages) {
if (KeyPurposeId.id_kp_serverAuth.equals(purposeId)) {
forSSLServer = true;
break;
}
}
if (forSSLServer) {
if (commonName == null) {
throw new IllegalArgumentException("commonName must not be null");
}
GeneralName name = new GeneralName(GeneralName.dNSName,
new DERIA5String(commonName, true));
subjectAltNames.add(name);
}
}
if (!subjectAltNames.isEmpty()) {
v3CertGen.addExtension(Extension.subjectAlternativeName, false,
new GeneralNames(subjectAltNames.toArray(new GeneralName[0])));
}
JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME)
.getCertificate(v3CertGen.build(contentSignerBuilder.build(issPriv)));
cert.verify(issPub);
return cert;
}
private JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub) throws Exception {
if (issPub.getAlgorithm().equals("EC")) {
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(SIGN_ALGO_SM3WITHSM2);
contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
return contentSignerBuilder;
}
throw new Exception("Unsupported PublicKey Algorithm:" + issPub.getAlgorithm());
}
}

View File

@ -0,0 +1,20 @@
package com.sunyard.chsm.param;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class AsymSignP7Req {
/**
* 签名证书的主题
*/
@NotBlank(message = "签名证书不能为空")
private String subject;
// 原文,使用Base64编码
@NotBlank(message = "原文不能为空")
private String plainData;
}

View File

@ -0,0 +1,10 @@
package com.sunyard.chsm.param;
import lombok.Data;
@Data
public class AsymSignP7Resp {
// 签名值 Base64编码
private String signData;
}

View File

@ -12,7 +12,7 @@ public class AsymSignRawReq {
@NotNull(message = "密钥ID不能为空")
private Long keyId;
// ,使用Base64编码
// ,使用Base64编码
@NotBlank(message = "明文不能为空")
private String plainData;

View File

@ -0,0 +1,23 @@
package com.sunyard.chsm.param;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class AsymVerifyP7Req {
/**
* 签名证书的主题
*/
// @NotBlank(message = "签名证书不能为空")
// private String subject;
// 原文,使用Base64编码
@NotBlank(message = "明文不能为空")
private String plainData;
// 签名值, 使用Base64编码
private String signData;
}

View File

@ -23,7 +23,7 @@ public class AsymVerifyRawReq {
@NotBlank(message = "密文不能为空")
private String signData;
// ,使用Base64编码
// ,使用Base64编码
@NotBlank(message = "明文不能为空")
private String plainData;

View File

@ -7,8 +7,11 @@ import com.sunyard.chsm.param.AsymDecryptReq;
import com.sunyard.chsm.param.AsymDecryptResp;
import com.sunyard.chsm.param.AsymEncryptReq;
import com.sunyard.chsm.param.AsymEncryptResp;
import com.sunyard.chsm.param.AsymSignP7Req;
import com.sunyard.chsm.param.AsymSignP7Resp;
import com.sunyard.chsm.param.AsymSignRawReq;
import com.sunyard.chsm.param.AsymSignRawResp;
import com.sunyard.chsm.param.AsymVerifyP7Req;
import com.sunyard.chsm.param.AsymVerifyRawReq;
import com.sunyard.chsm.param.VerifyResp;
import com.sunyard.chsm.service.AsymKeyService;
@ -85,5 +88,59 @@ public class AsymKeyController {
return R.data(resp);
}
/**
* P7 Attach签名
*
* @param req
* @return
*/
@PostMapping("/sign/P7Attach")
@AuthCode(AuthCodeConst.sign_P7Attach)
public R<AsymSignP7Resp> signP7Attach(@Valid @RequestBody AsymSignP7Req req) {
AsymSignP7Resp resp = asymKeyService.signP7Attach(req);
return R.data(resp);
}
/**
* P7 Attach验签
*
* @param req
* @return
*/
@PostMapping("/verify/P7Attach")
@AuthCode(AuthCodeConst.verify_P7Attach)
public R<VerifyResp> verifyP7Attach(@Valid @RequestBody AsymVerifyP7Req req) {
VerifyResp resp = asymKeyService.verifyP7Attach(req);
return R.data(resp);
}
/**
* P7 Detach签名
*
* @param req
* @return
*/
@PostMapping("/sign/P7Detach")
@AuthCode(AuthCodeConst.sign_P7Detach)
public R<AsymSignP7Resp> signP7Detach(@Valid @RequestBody AsymSignP7Req req) {
AsymSignP7Resp resp = asymKeyService.signP7Detach(req);
return R.data(resp);
}
/**
* P7 Detach验签
*
* @param req
* @return
*/
@PostMapping("/verify/P7Detach")
@AuthCode(AuthCodeConst.verify_P7Detach)
public R<VerifyResp> verifyP7Detach(@Valid @RequestBody AsymVerifyP7Req req) {
VerifyResp resp = asymKeyService.verifyP7Detach(req);
return R.data(resp);
}
}

View File

@ -5,37 +5,67 @@ import com.sunyard.chsm.enums.KeyAlg;
import com.sunyard.chsm.enums.KeyCategory;
import com.sunyard.chsm.enums.KeyStatus;
import com.sunyard.chsm.enums.KeyUsage;
import com.sunyard.chsm.mapper.AppCertMapper;
import com.sunyard.chsm.mapper.KeyInfoMapper;
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
import com.sunyard.chsm.model.entity.AppCert;
import com.sunyard.chsm.model.entity.KeyInfo;
import com.sunyard.chsm.model.entity.KeyRecord;
import com.sunyard.chsm.param.AsymDecryptReq;
import com.sunyard.chsm.param.AsymDecryptResp;
import com.sunyard.chsm.param.AsymEncryptReq;
import com.sunyard.chsm.param.AsymEncryptResp;
import com.sunyard.chsm.param.AsymSignP7Req;
import com.sunyard.chsm.param.AsymSignP7Resp;
import com.sunyard.chsm.param.AsymSignRawReq;
import com.sunyard.chsm.param.AsymSignRawResp;
import com.sunyard.chsm.param.AsymVerifyP7Req;
import com.sunyard.chsm.param.AsymVerifyRawReq;
import com.sunyard.chsm.param.VerifyResp;
import com.sunyard.chsm.sdf.SdfApiService;
import com.sunyard.chsm.sdf.model.EccCipher;
import com.sunyard.chsm.sdf.model.EccSignature;
import com.sunyard.chsm.utils.CodecUtils;
import com.sunyard.chsm.utils.gm.BCECUtils;
import com.sunyard.chsm.utils.gm.cert.BCSM2CertUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSignerInfoVerifierBuilder;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
/**
* @author liulu
* @since 2024/12/20
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AsymKeyService {
private final AppCertMapper appCertMapper;
private final KeyInfoMapper keyInfoMapper;
private final SpKeyRecordMapper spKeyRecordMapper;
private final SdfApiService sdfApiService;
@ -147,6 +177,118 @@ public class AsymKeyService {
return resp;
}
public AsymSignP7Resp signP7Attach(AsymSignP7Req req) {
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
AppCert appCert = appCertMapper.selectSignBySubject(req.getSubject());
Assert.notNull(appCert, "证书主题对应的证书不存在");
Assert.isTrue(Objects.equals(appCert.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此密钥ID");
byte[] encPri = CodecUtils.decodeHex(appCert.getEncPriKey());
byte[] pri = sdfApiService.decryptByTMK(encPri);
byte[] asymSignP7Resp = p7Sign(pri, appCert.getCertText(), plainData, true);
AsymSignP7Resp resp = new AsymSignP7Resp();
resp.setSignData(CodecUtils.encodeBase64(asymSignP7Resp));
return resp;
}
public VerifyResp verifyP7Attach(AsymVerifyP7Req req) {
byte[] signData = CodecUtils.decodeBase64(req.getSignData());
boolean verify;
try {
verify = p7Verify(signData, null);
} catch (Exception e) {
verify = false;
}
VerifyResp resp = new VerifyResp();
resp.setVerified(verify);
return resp;
}
public AsymSignP7Resp signP7Detach(AsymSignP7Req req) {
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
AppCert appCert = appCertMapper.selectSignBySubject(req.getSubject());
Assert.notNull(appCert, "证书主题对应的证书不存在");
Assert.isTrue(Objects.equals(appCert.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此密钥ID");
byte[] encPri = CodecUtils.decodeHex(appCert.getEncPriKey());
byte[] pri = sdfApiService.decryptByTMK(encPri);
byte[] asymSignP7Resp = p7Sign(pri, appCert.getCertText(), plainData, false);
AsymSignP7Resp resp = new AsymSignP7Resp();
resp.setSignData(CodecUtils.encodeBase64(asymSignP7Resp));
return resp;
}
public VerifyResp verifyP7Detach(AsymVerifyP7Req req) {
Assert.hasText(req.getPlainData(), "P7Detach验签原文不能为空");
byte[] signData = CodecUtils.decodeBase64(req.getSignData());
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
boolean verify;
try {
verify = p7Verify(signData, plainData);
} catch (Exception e) {
verify = false;
}
VerifyResp resp = new VerifyResp();
resp.setVerified(verify);
return resp;
}
private static byte[] p7Sign(byte[] pri, String cert, byte[] plainData, boolean encapsulate) {
try {
BCECPrivateKey privateKey = BCECUtils.createPrivateKey(pri);
X509Certificate x509Cert = BCSM2CertUtils.getX509Cert(cert);
// 构造签名内容
CMSTypedData cmsData = new CMSProcessableByteArray(plainData);
// 生成签名者信息
SignerInfoGenerator signerInfoGenerator = new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build()
).build(
new JcaContentSignerBuilder("SM3withSM2").setProvider(BouncyCastleProvider.PROVIDER_NAME).build(privateKey),
x509Cert
);
// 构建 CMS Signed Data
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(signerInfoGenerator);
generator.addCertificates(new JcaCertStore(Collections.singletonList(x509Cert)));
CMSSignedData signedData = generator.generate(cmsData, encapsulate);
return signedData.getEncoded();
} catch (Exception ex) {
log.warn("", ex);
throw new IllegalArgumentException("P7Attach 签名异常");
}
}
public static boolean p7Verify(byte[] signedDataBytes, byte[] originalData) throws Exception {
CMSSignedData signedData;
if (originalData == null || originalData.length == 0) {
signedData = new CMSSignedData(signedDataBytes);
} else {
CMSTypedData originalContent = new CMSProcessableByteArray(originalData);
signedData = new CMSSignedData(originalContent, signedDataBytes);
}
Store<X509CertificateHolder> certStore = signedData.getCertificates();
SignerInformationStore signers = signedData.getSignerInfos();
for (SignerInformation signer : signers.getSigners()) {
Collection<X509CertificateHolder> matches = certStore.getMatches(signer.getSID());
if (matches.isEmpty()) {
throw new IllegalArgumentException("No matching certificate found for signer");
}
X509CertificateHolder certHolder = matches.iterator().next(); // 这里进行类型安全的提取
X509Certificate cert = new JcaX509CertificateConverter()
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.getCertificate(certHolder);
if (signer.verify(new JcaSignerInfoVerifierBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build()).build(cert))) {
return true;
}
}
return false;
}
private KeyInfo checkKey(Long keyId, KeyUsage usage) {