(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
SSL通信的基础就是证书认证:通过认证完成信任关系建立;通过证书中携带的公钥,与持有的私钥,完成信息密钥交换,文件签名校验。
证书如此关键,但证书文件通过文本打开看的话,一般要么采用Der格式的二进制文件,也或Pem格式(base64加密Der格式后添加首位行标示的文件)。是无法看出什么有用的信息的。
那证书如何解析呢?
证书真实包含了那些信息,是否有隐藏的信息?
我们自己去写程序解析看看就能看出相关的真实情况。
证书Certificates file内容:
cert文件格式通常采用x.509 standard,包含公钥&加密算法,身份信息,证书机构签名或自签名。
An X.509 certificate contains a public key and an identity (a hostname, or an organization, or an individual), and is either signed by a certificate authority or self-signed.
cert文件包含的信息内容格式:
- Certificate
- Version Number
- Serial Number
- Signature Algorithm ID
- Issuer Name
- Validity period
- Not Before
- Not After
- Subject name
- Subject Public Key Info
- Public Key Algorithm
- Subject Public Key
- Issuer Unique Identifier (optional)
- Subject Unique Identifier (optional)
- Extensions (optional)
- ...
- Certificate Signature Algorithm
- Certificate Signature
证书Certificates file存储格式:
证书存储格式,常见的两种格式:Der格式与Pem格式:
Der格式为二进制格式;
Pem格式为Base64编码的Der格式并添加"-----BEGIN CERTIFICATE-----" 和 "-----END CERTIFICATE-----"。
There are several commonly used filename extensions for X.509 certificates.
.cer, .crt, .der – usually in binary DER form, but Base64-encoded certificates are common too (see .pem above)
.pem – (Privacy-enhanced Electronic Mail) Base64 encoded DER certificate, enclosed between "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----"
Der格式的全称为:Distinguished Encoding Rules,是著名的Asn.1(Abstract Syntax Notation One)编码规则之一,格式为:
二进制:类型,长度,数据,(可选存在的结束符)
The encoding of data does generally consist of four components which appear in the following order:
Identifier octets Type | Length octets Length | Contents octets Value | End-of-contents octets |
The End-of-contents octets are optional and only used if the indefinite length form is used. The Contents octet may also be omitted if there is no content to encode like in the NULL type.
length可能是1个字节,也可能是81+1字节,82+2字节,83+3字节,84+4字节,规则如下:
- any length from 0 up to 0x7F is encoded as one byte in
00
..7F
; - any higher length up to 0xFF is encoded as prefix
81
and one byte; - any higher length up to 0xFFFF is encoded as prefix
82
and two bytes; - any higher length up to 0xFFFFFF is encoded as prefix
83
and three bytes; - any higher length up to 0xFFFFFFFF is encoded as prefix
84
and four bytes;
例如:下面四个字节编码字符的解析结果
13 02 43 4E
13 -- Type=PrintableString
02 -- Length = 2
43 4E -- Data = CN
证书Certificates file解析实现:
“人生苦短,也用Python”,也采用Python解析实现,借助python的ansi库,解析Der或Pem格式的证书文件:
代码如下:
cert文件中采用的asn1编码是一层一层进行编码的,asn1每次解析一个编码组,采用递归解析是一个不错的选择。
(下面代码做了适配,也支持der/pem格式的scr和key文件的解析)
#!/usr/bin/python
#(Owed by: http://blog.csdn.net/chunyexiyu)
import asn1
import sys
import base64
import re
if (len(sys.argv) != 3):
print("Useage: {} certfile pem/der)".format(sys.argv[0]))
sys.exit(1)
def getTagDesc(tag):
if (tag.cls == 0x80):
return asn1.Classes(tag.cls).name + "[" + str(tag.nr) + "]"
try:
return asn1.Numbers(tag.nr).name + str(tag)
except:
return asn1.Types(tag.typ).name + str(tag)
der = ""
if (len(sys.argv) == 3 and sys.argv[2] == 'der'):
fd = open(sys.argv[1], 'rb')
der = fd.read()
fd.close()
else:
fd = open(sys.argv[1])
pem = fd.read()
fd.close()
pem = re.sub('[-]+[A-Z ]+[-]+\n','',pem)
der = base64.b64decode(pem)
def analyseAsn1(der, tab=''):
decoder = asn1.Decoder()
decoder.start(der)
while(True):
if (decoder.eof()):
break;
tag = decoder.peek()
if (tag.cls == 0x80): #context to octstring
tag = asn1.Tag(4, tag.typ, tag.cls)
tagReal, value = decoder.read(tag.nr)
else:
tagReal, value = decoder.read()
if (tag is None):
break;
print('\n{}{}:'.format(tab, getTagDesc(tagReal)), end='')
isSubDecode = False;
if (tag.typ != asn1.Types.Primitive.value): #constructed
isSubDecode = analyseAsn1(value, tab + " ")
elif (tag.cls == 0): #Universal
if (tag.nr == 4 and value[0] == 0x30): #ocstring
isSubDecode = analyseAsn1(value, tab + " ")
elif (tag.nr == 3 and value[0:2] == b'\x00\x30'): #binary string
isSubDecode = analyseAsn1(value[1:], tab + " ")
if (not isSubDecode):
if (tag.nr in (3,4)): #binarystring, octstring or big integer
print('0x{}'.format(value.hex()), end='')
elif (tag.nr == 2 and value.bit_length() > 64): #int
print('0x{}'.format(asn1.Encoder._encode_integer(value).hex()), end='')
elif (tag.nr == 0x1e): #unicodestring
print(value.decode('utf8'), end='')
elif (tag.nr == 0):
print('0x{}'.format(value.hex()), end='')
else:
print(value if (value) else '', end='')
return True;
analyseAsn1(der)
print("")
解析效果形如:
SequenceTag(nr=16, typ=32, cls=0):
SequenceTag(nr=16, typ=32, cls=0):
IntegerTag(nr=2, typ=0, cls=0):9912317898791003722
SequenceTag(nr=16, typ=32, cls=0):
ObjectIdentifierTag(nr=6, typ=0, cls=0):1.2.840.113549.1.1.11
NullTag(nr=5, typ=0, cls=0):
SequenceTag(nr=16, typ=32, cls=0):
SetTag(nr=17, typ=32, cls=0):
SequenceTag(nr=16, typ=32, cls=0):
ObjectIdentifierTag(nr=6, typ=0, cls=0):2.5.4.6
PrintableStringTag(nr=19, typ=0, cls=0):CN
...
其它解析实现:
如果不关注证书内部细节的话,也可以通过已有的工具来解析证书,查看证书中的内容:如下
通过openssl可以解析cert pem文件:
例如:openssl x509 -in cert.pem -noout -text
一些网站也提供了解析pem的实现,和自己实现效果类似:
例如:https://lapo.it/asn1js/
另外可以参考以下的这些文档,进一步对证书文件加深理解。
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
参考:https://docs.python.org/3/library/ssl.html
参考:https://media.readthedocs.org/pdf/python-asn1/latest/python-asn1.pdf
参考:https://stackoverflow.com/questions/16899247/how-can-i-decode-a-ssl-certificate-using-python
参考:https://pypi.org/project/asn1/
参考:https://en.wikipedia.org/wiki/Certificate_authority
参考:https://en.wikipedia.org/wiki/X.509
参考:https://github.com/andrivet/python-asn1/blob/master/src/asn1.py
参考:https://en.wikipedia.org/wiki/X.690#DER_encoding
参考:https://crypto.stackexchange.com/questions/21102/what-is-the-ssl-private-key-file-format