SM2数字证书手动验证证书链

本文介绍了在缺乏开源证书链验证工具的情况下,作者自研的手动验证数字证书链过程,包括获取公钥、处理签名数据和签名值,以及使用ASN.1进行解析和验证的详细步骤。
摘要由CSDN通过智能技术生成

#由于网络上找不到开源的证书链验证工具,自行研究了手动验证数字证书链的方法#

众所周知,验证数字签名需要签名数据、签名值、公钥。我们知道使用者证书是由上级证书的私钥签发的,所以验证时就需要调用上级证书的公钥。

1.公钥:这里直接获取上级证书的公钥。04不要 留下剩下的数据,8a 8a aa 47 0d 2a 9a 33 d8 50 5f ba 49 68 16 23 52 73 0c 73 c6 3b 48 93 8d 26 b9 9b 43 2e 47 f4 01 f3 7c 8b 06 49 dc 71 9d d3 d6 ac 1d 82 fd bb 7b 7a aa 28 c7 40 dd 4d e2 a5 99 33 ea ac 55 93

2.签名数据:证书的签名数据是基本证书域的asn.1 DER编码

我们需要将证书解析为DER格式,可以看到证书和上图介绍的结构一致。

将基本证书域的所有数据全部复制出来,根据验签工具判断是否需要删除换行与空格。(我这里工具可以不删除空格,只需要把换行删除就行)

3.签名值:最后一步获取签名值,同样是ASN.1中的签名值域

由于这里有部分数据不需要,所以需要做ASN.1解析。

把获取到的数据放在工具中进行验证,得到验签通过。

以下是Java代码示例,用于使用SM2证书对数据进行签名和验证: /** * SM2证书签名和验证示例 */ import java.security.Security; import java.security.KeyFactory; import java.security.PublicKey; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.X509CertificateStructure; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.signers.SM2Signer; public class SM2CertSignDemo { public static void main(String[] args) { Security.addProvider(new BouncyCastleProvider()); try { // 从证书获取SM2公钥 X509Certificate cert = loadCertificate("test-cert.pem"); PublicKey publicKey = cert.getPublicKey(); byte[] encodedPublicKey = publicKey.getEncoded(); SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedPublicKey); ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(subjectPublicKeyInfo.getPublicKeyData().getBytes(), SM2CurveUtils.SM2_CURVE); // 从密钥文件获取SM2私钥 byte[] privateKeyBytes = loadPrivateKeyBytes("test-key.pem"); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); byte[] encodedPrivateKey = privateKey.getEncoded(); ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(SM2CurveUtils.SM2_CURVE.decodeScalar(encodedPrivateKey), SM2CurveUtils.SM2_CURVE); // 签名示例 byte[] data = "Hello, world!".getBytes("UTF-8"); SM2Signer sm2Signer = new SM2Signer(); sm2Signer.init(true, ecPrivateKeyParameters); sm2Signer.update(data, 0, data.length); byte[] signature = sm2Signer.generateSignature(); System.out.println("Signature: " + bytesToHexString(signature)); // 验证签名示例 sm2Signer.init(false, ecPublicKeyParameters); sm2Signer.update(data, 0, data.length); boolean valid = sm2Signer.verifySignature(signature); System.out.println("Valid: " + valid); } catch (Exception e) { e.printStackTrace(); } } /** * 从证书文件加载X.509证书 */ private static X509Certificate loadCertificate(String certFile) throws Exception { byte[] certBytes = Files.readAllBytes(Paths.get(certFile)); X509CertificateHolder certHolder = new X509CertificateHolder(certBytes); X509CertificateStructure certStructure = certHolder.toASN1Structure(); X509Certificate cert = new JcaX509CertificateConverter().getCertificate(certStructure); return cert; } /** * 从密钥文件加载PKCS#8格式私钥 */ private static byte[] loadPrivateKeyBytes(String keyFile) throws Exception { byte[] keyBytes = Files.readAllBytes(Paths.get(keyFile)); String keyString = new String(keyBytes, "UTF-8"); keyString = keyString.replace("-----BEGIN PRIVATE KEY-----\n", ""); keyString = keyString.replace("-----END PRIVATE KEY-----\n", ""); keyString = keyString.replaceAll("\\s+", ""); return Base64.decode(keyString); } /** * 将字节数组转换为16进制字符串 */ private static String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { sb.append(String.format("%02X", bytes[i])); } return sb.toString(); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值