SM2证书签名值手动验证【含资源!见页首】

本文介绍了如何手动验证SM2证书的签名,包括使用ASN.1查看器提取证书域和签名值,以及利用公钥进行验证的过程。还提及了椭圆曲线公钥的压缩规则和验证脚本的使用方法。
摘要由CSDN通过智能技术生成

上传了个SM2证书手动验证器,强迫症得绑点文字上去,这篇文字只介绍手动验签的部分,关于证书字段、证书链验证等内容可以看以下两篇:
SM2证书解析-证书链验证具体实现【含资源!见页首】
SM2证书链验证(P7B)【含资源!见页首】

所谓手动验签,就是用上级CA证书的公钥,验证下级证书的签名值。逐级向上验证,直至根证书。

1.使用ASN.1查看器打开待验证的证书:将基本证书域的Hex流复制下来作为待验签的原文值。

基本证书域tbs获取

2.使用ASN.1查看器获取签名值域的TLV字段值

签名值域获取
然而,签名值域的长度一般在70byte到74byte之间,除去签名值域开头的内容标记,签名值RS点存储形式为TLV。故需要剥离头部标记和R,S点的TL,才能够从签名值域中获取到RS的值
在获取证书签名值域的时候可以用的偷懒小技巧:已知证书签名值的编码格式为TLV,即:R(TLV), S(TLV)。然而SM2验签模块需要的值只有R和S,那么我们对签名值后32字节进行截取,这32字节的内容即为S。接下来只需要判断S对应的Type和Length即可。我们已知证书签名值的Type为02,签名值长度一般会出现20(16进制20即为10进制32-对应32字节),21(16进制21即为10进制33-对应33字节)两种。因为SM2一个点的坐标长度为32字节,那么当长度为34字节时,其Length前方必然带有00填充。则可把规则写成:签名值域-截掉32字节S,判断末尾16进制字符串是否为“00”,若是“00”,则剥除前方3字节-4个16进制字符串(Type和length和填充“00”)-再从签名值字符串末尾取32字节(64个HexStr字符)-即为点R的值。

由于这玩意是做 [证书自动验证工具] 时的副产物,故源代码已经丢失,只留下了编译好的程序,供初学者或对待特殊证书时使用。所以下面只说使用流程:

这东西全部的内容有这些:
在这里插入图片描述

1.将上文从ASN.1中提取的基本证书域、签名值域,和上一级CA证书的公钥(直接点开CA证书复制即可)的内容直接复制到工具路径的input文件夹里对应的txt中

手动步骤1

2.点击验证脚本

验证脚本点击
关于椭圆曲线公钥压缩规则:Hex字符串04开头:是为未被压缩的公钥,Hex字符串03开头,后面的32byte为公钥X坐标的值,我们可以通过带入椭圆曲线参数对公钥进行解压缩。03意为:y的值(整形)为奇数;对应的,02意为y的值为偶数,可以用我这个工具来对椭圆曲线的公钥进行压缩、解压缩:SM2公钥压缩、解压缩工具

最后的 错误尝试:可以尝试修改output文件夹中txt字段的任意内容,再点开验证脚本:

在这里插入图片描述
得到验证False的结果
失败的验证

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是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
发出的红包

打赏作者

中二病也要吃饭啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值