通信人家园

标题: python关于SSL/TLS认证的实现  [查看完整版帖子] [打印本页]

时间:  2019-10-11 00:10
作者: sdxchinese     标题: python关于SSL/TLS认证的实现

[color=rgba(0, 0, 0, 0.75)]python关于SSL/TLS认证的实现
纯技术贴,不喜勿踩。




本文链接:https://blog.csdn.net/vip97yigang/article/details/84721027



最近有个客户端的需求是和服务端建立安全的链路,需要用ssl双向认证的方式实现。刚开始的时候被各种证书认证搞得晕乎乎-_-,花了好长时间才理清思路实现需求,所以写下这篇文章记录分享。先介绍下啥是SSL,后面给出demo源码。

参考

https://blog.csdn.net/wuliganggang/article/details/78428866
https://blog.csdn.net/duanbokan/article/details/50847612
https://blog.csdn.net/zhangtaoym/article/details/55259889

SSL/TSL简要介绍

SSL是安全套接层(secure sockets layer),而TLS是SSL的继任者,叫传输层安全(transport layer security)。是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。

比如如HTTP协议是明文传输,加上SSL层之后,就有了雅称HTTPS。它的发展依次经历了下面几个时期
SSL1.0: 已废除
SSL2.0: RFC6176,已废除
SSL3.0: RFC6101,基本废除
TLS1.0: RFC2246,目前大都采用此种方式
TLS1.1: RFC4346
TLS1.2: RFC5246,没有广泛使用
TLS1.3: RFC 8446,于2018年8月发表

流程

证书

CA: 证书授权中心( certificate authority)。 它呢,类似于国家出入境管理处一样,给别人颁发护照;也类似于国家工商管理局一样,给公司企业颁发营业执照。
它有两大主要性质:

SSL握手

SSL/TLS协商的过程可以参考这篇文章SSL/TLS协商过程详解

证书生成

makefile.sh

# * Redistributions in binary form must reproduce the above copyright#   notice, this list of conditions and the following disclaimer in the#   documentation and/or other materials provided with the distribution.# * Neither the name of the axTLS project nor the names of its#   contributors may be used to endorse or promote products derived#   from this software without specific prior written permission.## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.### Generate the certificates and keys for testing.#PROJECT_NAME="TLS Project"# Generate the openssl configuration files.cat > ca_cert.conf << EOF  [ req ]distinguished_name     = req_distinguished_nameprompt                 = no[ req_distinguished_name ] O                      = $PROJECT_NAME Dodgy Certificate AuthorityEOFcat > server_cert.conf << EOF  [ req ]distinguished_name     = req_distinguished_nameprompt                 = no[ req_distinguished_name ] O                      = $PROJECT_NAME CN                     = 192.168.111.100EOFcat > client_cert.conf << EOF  [ req ]distinguished_name     = req_distinguished_nameprompt                 = no[ req_distinguished_name ] O                      = $PROJECT_NAME Device Certificate CN                     = 192.168.111.101EOFmkdir camkdir servermkdir clientmkdir certDER# private key generationopenssl genrsa -out ca.key 1024openssl genrsa -out server.key 1024openssl genrsa -out client.key 1024# cert requestsopenssl req -out ca.req -key ca.key -new \            -config ./ca_cert.confopenssl req -out server.req -key server.key -new \            -config ./server_cert.conf openssl req -out client.req -key client.key -new \            -config ./client_cert.conf # generate the actual certs.openssl x509 -req -in ca.req -out ca.crt \            -sha1 -days 5000 -signkey ca.keyopenssl x509 -req -in server.req -out server.crt \            -sha1 -CAcreateserial -days 5000 \            -CA ca.crt -CAkey ca.keyopenssl x509 -req -in client.req -out client.crt \            -sha1 -CAcreateserial -days 5000 \            -CA ca.crt -CAkey ca.keyopenssl x509 -in ca.crt -outform DER -out ca.deropenssl x509 -in server.crt -outform DER -out server.deropenssl x509 -in client.crt -outform DER -out client.dermv ca.crt ca.key ca/mv server.crt server.key server/mv client.crt client.key client/mv ca.der server.der client.der certDER/rm *.confrm *.reqrm *.srl

做如下修改,终端执行。

实现单向认证

源码

server

