com.github.shepherdviolet.glacimon.java.crypto.AdvancedCertificateUtils Maven / Gradle / Ivy
/*
* Copyright (C) 2022-2022 S.Violet
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Project GitHub: https://github.com/shepherdviolet/glacimon
* Email: [email protected]
*/
package com.github.shepherdviolet.glacimon.java.crypto;
import com.github.shepherdviolet.glacimon.java.conversion.Base64Utils;
import com.github.shepherdviolet.glacimon.java.crypto.base.*;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.operator.OperatorCreationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertPath;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Date;
import java.util.List;
/**
* 高级证书工具
*
* 基本功能见glacijava-common的{@link CertificateUtils}
*
* @author shepherdviolet
*/
public class AdvancedCertificateUtils extends CertificateUtils {
/***********************************************************************************************
* Common
***********************************************************************************************/
/**
* 使用BouncyCastle从输入流中解析证书, 适用于SM2等更多算法的证书
* @param certData X509格式证书数据
*/
public static X509Certificate parseX509ToCertificateAdvanced(byte[] certData) throws CertificateException, NoSuchProviderException {
if (certData == null) {
throw new NullPointerException("certData == null");
}
return (X509Certificate) BaseBCCertificateUtils.parseCertificateByBouncyCastle(new ByteArrayInputStream(certData), BaseCertificateUtils.TYPE_X509);
}
/**
* 使用BouncyCastle从输入流中解析证书, 适用于SM2等更多算法的证书
* @param inputStream X509格式证书数据流, 会被close掉
*/
public static X509Certificate parseX509ToCertificateAdvanced(InputStream inputStream) throws CertificateException, NoSuchProviderException {
return (X509Certificate) BaseBCCertificateUtils.parseCertificateByBouncyCastle(inputStream, BaseCertificateUtils.TYPE_X509);
}
/**
* 将用户证书/CA证书/根证书组装成证书链
* @param certificateList 用户证书/CA证书/根证书, 顺序为用户证书->CA证书->根证书
*/
public static CertPath generateX509CertPath(List certificateList) throws CertificateException, NoSuchProviderException {
return BaseBCCertificateUtils.generateCertPath(certificateList, BaseCertificateUtils.TYPE_X509);
}
/**
* 使用BouncyCastle从输入流中解析证书链, 适用于SM2等更多算法的证书
* @param data 证书链数据, X509 PKCS7
*/
public static CertPath parseX509PKCS7CertPath(byte[] data) throws CertificateException, NoSuchProviderException {
if (data == null) {
throw new NullPointerException("data == null");
}
return BaseBCCertificateUtils.parseCertPathByBouncyCastle(new ByteArrayInputStream(data), BaseCertificateUtils.TYPE_X509, "PKCS7");
}
/**
* 使用BouncyCastle从输入流中解析证书链, 适用于SM2等更多算法的证书
* @param inputStream 证书链数据流, 会被close掉
*/
public static CertPath parseX509PKCS7CertPath(InputStream inputStream) throws CertificateException, NoSuchProviderException {
return BaseBCCertificateUtils.parseCertPathByBouncyCastle(inputStream, BaseCertificateUtils.TYPE_X509, "PKCS7");
}
/**
* 使用颁发者公钥验证证书有效性
* @param certificate 证书
* @param issuerPublicKey 颁发者公钥
*/
public static void verifyCertificate(X509Certificate certificate, RSAPublicKey issuerPublicKey)
throws NoSuchAlgorithmException, CertificateException, NoSuchProviderException, InvalidKeyException, SignatureException {
BaseBCCertificateUtils.verifyCertificate(certificate, issuerPublicKey, new Date());
}
/**
* 使用颁发者公钥验证证书有效性
* @param certificate 证书
* @param issuerPublicKey 颁发者公钥
*/
public static void verifyCertificate(X509Certificate certificate, BCECPublicKey issuerPublicKey)
throws NoSuchAlgorithmException, CertificateException, NoSuchProviderException, InvalidKeyException, SignatureException {
BaseBCCertificateUtils.verifyCertificate(certificate, issuerPublicKey, new Date());
}
/**
* 使用颁发者公钥验证证书有效性
* @param certificate 证书
* @param issuerPublicKeyParams 颁发者公钥
*/
public static void verifyCertificate(X509Certificate certificate, ECPublicKeyParameters issuerPublicKeyParams)
throws NoSuchAlgorithmException, CertificateException, NoSuchProviderException, InvalidKeyException, SignatureException {
BaseBCCertificateUtils.verifyCertificate(certificate, issuerPublicKeyParams, new Date());
}
/**
* 使用颁发者公钥验证证书有效性
* @param certificate 证书
* @param issuerPublicKey 颁发者公钥
* @param currentTime 当前时间(用于有效期验证)
*/
public static void verifyCertificate(X509Certificate certificate, RSAPublicKey issuerPublicKey, Date currentTime)
throws NoSuchAlgorithmException, CertificateException, NoSuchProviderException, InvalidKeyException, SignatureException {
BaseBCCertificateUtils.verifyCertificate(certificate, issuerPublicKey, currentTime);
}
/**
* 使用颁发者公钥验证证书有效性
* @param certificate 证书
* @param issuerPublicKey 颁发者公钥
* @param currentTime 当前时间(用于有效期验证)
*/
public static void verifyCertificate(X509Certificate certificate, BCECPublicKey issuerPublicKey, Date currentTime)
throws NoSuchAlgorithmException, CertificateException, NoSuchProviderException, InvalidKeyException, SignatureException {
BaseBCCertificateUtils.verifyCertificate(certificate, issuerPublicKey, currentTime);
}
/**
* 使用颁发者公钥验证证书有效性
* @param certificate 证书
* @param issuerPublicKeyParams 颁发者公钥
* @param currentTime 当前时间(用于有效期验证)
*/
public static void verifyCertificate(X509Certificate certificate, ECPublicKeyParameters issuerPublicKeyParams, Date currentTime)
throws NoSuchAlgorithmException, CertificateException, NoSuchProviderException, InvalidKeyException, SignatureException {
BaseBCCertificateUtils.verifyCertificate(certificate, issuerPublicKeyParams, currentTime);
}
/**
* 证书链的方式验证证书是否有效. (CA和ROOT证书由issuerProvider提供)
*
* 根证书 和 CA证书都由服务端限定, 客户端上送自己的证书
* AdvancedCertificateUtils.verifyCertificateByIssuers(cert, DateTimeUtils.stringToDate("2020-05-21", "yyyy-MM-dd"), new SimpleIssuerProvider(Arrays.asList(caCert, rootCert)));
* CA证书由服务端限定, 且服务端把CA证书当根证书用, 客户端上送自己的证书
* AdvancedCertificateUtils.verifyCertificateByIssuers(cert, DateTimeUtils.stringToDate("2020-05-21", "yyyy-MM-dd"), new SimpleIssuerProvider(Collections.singletonList(new IssuerProvider.ActAsRoot(caCert))));
* 更多示例见: RSACertTest
*
* @param certificate 待验证的证书. 注意, 不可以是根证书.
* @param currentTime 当前时间(用于有效期验证), 可以简单地new Date()
* @param issuerProvider 提供验证所需的证书颁发者. 例如: SimpleIssuerProvider / RootIssuerProvider
*/
public static void verifyCertificateByIssuers(X509Certificate certificate, Date currentTime, IssuerProvider> issuerProvider) throws CertificateException {
BaseBCCertificateUtils.verifyCertificateByIssuers(certificate, currentTime, issuerProvider, null);
}
/**
* 证书链的方式验证证书是否有效. (CA证书可由客户端提供, ROOT证书必须由issuerProvider提供)
*
* 根证书由服务端限定, 客户端上送自己的证书和CA证书
* AdvancedCertificateUtils.verifyCertificateByIssuers(cert, DateTimeUtils.stringToDate("2020-05-21", "yyyy-MM-dd"), new RootIssuerProvider(Collections.singletonList(rootCert)), Collections.singletonList(caCert));
* 更多示例见: RSACertTest
*
* @param certificate 待验证的证书. 注意, 不可以是根证书.
* @param currentTime 当前时间(用于有效期验证), 可以简单地new Date()
* @param issuerProvider 提供验证所需的证书颁发者. 例如: SimpleIssuerProvider / RootIssuerProvider
* @param issuerProviderParameter 传给IssuerProvider的参数, 可选, 取决于IssuerProvider是否需要; RootIssuerProvider送客户端上送的CA证书
*/
public static void verifyCertificateByIssuers(X509Certificate certificate, Date currentTime, IssuerProvider issuerProvider, ParameterType issuerProviderParameter) throws CertificateException {
BaseBCCertificateUtils.verifyCertificateByIssuers(certificate, currentTime, issuerProvider, issuerProviderParameter);
}
/**
* 获取证书支持的所有域名, 从CN和Alternative Names中获取
* @param certificate 证书
*/
public static List getDomainNamesFromCertificate(X509Certificate certificate) throws CertificateParsingException, IOException {
return BaseBCCertificateUtils.getDomainNamesFromCertificate(certificate);
}
/***********************************************************************************************
* RSA
***********************************************************************************************/
/**
* 密钥类型:RSA
*/
public static final String KEK_ALGORITHM_RSA = "RSA";
/**
* 签名算法:MD5withRSA
*/
public static final String SIGN_ALGORITHM_RSA_MD5 = "MD5withRSA";
/**
* 签名算法:SHA1withRSA
*/
public static final String SIGN_ALGORITHM_RSA_SHA1 = "SHA1withRSA";
/**
* 签名算法:SHA256withRSA
*/
public static final String SIGN_ALGORITHM_RSA_SHA256 = "SHA256withRSA";
/**
* 生成RSA根证书(自签名证书)
*
*
* CN=(名称或域名),
* OU=(部门名称),
* O=(组织名称),
* L=(城市或区域名称),
* ST=(州或省份名称),
* C=(国家代码)
*
*
* @param dn 证书的DN信息, 例:CN=Test CA, OU=IT Dept, O=My Company, L=Ningbo, ST=Zhejiang, C=CN
* @param publicKey 证书的RSA公钥
* @param privateKey 证书的RSA私钥(自签名)
* @param validity 证书的有效期(天), 例:3650
* @param signAlgorithm 签名算法, AdvancedCertificateUtils.SIGN_ALGORITHM_RSA_SHA256
*
*/
public static X509Certificate generateRSAX509RootCertificate(String dn, RSAPublicKey publicKey, RSAPrivateKey privateKey, int validity, String signAlgorithm) throws IOException, CertificateException, OperatorCreationException {
return BaseBCCertificateUtils.generateRSAX509RootCertificate(dn, publicKey, privateKey, validity, signAlgorithm);
}
/**
* 生成RSA证书(由CA证书私钥颁发)
*
*
* CN=(名称或域名),
* OU=(部门名称),
* O=(组织名称),
* L=(城市或区域名称),
* ST=(州或省份名称),
* C=(国家代码)
*
*
* @param subjectDn 申请证书的DN信息, 例:CN=Test CA, OU=IT Dept, O=My Company, L=Ningbo, ST=Zhejiang, C=CN
* @param subjectPublicKey 申请证书的RSA公钥
* @param subjectValidity 申请证书的有效期(天), 例:3650
* @param signAlgorithm 签名算法, AdvancedCertificateUtils.SIGN_ALGORITHM_RSA_SHA256
* @param caCertificate CA的证书
* @param caPrivateKey CA的RSA私钥
*/
public static X509Certificate generateRSAX509Certificate(String subjectDn, RSAPublicKey subjectPublicKey, int subjectValidity, String signAlgorithm, X509Certificate caCertificate, RSAPrivateKey caPrivateKey) throws IOException, CertificateException, OperatorCreationException {
return BaseBCCertificateUtils.generateRSAX509Certificate(subjectDn, subjectPublicKey, subjectValidity, signAlgorithm, caCertificate, caPrivateKey);
}
/***********************************************************************************************
* SM2
***********************************************************************************************/
/**
* 密钥类型:SM2
*/
public static final String KEK_ALGORITHM_SM2 = "SM2";
public static final String EC_KEY_ALGORITHM = "EC";
/**
* 签名算法:SM3withSM2
*/
public static final String SIGN_ALGORITHM_SM2_SM3 = "SM3withSM2";
/**
* 生成SM2证书的申请数据CSR (CSR包含申请者信息, CA收到CSR后签名并返回证书)
*
*
* CN=(名称或域名),
* OU=(部门名称),
* O=(组织名称),
* L=(城市或区域名称),
* ST=(州或省份名称),
* C=(国家代码)
*
*
* @param subjectDn 申请人DN信息, 例:CN=Test CA, OU=IT Dept, O=My Company, L=Ningbo, ST=Zhejiang, C=CN
* @param publicKeyParams 申请人公钥
* @param privateKeyParams 申请人私钥
* @return 证书申请数据, CSR
*/
public static byte[] generateSm2Csr(String subjectDn,
ECPublicKeyParameters publicKeyParams,
ECPrivateKeyParameters privateKeyParams) throws OperatorCreationException, IOException {
return BaseBCCertificateUtils.generateSm2Csr(subjectDn, publicKeyParams, privateKeyParams);
}
/**
* 生成SM2根证书(顶级CA)
*
*
* CN=(名称或域名),
* OU=(部门名称),
* O=(组织名称),
* L=(城市或区域名称),
* ST=(州或省份名称),
* C=(国家代码)
*
*
* @param caDn CA的DN信息, 例:CN=Test CA, OU=IT Dept, O=My Company, L=Ningbo, ST=Zhejiang, C=CN
* @param validity 申请证书的有效期(天), 例:3650
* @param issuerPublicKeyParams 证书颁发者(CA)的公钥
* @param issuerPrivateKeyParams 证书颁发者(CA)的私钥
*/
public static X509Certificate generateSm2X509RootCertificate(String caDn,
int validity,
ECPublicKeyParameters issuerPublicKeyParams,
ECPrivateKeyParameters issuerPrivateKeyParams) throws IOException, OperatorCreationException, CertificateException, InvalidKeySpecException, NoSuchAlgorithmException {
byte[] csr = BaseBCCertificateUtils.generateSm2Csr(caDn, issuerPublicKeyParams, issuerPrivateKeyParams);
return BaseBCCertificateUtils.generateSm2X509Certificate(
csr,
validity,
caDn,
issuerPublicKeyParams,
issuerPrivateKeyParams,
true,
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign),
null);
}
/**
* 生成SM2二级CA证书
*
*
* CN=(名称或域名),
* OU=(部门名称),
* O=(组织名称),
* L=(城市或区域名称),
* ST=(州或省份名称),
* C=(国家代码)
*
*
* @param csr 证书申请数据, BaseBCCertificateUtils.generateSm2Csr(...)
* @param validity 申请证书的有效期(天), 例:3650
* @param issuerCertificate 证书颁发者(CA)的证书
* @param issuerPrivateKeyParams 证书颁发者(CA)的私钥
*/
public static X509Certificate generateSm2X509CaCertificate(byte[] csr,
int validity,
X509Certificate issuerCertificate,
ECPrivateKeyParameters issuerPrivateKeyParams) throws InvalidKeySpecException, OperatorCreationException, CertificateException, NoSuchAlgorithmException, IOException {
return BaseBCCertificateUtils.generateSm2X509Certificate(
csr,
validity,
issuerCertificate.getSubjectX500Principal().toString(),
BaseBCAsymKeyGenerator.parseEcPublicKeyParamsFromCertificate(SM2DefaultCurve.DOMAIN_PARAMS, issuerCertificate),
issuerPrivateKeyParams,
true,
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign),
null);
}
/**
* 生成SM2用户证书, 这个方法只从CSR(P10)中获取公钥和DN信息, 其他扩展信息忽略了.
*
*
* CN=(名称或域名),
* OU=(部门名称),
* O=(组织名称),
* L=(城市或区域名称),
* ST=(州或省份名称),
* C=(国家代码)
*
*
* @param csr 证书申请数据, BaseBCCertificateUtils.generateSm2Csr(...)
* @param validity 申请证书的有效期(天), 例:3650
* @param issuerCertificate 证书颁发者(CA)的证书
* @param issuerPrivateKeyParams 证书颁发者(CA)的私钥
* @param usage 证书用途, 本方法签发证书时, 会忽略CSR中请求的证书用途, 用这个参数强制覆盖.
* 用于签名: new KeyUsage(KeyUsage.digitalSignature | KeyUsage.nonRepudiation).
* 用于加密: new KeyUsage(KeyUsage.keyEncipherment | KeyUsage.dataEncipherment).
* 用于签发证书: new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign).
* @param subjectAlternativeName 可选域名, 可为空, new GeneralName[]{new GeneralName(GeneralName.dNSName, "1.host.com"),
* new GeneralName(GeneralName.dNSName, "2.host.com")}
*/
public static X509Certificate generateSm2X509Certificate(byte[] csr,
int validity,
X509Certificate issuerCertificate,
ECPrivateKeyParameters issuerPrivateKeyParams,
KeyUsage usage,
GeneralName[] subjectAlternativeName) throws InvalidKeySpecException, OperatorCreationException, CertificateException, NoSuchAlgorithmException, IOException {
return BaseBCCertificateUtils.generateSm2X509Certificate(
csr,
validity,
issuerCertificate.getSubjectX500Principal().toString(),
BaseBCAsymKeyGenerator.parseEcPublicKeyParamsFromCertificate(SM2DefaultCurve.DOMAIN_PARAMS, issuerCertificate),
issuerPrivateKeyParams,
false,
usage,
subjectAlternativeName);
}
/**
* 生成SM2用户证书, 这个方法只从CSR(P10)中获取公钥和DN信息, 其他扩展信息忽略了.
*
*
* CN=(名称或域名),
* OU=(部门名称),
* O=(组织名称),
* L=(城市或区域名称),
* ST=(州或省份名称),
* C=(国家代码)
*
*
* @param csr 证书申请数据, BaseBCCertificateUtils.generateSm2Csr(...)
* @param validity 申请证书的有效期(天), 例:3650
* @param issuerCertificate 证书颁发者(CA)的证书
* @param issuerPrivateKeyParams 证书颁发者(CA)的私钥
*/
public static X509Certificate generateSm2X509Certificate(byte[] csr,
int validity,
X509Certificate issuerCertificate,
ECPrivateKeyParameters issuerPrivateKeyParams) throws InvalidKeySpecException, OperatorCreationException, CertificateException, NoSuchAlgorithmException, IOException {
return BaseBCCertificateUtils.generateSm2X509Certificate(
csr,
validity,
issuerCertificate.getSubjectX500Principal().toString(),
BaseBCAsymKeyGenerator.parseEcPublicKeyParamsFromCertificate(SM2DefaultCurve.DOMAIN_PARAMS, issuerCertificate),
issuerPrivateKeyParams,
false,
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.nonRepudiation | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment),
null);
}
/***********************************************************************************************
* Others
***********************************************************************************************/
/**
* 将证书的DN信息转成X500Name实例 (便于获取里面具体的值, 例如获取CN)
* @param dn DN信息
* @return X500Name
*/
public static X500NameWrapper dnToX500Name(String dn) throws IOException {
return BaseBCCertificateUtils.dnToX500Name(dn);
}
/**
* 将证书的DN信息转成X500Name实例 (便于获取里面具体的值, 例如获取CN)
* @param principal DN信息, Certificate#getSubjectX500Principal / getIssuerX500Principal得到
* @return X500Name
*/
public static X500NameWrapper dnToX500Name(Principal principal) throws IOException {
return BaseBCCertificateUtils.dnToX500Name(principal.getName());
}
/**
* 解析ASN.1编码的X509证书数据
*
* @param certBase64 X509证书数据, ASN.1编码, Base64编码
*/
@SuppressWarnings("deprecation")
public static X509CertificateStructure parseX509ToStructure(String certBase64) throws IOException {
return parseX509ToStructure(Base64Utils.decode(certBase64));
}
/**
* 解析ASN.1编码的X509证书数据
*
* @param certData X509证书数据, ASN.1编码, 非Base64编码
*/
@SuppressWarnings("deprecation")
public static X509CertificateStructure parseX509ToStructure(byte[] certData) throws IOException {
return parseX509ToStructure(new ByteArrayInputStream(certData));
}
/**
* 解析ASN.1编码的X509证书数据
*
*
* 证书版本:cert.getVersion()
* 序列号:cert.getSerialNumber().getValue().toString(16)
* 算法标识:cert.getSignatureAlgorithm().getObjectId().getId()
* 签发者:cert.getIssuer()
* 开始时间:cert.getStartDate().getTime()
* 结束时间:cert.getEndDate().getTime()
* 主体名:cert.getSubject()
* 签名值:cert.getSignature().getBytes()
*
* 主体公钥:SubjectPublicKeyInfo publicInfo = cert.getSubjectPublicKeyInfo();
* 标识符:publicInfo.getAlgorithmId().getObjectId().getId()
* 公钥值:publicInfo.getPublicKeyData().getBytes()
*
*
*
* 标识符:
*
* rsaEncryption RSA算法标识 1.2.840.113549.1.1.1
* sha1withRSAEncryption SHA1的RSA签名 1.2.840.113549.1.1.5
* ECC ECC算法标识 1.2.840.10045.2.1
* SM2 SM2算法标识 1.2.156.10197.1.301
* SM3WithSM2 SM3的SM2签名 1.2.156.10197.1.501
* sha1withSM2 SHA1的SM2签名 1.2.156.10197.1.502
* sha256withSM2 SHA256的SM2签名 1.2.156.10197.1.503
* sm3withRSAEncryption SM3的RSA签名 1.2.156.10197.1.504
* commonName 主体名 2.5.4.3
* emailAddress 邮箱 1.2.840.113549.1.9.1
* cRLDistributionPoints CRL分发点 2.5.29.31
* extKeyUsage 扩展密钥用法 2.5.29.37
* subjectAltName 使用者备用名称 2.5.29.17
* CP 证书策略 2.5.29.32
* clientAuth 客户端认证 1.3.6.1.5.5.7.3.2
*
*
* @param inputStream X509证书输入流, ASN.1编码, 非Base64编码
*/
@SuppressWarnings("deprecation")
public static X509CertificateStructure parseX509ToStructure(InputStream inputStream) throws IOException {
return BaseBCCertificateUtils.parseX509ToStructure(inputStream);
}
/**
* (尝试)修复非标准的X509证书中的签名数据.
*
* 20240820:
* 发现某RA签发出来的SM2证书本身签名格式不标准(CA私钥对用户证书签名), 截取证书末尾的签名数据(ASN.1), 如下所示:
* 30 45 02 21 008265......27 02 20 0039e7......43
* 30表示type=SEQUENCE, 45表示SEQUENCE长度69 (0x45首位为0, 本身即为长度, 若首位为1, 表示长度区域的长度)
* 02表示type=INTEGER, 21表示INTEGER长度为33, 008265......27为INTEGER的值
* 02表示type=INTEGER, 20表示INTEGER长度为32, 0039e7......43为INTEGER的值
* 问题出在0039e7......43, ASN.1标准要求正整数首位为1时, 前面补零(0x00, 防止变成负数),
* 而0039e7......43的39首位不为1, 不应该补零, 但是这个RA签发的证书却补零了, 所以BouncyCastle解析签名数据失败, 导致证书验签失败.
*
* ASN.1格式参考可参考: https://blog.csdn.net/new9232/article/details/139205158
*
* @param certX509 X509格式的证书数据(需要修复其中的签名数据)
* @return 尝试修复后的X509证书数据, 修复失败返回原证书
*/
public static byte[] fixCertSign(byte[] certX509) {
return NonStandardDataFixer.fixCertSign(certX509);
}
/**
* (尝试)修复非标准的X509证书中的签名数据.
*
* 20240820:
* 发现某RA签发出来的SM2证书本身签名格式不标准(CA私钥对用户证书签名), 截取证书末尾的签名数据(ASN.1), 如下所示:
* 30 45 02 21 008265......27 02 20 0039e7......43
* 30表示type=SEQUENCE, 45表示SEQUENCE长度69 (0x45首位为0, 本身即为长度, 若首位为1, 表示长度区域的长度)
* 02表示type=INTEGER, 21表示INTEGER长度为33, 008265......27为INTEGER的值
* 02表示type=INTEGER, 20表示INTEGER长度为32, 0039e7......43为INTEGER的值
* 问题出在0039e7......43, ASN.1标准要求正整数首位为1时, 前面补零(0x00, 防止变成负数),
* 而0039e7......43的39首位不为1, 不应该补零, 但是这个RA签发的证书却补零了, 所以BouncyCastle解析签名数据失败, 导致证书验签失败.
*
* ASN.1格式参考可参考: https://blog.csdn.net/new9232/article/details/139205158
*
* @param cert X509证书(需要修复其中的签名数据)
* @return 尝试修复后的证书, 修复失败返回原证书
*/
public static X509Certificate fixCertSign(X509Certificate cert) {
return NonStandardDataFixer.fixCertSign(cert);
}
}