提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、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:
使用效果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
谢谢啦~