问题描述
我是Windows用户.我使用Python 3.6.5
,并且导入了此版本的OpenSSL OpenSSL 1.0.2k
.
I am a Windows user. I use Python 3.6.5
and I import this version of OpenSSL OpenSSL 1.0.2k
.
我需要为python TLS客户端编写一个脚本,该脚本可以根据受支持的TLS版本和密码套件以及其他配置进行自定义.客户端应该能够使用自签名证书进行连接.因此,我相信我应该使用:ssl.SSLContext()
创建上下文而不是ssl.create_default_context()
.
I need to write a script for a python TLS client that I can customize in terms of the supported TLS versions and ciphersuites and other configurations. The client should be able to make connections with self-signed certificates. Therefore, I believe I should use: ssl.SSLContext()
to create my context and not ssl.create_default_context()
.
但是,使用以下脚本,我将永远无法获得对等方的证书.请用代码提供明确的答案,否则我将尝试许多解决方案,并且毫无希望地看了以前的帖子.
However, with the following script, I can never get the peer's certificate. Please, provide clear answers with code as otherwise I tried many solutions and looked at previous posts with no hope.
context = ssl.SSLContext() # ssl.create_default_context()
#context.verify_mode = ssl.CERT_NONE
#context.check_hostname = True
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
domain="google.com"
ssl_sock = context.wrap_socket(s, server_hostname=domain)
ssl_sock.connect((domain, 443))
print("====== peer's certificate ======")
try:
cert = ssl_sock.getpeercert()
print ("issued to:", dict(itertools.chain(*cert["subject"]))["commonName"])
print ("issued by:", dict(itertools.chain(*cert["issuer"]))["commonName"])
print("issuance date:", cert["notBefore"])
print("expairy date: ", cert["notAfter"])
if (cert == None):
print("no certificate")
except Exception as e:
print("Error:",e)
ssl_sock.close()
问题是,当我使用ssl.SSLContext()
时,我没有收到对等方的证书,但是当我使用ssl.create_default_context()
时,则可以正确接收该证书.但是,我需要能够接收自签名证书(即未验证的证书),这就是为什么我必须使用ssl.SSLContext()
的原因.
The problem is that I do not receive the peer's certificate when I use ssl.SSLContext()
but when I use ssl.create_default_context()
it is received correctly. However, I need to be able to receive self-signed certificates (i.e. unverified certificates) that's why I have to use ssl.SSLContext()
.
感谢您发布解决方案.但是,即使没有经过验证(自签名),我也需要解析该证书.我信任此证书,并且需要其信息.我查看了几篇文章,包括这一篇.我做了这些步骤:
Thanks for the solution posted. But I need to parse the certificate even if it is not verified (self-signed). I trust this certificate and I need its info. I looked at several posts including this one. I did these steps:
- 我获取了服务器证书的
.pem
内容. - 我导航至:
C:\Python36\Lib\site-packages\certifi
- 我打开了放置在目录中的
cacert.pem
(第2步) - 我添加了服务器的cert .pem内容,其内容以
-----BEGIN CERTIFICATE-----
开头,以-----END CERTIFICATE-----
结尾
- I took the
.pem
content of my server's certificate. - I navigated to:
C:\Python36\Lib\site-packages\certifi
- I opened
cacert.pem
which is placed in the directory (step 2) - I added my server's cert .pem content which starts with:
-----BEGIN CERTIFICATE-----
and ends with-----END CERTIFICATE-----
我收到此错误:
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)
推荐答案
经过多次尝试,有的失败,有的部分成功,我找到了一种可行的方法(不过,没有使用自签名证书对其进行测试) .另外,我清除了先前尝试中的所有内容.
After a number of attempts, some failed, some partial successful, I found a way that should work (didn't test it with self signed certificates, though). Also, I wiped out everything from the previous attempts.
有2个必要步骤:
-
使用 [Python 3.Docs ]:(ssl. get_server_certificate ( addr,ssl_version = PROTOCOL_TLS,ca_certs = None ),将其作为 PEM 编码返回字符串(例如:我们的-精美印刷):
Get the server certificate using [Python 3.Docs]: (ssl.get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None), which returns it as a PEM encoded string (e.g.: ours - pretty printed):
'-----BEGIN CERTIFICATE-----'
'MIIIPjCCByagAwIBAgIICG/ofYt2G48wDQYJKoZIhvcNAQELBQAwSTELMAkGA1UE`
'BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl'
...
'L2KuOvWZ40sTVCJdWPUMtT9VP7VHfLNTFft/IhR+bUPkr33xjOa0Idq6cL89oufn'
'-----END CERTIFICATE-----'
使用( !!! undocumented !!! )对证书进行解码ssl._ssl._test_decode_cert
(出现在 Python 3 / Python 2 )
Decode the certificate using (!!!undocumented!!!) ssl._ssl._test_decode_cert
(present in Python 3 / Python 2)
- 将 #1. 中的证书保存在临时文件中(显然,在 #2. 之前)
- 完成后删除该文件
我想强调 [Python 3 [文档]:SSLSocket.getpeercert( binary_form = False ) ,其中包含很多信息(我上次错过了).
我还通过查看SSLSocket.getpeercert
实现("$ {PYTHON_SRC_DIR}/Modules/_ssl.c" )来了解ssl._ssl._test_decode_cert
.
I would like to emphasize [Python 3.Docs]: SSLSocket.getpeercert(binary_form=False), which contains lots of info (that I missed the last time(s)).
Also, I found out about ssl._ssl._test_decode_cert
, by looking at SSLSocket.getpeercert
implementation ("${PYTHON_SRC_DIR}/Modules/_ssl.c").
code00.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:])
注释:
- _get_tmp_cert_file_name :生成用于存储证书的临时文件名(与脚本位于同一目录中)
- _decode_cert :将证书保存在文件中,然后解码文件并返回生成的 dict
- get_srv_cert_0 :获取证书表格服务器,然后对其进行解码
- get_srv_cert_1 :与 get_srv_cert_0 相同,但手动"
- 它的优点是控制 SSL 上下文的创建/操作(我认为这是问题的要点)
- _get_tmp_cert_file_name: generates the temporary file name (located in the same dir as the script) that will store the certificate
- _decode_cert: saves the certificate in the file, then decodes the file and returns the resulting dict
- get_srv_cert_0: gets the certificate form server, then decodes it
- get_srv_cert_1: same thing that get_srv_cert_0 does, but "manually"
- Its advantage is controlling the SSL context creation / manipulation (which I think was the main point of the question)
- 使用上述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]:如何我使用python解码SSL证书? (@CristiFati的回答)仅用于解码部分.
这篇关于无法使用OpenSSL的ssl.SSLContext()在Python客户端中接收对等证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!