python SM2证书解析-证书链验证具体实现【含资源!见页首】

本文详细介绍了SM2证书的验证过程,包括使用上级证书公钥验证下级证书的基本证书域,以及执行步骤中的证书签名校验、读取和验证证书信息,CRL请求等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、SM2证书验证简述

主要的:需要实现的功能主要是用上级证书的<公钥>验证下级证书的<基本证书域>。
次要的:证书过没过期,证书上的公钥是否与证书本体绑定等…

二、执行

1.先写一个SM2验签模块
gmssl version 3.2.1

# -*- coding: utf-8 -*-
import binascii

from gmssl import sm3, func, sm2
from gmssl.sm2 import default_ecc_table


def Verify(pubkey, M, sign):
    ID = "31323334353637383132333435363738"
    ENTL = "0080"  # 1234567812345678的长度为:16 * 8 = 128个bit即为0x80长度
    a = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC"  # 默认值
    b = "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93"  # 默认值
    x_G = "32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7"  # 默认值
    y_G = "bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0"  # 默认值
    pubKey = pubkey  # 测试用公钥常量
    sign_value = sign  # 签名值

    z_object = bytes.fromhex(ENTL + ID + a + b + x_G + y_G + pubKey)  # (ENTL || ID || a || b || x_P || y_P)
    # print("常量+密钥的byte总和:", z_object)

    Z = sm3.sm3_hash(func.bytes_to_list(z_object))  # hash(ENTL || ID || a || b || x_P || y_P)
    # print("Z的值是:", Z)

    # 基本证书域内容——已经过工具测试
    m = M

    # hash(Z + m)计算出验签data
    e1 = sm3.sm3_hash(func.bytes_to_list(bytes.fromhex(Z) + m))
    # print("总杂凑值e1:", e1)

    # 验签环节:
    verify_func = sm2.CryptSM2(private_key=None,
                               public_key=pubKey,
                               ecc_table=default_ecc_table)
    verify_done = verify_func.verify(
        sign_value,  # 签名值(R,S)
        bytes.fromhex(e1))  # hash(Z || M)的杂凑值e1
    print("验签结果:", verify_done)
    return verify_done

# if __name__ == '__main__':
#     Material_Z()

证书验证效果

2.再写一个读取证书的模块

# -*- coding: utf-8 -*-
import binascii
import cryptography.x509 as x509

def sign_get(Certificate):  # 获取签名值
    # 读取数字证书文件
    with open(Certificate, 'rb') as f:
        cert_data = f.read()
    # 解析证书
    cert = x509.load_pem_x509_certificate(cert_data)

    # 获取签名值
    signature = cert.signature

    # 将签名值以16进制格式打印出来
    hex_signature = signature.hex()
    print("签名值:", hex_signature)

    S = hex_signature[-64:]
    Newfile_half = hex_signature[:-64]
    if Newfile_half[-1] == "0" and Newfile_half[-2] == "0":
        Newfile_half = Newfile_half[:-6]
        R = Newfile_half[-64:]
    else:
        Newfile_half = Newfile_half[:-4]
        R = Newfile_half[-64:]
    print("提取的签名值:\nR:", R, "\nS:", S)
    return R + S


def mainArea_get(Certificate):  # 获取基本证书域
    cer_file = Certificate
    with open(cer_file, 'rb') as f:
        cert_data = f.read()
        cert = x509.load_pem_x509_certificate(cert_data)
        # 获取签名值
        mainArea = cert.tbs_certificate_bytes
        return mainArea

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

获取签名值域和基本证书域信息 3.再写一个CRL请求-验证模块,从证书中获取最新的CRL列表

import binascii
import requests
from der2crt import dataType_exchange
from cryptography import x509
from cryptography.hazmat.backends import default_backend

class crlGet:
    def __init__(self, Cert_get):
        self.cert = Cert_get.strip('"')

    def crlUriGet(self):
        # Load the certificate
        uri_value = ""
        with open(self.cert, "rb") as cert_file:
            cert_data = cert_file.read()

        certificate = x509.load_pem_x509_certificate(cert_data, default_backend())

        # Extract CRL Distribution Points extension
        crl_distribution_points = certificate.extensions.get_extension_for_class(x509.CRLDistributionPoints)

        # Iterate over CRLs and print URI values
        for distribution_point in crl_distribution_points.value:
            for uri in distribution_point.full_name:
                if isinstance(uri, x509.UniformResourceIdentifier):
                    uri_value = uri.value

                try:
                    crl_data = requests.get(uri_value).content
                except requests.RequestException as e:
                    print(f"Error downloading CRL: {e}")
                    return None

                    # 保存CRL文件
                try:
                    with open(f'{self.cert}_crl.crl', 'wb') as crl_file:
                        crl_file.write(crl_data)
                except IOError as e:
                    print(f"Error saving CRL file: {e}")
                    return None

                return f'{self.cert}_crl.crl', uri_value

