如何使用python建立TLS连接? [英] How to make a TLS connection using python?

查看:81
本文介绍了如何使用python建立TLS连接?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个到服务器的 TLS 连接.然后,我想向服务器发送一些加密数据.我知道主机名和端口并且我有证书.令人惊讶的是,我还收到了服务器的私钥.不过我觉得收到私钥是不正常的.

第一个问题是,我真的需要私钥来建立 TLS 连接吗?

顺便说一句,我正在使用这个python脚本

导入套接字导入 sslserver_addr = '**.**.**.**'服务器端口 = ****server_cert = 'server.crt'server_key = 'server.key' # 我使用私钥上下文 = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)context.verify_mode = ssl.CERT_REQUIREDcontext.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] 无法分配请求的地址

我已经阅读了许多有关上述错误的相关问题,但是,我仍然不明白为什么会发生这种情况.因为我有主机名、端口、证书和密钥,所以我希望能够成功创建 TLS 连接.

第二个问题是如何建立TLS连接?我的脚本正确吗?

我非常感谢任何改进脚本的评论.

解决方案

所以,首先,你绝对不应该有私钥!顾名思义,它是私​​有的,不需要建立连接.
您可以拥有公钥,但只要您使用标准 SSL 并且您信任签署服务器证书的 CA,即使这也不是必需的.
你确定,这是私钥?文件是否以 -----BEGIN PRIVATE KEY----- 开头?检查 openssl rsa -noout -text -in server.key.
请参阅维基百科文章这篇文章 了解更多关于非对称加密的信息.

更进一步:
使用 socket.bind() 可以将套接字绑定到本地机器上的端口.这是不可能的,因为您的机器没有地址(您提供服务器地址).
从您的代码来看,您似乎正在尝试将套接字作为服务器打开.为此,您将需要私钥,但随后您将接受连接而不是自己连接到其他机器.我有一种感觉,你在这里混淆了两件事.
参考socket的python文档.bind()这个问题似乎有密切关系.
另请查看 关于 ssl 的 python 文档.我举了一个例子,它满足了您从上述文档中的要求:

导入socket、ssl、pprints = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 需要来自服务器的证书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())# 注意关闭 SSLSocket 也会关闭底层套接字ssl_sock.close()

另请查看示例,了解如何打开服务器模式下的 SSL 套接字.

进一步的想法:
你真的需要自己做所有 TLS 的事情吗?例如,如果服务器使用 HTTPS(SSL 加密的 HTTP),您可以只使用 http.client 库.

如果您需要我澄清一些事情,请随时提问.我会相应地更新我的答案.


正如你所指出的,你想在服务器模式下打开一个端口,我为你做了一个例子(它严重依赖于 python 文档示例):

导入套接字,ssl上下文 = 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):数据 = connstream.recv(1024)# 空数据表示客户端与我们完成而数据:打印(数据)数据 = connstream.recv(1024)为真:newsocket, fromaddr = bindsocket.accept()connstream = context.wrap_socket(newsocket, server_side=True)尝试:deal_with_client(connstream)最后:connstream.shutdown(socket.SHUT_RDWR)connstream.close()

运行:

% python3 ssltest.pyb'你好服务器!\n'b'这是数据\n'

客户端:

% openssl s_client -connect 127.0.0.1:10023已连接(00000005)depth=0 C = SE, ST = Some-State, O = Internet Widgits Pty Ltd验证错误:num=18:自签名证书验证返回:1depth=0 C = SE, ST = Some-State, O = Internet Widgits Pty Ltd验证返回:1---证书链0 s:C = SE,ST = 某个州,O = Internet Widgits Pty Ltdi:C = SE, ST = Some-State, O = Internet Widgits Pty Ltd---服务器证书-----开始认证-----... 证书 ...-----结束证书-----subject=C = SE, ST = Some-State, O = Internet Widgits Pty Ltdissuer=C = SE,ST = Some-State,O = Internet Widgits Pty Ltd---未发送客户端证书 CA 名称对等签名摘要:SHA256对等签名类型:RSA-PSS服务器临时密钥:X25519,253 位---SSL 握手已读取 2272 个字节并写入 404 个字节验证错误:自签名证书---新,TLSv1.2,密码是 ECDHE-RSA-AES256-GCM-SHA384服务器公钥为 4096 位支持安全重新协商压缩:无扩展:无没有协商 ALPNSSL-会话:协议:TLSv1.2密码:ECDHE-RSA-AES256-GCM-SHA384......会议的东西......扩展主密钥:是---你好服务器!这是数据^C

如果你使用一些常用的协议,比如 HTTP,你应该使用一个库.这是烧瓶的示例:

<预><代码>>>>从烧瓶进口烧瓶>>>app = Flask(__name__)>>>>>>@app.route("/")...定义你好():...返回Hello World!"...>>>如果 __name__ == __main__":... app.run(ssl_context=('cert.pem', 'key.pem'))...* 服务 Flask 应用程序__main__";(懒加载)* 环境:生产警告:这是一个开发服务器.不要在生产部署中使用它.改用生产 WSGI 服务器.* 调试模式:关闭* 在 https://127.0.0.1:5000/上运行(按 CTRL+C 退出)127.0.0.1 - - [06/Aug/2020 11:45:50] GET/HTTP/1.1"200 -

I would like to create a TLS connection to a server. Then, I want to send some encrypted data to the server. I know the hostname and port and I have the certificate. Surprisingly, I also received the private key of the server. However, I think it is not normal that I received the private key.

The first question is that, do I really need the private key to make a TLS connection?

By the way, I am using this python script

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))

I am using the private key in the above script. It works without any error. However, when I try to bind() instead of connect(), i.e.,

bindsocket.bind((server_addr, server_port))

I get the following error:

OSError: [Errno 99] Cannot assign requested address

I have read many related questions about the above error, however, I still do not understand why this happens. Since, I have the hostname, port, certificate, and the key, I expect to create a TLS connection successfully.

The second question is that how can I establish a TLS connection? Is my script correct?

I highly appreciate any comment to improve the script.

解决方案

So, first of all, you should absolutely not have the private key! As the name says, it is private and not necessary to establish a connection.
You could have the public key, but even that is not necessary as long as you use standard SSL and you trust the CA that signed the servers certificate.
Are you sure, it is the private key? Does the file begin with -----BEGIN PRIVATE KEY-----? Check with openssl rsa -noout -text -in server.key.
Refer to the wikipedia article and this post for more on asymmetric cryptography.

Further along the way:
With socket.bind() you bind a socket to a port on your local machine. This is not possible, as your machine does not have the address (you provide a server address).
From your code, it looks like you are trying to open the socket as a server. You will need the private key for that, but then you will be accepting connections and not connect to other machines yourself. I have a feeling, that you are mixing up two things here.
Refer to the python documentation of socket.bind() and to this question as this seems to be closely related.
Also check out the python documentation on ssl. I took the example, that does what you are asking for from said documentation:

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()

Also have a look on the example on how to open a SSL socket in server mode.

Further thoughts:
Do you really need to do all that TLS stuff yourself? If the server, for example, uses HTTPS (SSL encrypted HTTP), you can just use the http.client library.

Feel free to ask, if you need me to clarify something. I'll update my answer accordingly.

EDIT:
As you indicated, you want to open a port in server mode, I made an example for you (it heavily leans on the python documentation example):

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()

Running it:

% python3 ssltest.py
b'hello server!\n'
b'this is data\n'

The client side:

% 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

If you use some usual protocol like HTTP, you should use a library though. Here is a example with flask:

>>> 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 -

这篇关于如何使用python建立TLS连接?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