经过多次尝试,有些失败,有些部分成功,我找到了一种应该可以工作的方法(但没有使用自签名证书进行测试)。此外,我还清除了之前尝试的所有内容。
有两个必要步骤:
-
使用获取服务器证书
[Python 3.Docs]: (ssl.
get_server_certificate
(
addr, ssl_version=PROTOCOL_TLS, ca_certs=None
)
,它以
PEM公司
编码字符串(例如:我们的-
印刷精美
):
'-----BEGIN CERTIFICATE-----'
'MIIIPjCCByagAwIBAgIICG/ofYt2G48wDQYJKoZIhvcNAQELBQAwSTELMAkGA1UE`
'BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl'
...
'L2KuOvWZ40sTVCJdWPUMtT9VP7VHfLNTFft/IhR+bUPkr33xjOa0Idq6cL89oufn'
'-----END CERTIFICATE-----'
-
使用解码证书(
!!!未记录!!!
)
ssl._ssl._test_decode_cert
(存在于
python
3.
/
python
2.
)
-
由于以下事实:
ssl_ssl_测试\u解码\u证书
只能从文件中读取证书,需要另外两个步骤:
-
从保存证书
#1。
在临时文件中(之前
#2。
,显然)
-
完成后删除该文件
我想
强调
[Python 3.Docs]: SSLSocket.getpeercert(
binary_form=False
)
,其中包含大量信息(我上次错过了这些信息)。
还有,我发现
ssl_ssl_测试\u解码\u证书
,通过查看
SSLSocket.getpeercert
实施(
“${PYTHON\u SRC\u DIR}/Modules/\u ssl.c”
)。
代码00。py公司
:
#!/usr/bin/env python3
import sys
import os
import socket
import ssl
import itertools
def _get_tmp_cert_file_name(host, port):
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "_".join(("cert", host, str(port), str(os.getpid()), ".crt")))
def _decode_cert(cert_pem, tmp_cert_file_name):
#print(tmp_cert_file_name)
with open(tmp_cert_file_name, "w") as fout:
fout.write(cert_pem)
try:
return ssl._ssl._test_decode_cert(tmp_cert_file_name)
except Exception as e:
print("Error decoding certificate:", e)
return dict()
finally:
os.unlink(tmp_cert_file_name)
def get_srv_cert_0(host, port=443):
try:
cert_pem = ssl.get_server_certificate((host, port))
except Exception as e:
print("Error getting certificate:", e)
return dict()
tmp_cert_file_name = _get_tmp_cert_file_name(host, port)
return _decode_cert(cert_pem, tmp_cert_file_name)
def get_srv_cert_1(host, port=443):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
context = ssl.SSLContext()
ssl_sock = context.wrap_socket(sock, server_hostname=host)
try:
ssl_sock.connect((host, port))
except Exception as e:
print("Error connecting:\n", e)
return dict()
try:
cert_der = ssl_sock.getpeercert(True) # NOTE THE ARGUMENT!!!
except Exception as e:
print("Error getting cert:\n", e)
return dict()
tmp_cert_file_name = _get_tmp_cert_file_name(host, port)
return _decode_cert(ssl.DER_cert_to_PEM_cert(cert_der), tmp_cert_file_name)
def main(argv):
domain = "google.com"
if argv:
print("Using custom method")
get_srv_cert_func = get_srv_cert_1
else:
print("Using regular method")
get_srv_cert_func = get_srv_cert_0
cert = get_srv_cert_func(domain)
print("====== peer's certificate ======")
try:
print("Issued To:", dict(itertools.chain(*cert["subject"]))["commonName"])
print("Issued By:", dict(itertools.chain(*cert["issuer"]))["commonName"])
print("Valid From:", cert["notBefore"])
print("Valid To:", cert["notAfter"])
if (cert == None):
print("no certificate")
except Exception as e:
print("Error getting certificate:", e)
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main(sys.argv[1:])
注释
:
-
_获取\u tmp\u cert\u file\u名称
:生成将存储证书的临时文件名(与脚本位于同一目录中)
-
_解码\u证书
:将证书保存在文件中,然后解码文件并返回结果
字典
-
获取\u srv\u证书\u 0
:获取证书表单服务器,然后对其进行解码
-
获取\u srv\u证书\u 1
:与
获取\u srv\u证书\u 0
是,但“手动”
-
其优势在于控制
SSL
上下文创建/操作(我认为是
主要观点
问题的答案)
-
主要的
:
-
使用上述2个方法之一获取服务器证书(基于是否传递给脚本的参数)
-
打印证书数据(代码中有一些小的更正)
输出
:
(py35x64_test) e:\Work\Dev\StackOverflow\q050055935>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code00.py
Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
Using regular method
====== peer's certificate ======
Issued To: *.google.com
Issued By: Google Internet Authority G2
Valid From: Apr 10 18:58:05 2018 GMT
Valid To: Jul 3 18:33:00 2018 GMT
(py35x64_test) e:\Work\Dev\StackOverflow\q050055935>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code00.py 1
Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
Using custom method
====== peer's certificate ======
Issued To: *.google.com
Issued By: Google Internet Authority G2
Valid From: Apr 10 18:55:13 2018 GMT
Valid To: Jul 3 18:33:00 2018 GMT
检查
[SO]: How can I decode a SSL certificate using python? (@CristiFati's answer)
仅用于解码部分。