def get_crl_serial_numbers(file_path):
    with open(file_path, 'rb') as crl_file:
        crl_data = crl_file.read()
        crl = x509.load_der_x509_crl(crl_data, default_backend())
        serial_numbers = [entry.serial_number for entry in crl]
    return serial_numbers

def normal(certstrip):
    xxx = crlGet(certstrip)
    crl = xxx.crlUriGet()[0]
    list0fcrl = get_crl_serial_numbers(crl)
    return list0fcrl

使用效果1:半自动验证crl:
半自动验证CRL
使用效果2:全自动验证crl:
全自动验证
4.可以顺带写个次要的:读取证书信息的模块,如果只为了验证证书链真实性,这个不写也行。

# -*- coding: utf-8 -*-
import binascii
import base64
import sys
import OpenSSL
import time
from dateutil import parser

def read_Cert(a):
    with open(a, "r") as fp:
        crt_data = fp.read()

    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, crt_data.encode())
    certIssue = cert.get_issuer()

    print("证书版本:            ", cert.get_version() + 1)

    print("证书序列号:          ", hex(cert.get_serial_number()))

    print("证书中使用的签名算法: ", cert.get_signature_algorithm().decode("UTF-8"))

    print("颁发者:              ", certIssue.commonName)

    datetime_struct = parser.parse(cert.get_notBefore().decode("UTF-8"))

    print("有效期从:             ", datetime_struct.strftime('%Y-%m-%d %H:%M:%S'))

    datetime_struct = parser.parse(cert.get_notAfter().decode("UTF-8"))

    print("到:                   ", datetime_struct.strftime('%Y-%m-%d %H:%M:%S'))

    if cert.has_expired():
        print("证书已过期")
    else:
        print("证书未过期")

    print("公钥长度", cert.get_pubkey().bits())

    print("公钥:\n", OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_PEM, cert.get_pubkey()).decode("utf-8"))
    # print(OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_PEM, cert.get_pubkey()))


def pub_key_Get(input):
    with open(input, "r") as fp:
        crt_data = fp.read()
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, crt_data.encode())
    a = OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_PEM, cert.get_pubkey())[27:-26]
    # print("a:", a, type(a))
    single_Public_Key = ((base64.b64decode(a)).hex())[-128:]
    # print("公钥16进制模式:", single_Public_Key, type(single_Public_Key))
    return single_Public_Key


def main_info(a):
    print("主体信息:")
    with open(a, "r") as fp:
        crt_data = fp.read()
    # print("CN : 通用名称  OU : 机构单元名称")
    # print("O  : 机构名    L  : 地理位置")
    # print("S  : 州/省名   C  : 国名")
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, crt_data.encode())
    certIssue = cert.get_issuer()
    dictCert = {"国家:": None, "机构名:": None, "机构单元名称:": None, "通用名称:": None, "地理位置:": None, "州/省名:": None}
    for item in certIssue.get_components():
        if item[0].decode("utf-8") == "C":
            dictCert["国家:"] = item[1].decode("utf-8")
        elif item[0].decode("utf-8") == "O":
            dictCert["机构名:"] = item[1].decode("utf-8")
        elif item[0].decode("utf-8") == "OU":
            dictCert["机构单元名称:"] = item[1].decode("utf-8")
        elif item[0].decode("utf-8") == "CN":
            dictCert["通用名称:"] = item[1].decode("utf-8")
        elif item[0].decode("utf-8") == "L":
            dictCert["地理位置:"] = item[1].decode("utf-8")
        elif item[0].decode("utf-8") == "S":
            dictCert["州/省名:"] = item[1].decode("utf-8")
        # print(item[0].decode("utf-8"), "  ——  ", item[1].decode("utf-8"))

    # print(cert.get_extension_count())
    return dictCert

获取证书基本信息

5.最后写个main,来使用这些模块:
在这里插入图片描述

内容:【3.证书基本信息解析】的代码抄了:【B站:阿里武】https://blog.csdn.net/qq874455953/article/details/85041415
谢谢啦~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

中二病也要吃饭啊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值