from flask import Flaskapp = Flask(__name__)@app.route('/')def hello_world():    return 'Hello World!'if __name__ == '__main__':    app.run(debug=True, ssl_context=('server.crt', 'server.key'))

client

import urllib.requestimport sslif __name__ == '__main__':    CA_FILE = "ca.crt"    context = ssl.SSLContext(ssl.PROTOCOL_TLS)    context.check_hostname = False    context.load_verify_locations(CA_FILE)    context.verify_mode = ssl.CERT_REQUIRED    try:        request = urllib.request.Request('https://127.0.0.1:5000/')        res = urllib.request.urlopen(request, context=context)        print(res.code)        print(res.read().decode("utf-8"))    except Exception as ex:        print("Found Error in auth phase:%s" % str(ex))

https的测试本来使用使用浏览器,只要将ca证书安装到本地就可能使用浏览器访问,但是因为认证过程需要检测验证hostname,测试使用的自签名证书信息是随意填写的,所以即使安装了证书浏览器也会提示链接不安全。所以客户端使用ssl模块的load_verify_locations()方法加载根证书,并且将check_hostname设置为False。

双向认证

源码

https版本
server

from flask import Flaskimport sslapp = Flask(__name__)@app.route('/')def hello_world():    return 'Hello World!'if __name__ == '__main__':    CA_FILE = "ca.crt"    KEY_FILE = "server.key"    CERT_FILE = "server.crt"    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)    context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)    context.load_verify_locations(CA_FILE)    context.verify_mode = ssl.CERT_REQUIRED    app.run(debug=True, ssl_context=context)   

client

import urllib.requestimport sslif __name__ == '__main__':    CA_FILE = "ca.crt"    KEY_FILE = "client.key"    CERT_FILE = "client.crt"    context = ssl.SSLContext(ssl.PROTOCOL_TLS)    context.check_hostname = False    context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)    context.load_verify_locations(CA_FILE)    context.verify_mode = ssl.CERT_REQUIRED    try:        # 通过request()方法创建一个请求:        request = urllib.request.Request('https://127.0.0.1:5000/')        res = urllib.request.urlopen(request, context=context)        print(res.code)        print(res.read().decode("utf-8"))    except Exception as ex:        print("Found Error in auth phase:%s" % str(ex))

socket版本
server

import socketimport sslclass server_ssl:    def build_listen(self):        CA_FILE = "ca.crt"        KEY_FILE = "server.key"        CERT_FILE = "server.crt"        context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)        context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)        context.load_verify_locations(CA_FILE)        context.verify_mode = ssl.CERT_REQUIRED        # 监听端口        with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:            # 将socket打包成SSL socket            with context.wrap_socket(sock, server_side=True) as ssock:                ssock.bind(('127.0.0.1', 5678))                ssock.listen(5)                while True:                    # 接收客户端连接                    client_socket, addr = ssock.accept()                    # 接收客户端信息                    msg = client_socket.recv(1024).decode("utf-8")                    print(f"receive msg from client {addr}:{msg}")                    # 向客户端发送信息                    msg = f"yes , you have client_socketect with server.\r\n".encode("utf-8")                    client_socket.send(msg)                    client_socket.close()if __name__ == "__main__":    server = server_ssl()    server.build_listen()

client

import socketimport sslclass client_ssl:    def send_hello(self,):        CA_FILE = "ca.crt"        KEY_FILE = "client.key"        CERT_FILE = "client.crt"        context = ssl.SSLContext(ssl.PROTOCOL_TLS)        context.check_hostname = False        context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)        context.load_verify_locations(CA_FILE)        context.verify_mode = ssl.CERT_REQUIRED                # 与服务端建立socket连接        with socket.socket() as sock:            # 将socket打包成SSL socket            with context.wrap_socket(sock, server_side=False) as ssock:                ssock.connect(('127.0.0.1', 5678))                # 向服务端发送信息                msg = "do i connect with server ?".encode("utf-8")                ssock.send(msg)                # 接收服务端返回的信息                msg = ssock.recv(1024).decode("utf-8")                print(f"receive msg from server : {msg}")                ssock.close()if __name__ == "__main__":    client = client_ssl()    client.send_hello()

ssl认证主要用到python的ssl模块,如果有不清楚的也可以自己阅读下官方文档









通信人家园 (https://www.txrjy.com/) Powered by C114