X.509 证书结构描述
常见的X.509证书格式包括:
后缀作用cer/crt用于存放证书,它是2进制形式存放的,不含私钥pem以Ascii来表示,可以用于存放证书或私钥。pfx/p12用于存放个人证书/私钥,他通常包含保护密码,2进制方式。p10证书请求p7rCA对证书请求的回复,只用于导入p7b以树状展示证书链(certificate chain),同时也支持单个证书,不含私钥。
对于常见的https证书 一般是用crt或者pem来保存, http证书可点击网页前的锁按钮得到, 并且进行导出
注意,此处导出的证书可能是 .cer 文件,在使用 python3 处理的时候,可能报告如下错误:
Traceback (most recent call last): File "tool.py", line 9, incrt_data = fp.read() File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/codecs.py", line 322, in decode (result, consumed) = self._buffer_decode(data, self.errors, final) UnicodeDecodeError: 'utf-8' codec can't decode byte 0x82 in position 1: invalid start byte
这个错误是由于 python3 处理二进制数据的时候编码不正确导致的,简单的解决方法使用使用 openssl 工具转换成文本格式,执行如下命令:
$ openssl x509 -inform DER -in test.cer -out certificate.crt
然后解析 certificate.crt 文件即可。
证书数据结构
此证书结构来着白皮书
https://tools.ietf.org/html/rfc2459#section-4.1
Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, -- 证书主体 signatureAlgorithm AlgorithmIdentifier, -- 证书签名算法标识 signatureValue BIT STRING --证书签名值,是使用signatureAlgorithm部分指定的签名算法对tbsCertificate证书主题部分签名后的值. } TBSCertificate ::= SEQUENCE { version [0] EXPLICIT Version DEFAULT v1, -- 证书版本号 serialNumber CertificateSerialNumber, -- 证书序列号,对同一CA所颁发的证书,序列号唯一标识证书 signature AlgorithmIdentifier, --证书签名算法标识 issuer Name, --证书发行者名称 validity Validity, --证书有效期 subject Name, --证书主体名称 subjectPublicKeyInfo SubjectPublicKeyInfo,--证书公钥 issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, -- 证书发行者ID(可选),只在证书版本2、3中才有 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, -- 证书主体ID(可选),只在证书版本2、3中才有 extensions [3] EXPLICIT Extensions OPTIONAL -- 证书扩展段(可选),只在证书版本3中才有 } Version ::= INTEGER { v1(0), v2(1), v3(2) } CertificateSerialNumber ::= INTEGER AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } parameters: Dss-Parms ::= SEQUENCE { -- parameters ,DSA(DSS)算法时的parameters, RSA算法没有此参数 p INTEGER, q INTEGER, g INTEGER } signatureValue: Dss-Sig-Value ::= SEQUENCE { -- sha1DSA签名算法时,签名值 r INTEGER, s INTEGER } Name ::= CHOICE { RDNSequence } RDNSequence ::= SEQUENCE OF RelativeDistinguishedName RelativeDistinguishedName ::= SET OF AttributeTypeAndValue AttributeTypeAndValue ::= SEQUENCE { type AttributeType, value AttributeValue } AttributeType ::= OBJECT IDENTIFIER AttributeValue ::= ANY DEFINED BY AttributeType Validity ::= SEQUENCE { notBefore Time, -- 证书有效期起始时间 notAfter Time -- 证书有效期终止时间 } Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime } UniqueIdentifier ::= BIT STRING SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, -- 公钥算法 subjectPublicKey BIT STRING -- 公钥值 } subjectPublicKey: RSAPublicKey ::= SEQUENCE { -- RSA算法时的公钥值 modulus INTEGER, -- n publicExponent INTEGER -- e -- } Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING }
参考博客:
https://blog.csdn.net/xy010902100449/article/details/52145009
源代码
这里利用的是python3 的 Openssl 库进行解析, 此库的说明文档如下,
https://pyopenssl.org/en/0.15.1/api/crypto.html#x509name-objects
通过阅读说明文档, 可以轻松读取证书相关信息
代码如下
#!/usr/bin/python3 # -*- coding: utf-8 -*- import OpenSSL import time from dateutil import parser #openssl x509 -inform DER -in test.cer -out certificate.crt with open("certificate.crt", "r") as fp: crt_data = fp.read() cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, crt_data) 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')) print ("证书是否已经过期: ",cert.has_expired()) print("公钥长度" ,cert.get_pubkey().bits()) print("公钥:\n" ,OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_PEM, cert.get_pubkey()).decode("utf-8")) print("主体信息:") print("CN : 通用名称 OU : 机构单元名称") print("O : 机构名 L : 地理位置") print("S : 州/省名 C : 国名") for item in certIssue.get_components(): print(item[0].decode("utf-8"), " —— ",item[1].decode("utf-8")) print(cert.get_extension_count())
编译运行输出结果
注意, .cer 格式的文件,在使用 python3 处理的时候,可能报告如下错误:
Traceback (most recent call last): File "tool.py", line 9, incrt_data = fp.read() File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/codecs.py", line 322, in decode (result, consumed) = self._buffer_decode(data, self.errors, final) UnicodeDecodeError: 'utf-8' codec can't decode byte 0x82 in position 1: invalid start byte
这个错误是由于 python3 处理二进制数据的时候编码不正确导致的,简单的解决方法使用使用 openssl 工具转换成文本格式,执行如下命令:
$ openssl x509 -inform DER -in test.cer -out certificate.crt
然后解析 certificate.crt 文件即可。
Chrome 自带的解析结果对比
是相同的。
更多有趣案例视频教程源码获取加群:850591259