如何使用 python 建立 TLS 连接?
How to make a TLS connection using python?
我想创建到服务器的 TLS 连接。然后,我想向服务器发送一些加密数据。我知道主机名和端口,并且有证书。令人惊讶的是,我还收到了服务器的私钥。但是我觉得收到私钥是不正常的。
第一个问题是,我真的需要私钥来建立 TLS 连接吗?
顺便说一下,我正在使用这个 python 脚本
import socket
import ssl
server_addr = '**.**.**.**'
server_port = ****
server_cert = 'server.crt'
server_key = 'server.key' # I use the private key
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain(certfile=server_cert, keyfile=server_key)
bindsocket = socket.socket()
bindsocket.connect((server_addr, server_port))
我在上面的脚本中使用了私钥。它没有任何错误。但是,当我尝试使用 bind() 而不是 connect() 时,即
bindsocket.bind((server_addr, server_port))
我收到以下错误:
OSError: [Errno 99] Cannot assign requested address
我看了很多关于上述错误的相关问题,但是,我仍然不明白为什么会这样。因为,我有主机名、端口、证书和密钥,我希望能成功创建 TLS 连接。
第二个问题是如何建立TLS连接?我的脚本正确吗?
非常感谢任何改进脚本的评论。
所以,首先,你绝对不能有私钥!顾名思义,它是私有的,不需要建立连接。
您可以拥有 public 密钥,但即使您使用标准 SSL 并且您信任签署服务器证书的 CA,也没有必要。
你确定,这是私钥?文件是否以 -----BEGIN PRIVATE KEY-----
开头?用 openssl rsa -noout -text -in server.key
.
检查
有关非对称加密的更多信息,请参阅 wikipedia article and this post。
一路走来:
使用 socket.bind()
可以将套接字绑定到本地计算机上的端口。这是不可能的,因为您的机器没有地址(您提供服务器地址)。
从您的代码来看,您似乎正试图将套接字作为服务器打开。为此,您将需要私钥,但随后您将接受连接,而不是自己连接到其他机器。我有一种感觉,你在这里混淆了两件事。
参考python documentation of socket.bind()
and to this question,因为这似乎是密切相关的。
另请查看 python documentation on ssl。我举了这个例子,它满足了您从上述文档中要求的要求:
import socket, ssl, pprint
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# require a certificate from the server
ssl_sock = ssl.wrap_socket(s,
ca_certs="/etc/ca_certs_file",
cert_reqs=ssl.CERT_REQUIRED)
ssl_sock.connect(('www.verisign.com', 443))
pprint.pprint(ssl_sock.getpeercert())
# note that closing the SSLSocket will also close the underlying socket
ssl_sock.close()
也可以看看 example on how to open a SSL socket in server mode。
进一步思考:
您真的需要自己完成所有 TLS 工作吗?例如,如果服务器使用 HTTPS(SSL 加密的 HTTP),您可以只使用 http.client 库。
如果您需要我澄清一些事情,请随时询问。我会相应地更新我的答案。
编辑:
正如您所指出的,您想在服务器模式下打开一个端口,我为您做了一个示例(它很大程度上依赖于 python 文档示例):
import socket, ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain(certfile="cert.pem", keyfile="key.pem")
bindsocket = socket.socket()
bindsocket.bind(('127.0.0.1', 10023))
bindsocket.listen(5)
def deal_with_client(connstream):
data = connstream.recv(1024)
# empty data means the client is finished with us
while data:
print(data)
data = connstream.recv(1024)
while True:
newsocket, fromaddr = bindsocket.accept()
connstream = context.wrap_socket(newsocket, server_side=True)
try:
deal_with_client(connstream)
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
运行它:
% python3 ssltest.py
b'hello server!\n'
b'this is data\n'
客户端:
% openssl s_client -connect 127.0.0.1:10023
CONNECTED(00000005)
depth=0 C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
verify error:num=18:self signed certificate
verify return:1
depth=0 C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
verify return:1
---
Certificate chain
0 s:C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
i:C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
---
Server certificate
-----BEGIN CERTIFICATE-----
... certificate ...
-----END CERTIFICATE-----
subject=C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
issuer=C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2272 bytes and written 404 bytes
Verification error: self signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
... session stuff ...
Extended master secret: yes
---
hello server!
this is data
^C
如果你使用像 HTTP 这样的常用协议,你应该使用一个库。这是一个烧瓶的例子:
>>> from flask import Flask
>>> app = Flask(__name__)
>>>
>>> @app.route("/")
... def hello():
... return "Hello World!"
...
>>> if __name__ == "__main__":
... app.run(ssl_context=('cert.pem', 'key.pem'))
...
* Serving Flask app "__main__" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on https://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [06/Aug/2020 11:45:50] "GET / HTTP/1.1" 200 -
我想创建到服务器的 TLS 连接。然后,我想向服务器发送一些加密数据。我知道主机名和端口,并且有证书。令人惊讶的是,我还收到了服务器的私钥。但是我觉得收到私钥是不正常的。
第一个问题是,我真的需要私钥来建立 TLS 连接吗?
顺便说一下,我正在使用这个 python 脚本
import socket
import ssl
server_addr = '**.**.**.**'
server_port = ****
server_cert = 'server.crt'
server_key = 'server.key' # I use the private key
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain(certfile=server_cert, keyfile=server_key)
bindsocket = socket.socket()
bindsocket.connect((server_addr, server_port))
我在上面的脚本中使用了私钥。它没有任何错误。但是,当我尝试使用 bind() 而不是 connect() 时,即
bindsocket.bind((server_addr, server_port))
我收到以下错误:
OSError: [Errno 99] Cannot assign requested address
我看了很多关于上述错误的相关问题,但是,我仍然不明白为什么会这样。因为,我有主机名、端口、证书和密钥,我希望能成功创建 TLS 连接。
第二个问题是如何建立TLS连接?我的脚本正确吗?
非常感谢任何改进脚本的评论。
所以,首先,你绝对不能有私钥!顾名思义,它是私有的,不需要建立连接。
您可以拥有 public 密钥,但即使您使用标准 SSL 并且您信任签署服务器证书的 CA,也没有必要。
你确定,这是私钥?文件是否以 -----BEGIN PRIVATE KEY-----
开头?用 openssl rsa -noout -text -in server.key
.
检查
有关非对称加密的更多信息,请参阅 wikipedia article and this post。
一路走来:
使用 socket.bind()
可以将套接字绑定到本地计算机上的端口。这是不可能的,因为您的机器没有地址(您提供服务器地址)。
从您的代码来看,您似乎正试图将套接字作为服务器打开。为此,您将需要私钥,但随后您将接受连接,而不是自己连接到其他机器。我有一种感觉,你在这里混淆了两件事。
参考python documentation of socket.bind()
and to this question,因为这似乎是密切相关的。
另请查看 python documentation on ssl。我举了这个例子,它满足了您从上述文档中要求的要求:
import socket, ssl, pprint
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# require a certificate from the server
ssl_sock = ssl.wrap_socket(s,
ca_certs="/etc/ca_certs_file",
cert_reqs=ssl.CERT_REQUIRED)
ssl_sock.connect(('www.verisign.com', 443))
pprint.pprint(ssl_sock.getpeercert())
# note that closing the SSLSocket will also close the underlying socket
ssl_sock.close()
也可以看看 example on how to open a SSL socket in server mode。
进一步思考:
您真的需要自己完成所有 TLS 工作吗?例如,如果服务器使用 HTTPS(SSL 加密的 HTTP),您可以只使用 http.client 库。
如果您需要我澄清一些事情,请随时询问。我会相应地更新我的答案。
编辑:
正如您所指出的,您想在服务器模式下打开一个端口,我为您做了一个示例(它很大程度上依赖于 python 文档示例):
import socket, ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain(certfile="cert.pem", keyfile="key.pem")
bindsocket = socket.socket()
bindsocket.bind(('127.0.0.1', 10023))
bindsocket.listen(5)
def deal_with_client(connstream):
data = connstream.recv(1024)
# empty data means the client is finished with us
while data:
print(data)
data = connstream.recv(1024)
while True:
newsocket, fromaddr = bindsocket.accept()
connstream = context.wrap_socket(newsocket, server_side=True)
try:
deal_with_client(connstream)
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
运行它:
% python3 ssltest.py
b'hello server!\n'
b'this is data\n'
客户端:
% openssl s_client -connect 127.0.0.1:10023
CONNECTED(00000005)
depth=0 C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
verify error:num=18:self signed certificate
verify return:1
depth=0 C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
verify return:1
---
Certificate chain
0 s:C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
i:C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
---
Server certificate
-----BEGIN CERTIFICATE-----
... certificate ...
-----END CERTIFICATE-----
subject=C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
issuer=C = SE, ST = Some-State, O = Internet Widgits Pty Ltd
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2272 bytes and written 404 bytes
Verification error: self signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
... session stuff ...
Extended master secret: yes
---
hello server!
this is data
^C
如果你使用像 HTTP 这样的常用协议,你应该使用一个库。这是一个烧瓶的例子:
>>> from flask import Flask
>>> app = Flask(__name__)
>>>
>>> @app.route("/")
... def hello():
... return "Hello World!"
...
>>> if __name__ == "__main__":
... app.run(ssl_context=('cert.pem', 'key.pem'))
...
* Serving Flask app "__main__" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on https://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [06/Aug/2020 11:45:50] "GET / HTTP/1.1" 200 -