python tcp socket中实现SSL/TLS认证

news2024/11/19 5:41:08

SSL/TLS介绍

官话说SSL是安全套接层(secure sockets layer),TLS是SSL的继任者,叫传输层安全(transport layer security)。

说白点,就是在明文的上层和TCP层之间加上一层加密,这样就保证上层信息传输的安全。如HTTP协议是明文传输,加上SSL层之后,就有了雅称HTTPS。它存在的唯一目的就是保证上层通讯安全的一套机制。

传统的 TLS 握手过程:

单向认证和双向认证

SSL认证分为单向认证和双向认证,是在安全通信中使用的两种不同的身份验证方式,它们之间的主要区别在于身份验证的方向和安全性。

  1. 单向认证(One-Way Authentication)
    • 客户端认证服务器

      • 无需客户端拥有客户端证书,只需服务端拥有服务器证书。
      • 例如:浏览器通常会内置一组根证书,这些根证书由浏览器厂商或操作系统供应商预先安装在浏览器中。这组内置的根证书是被广泛信任的证书颁发机构(Certificate Authorities,CAs)签发的根证书,用于验证网站和服务的数字证书的可信性。
      • 当您访问一个使用HTTPS协议的网站时,您的浏览器会使用其内置的根证书来验证该网站的数字证书是否由受信任的CA签发。如果验证通过,浏览器将显示一个锁定图标或其他安全标志,表示连接是安全的。如果验证未通过,浏览器通常会发出警告,提醒您可能存在安全风险。
      • 因此,根证书在浏览器中起到了关键的作用,它们构成了安全通信的基础。浏览器定期更新其内置的根证书,以反映新的CA或吊销的证书,以确保持续的安全性和可信性。
    • 适用场景:单向认证适用于大多数Web浏览和服务器通信场景,一般情况下,在打开页面的时候没有提示数据加密插件的,属于单向认证,即浏览器只持有公钥

  1. 双向认证(Two-Way Authentication)

    • 服务器验证客户端和客户端验证服务器:双向认证中,服务器验证客户端的身份,同时客户端也验证服务器的身份。这意味着客户端和服务器都需要提供有效的证书以进行相互验证。

    • 更高的安全性:双向认证提供更高的安全性,因为它确保了通信的两端都是合法的,并且双方都可以互相验证。

    • 适用场景:双向认证通常在需要更高级别的安全性的场景中使用,例如金融交易、医疗保健、政府通信等,其中双方都需要互相验证以确保身份。类似于支付宝、银行的U顿支付之类的,会要求用户安装插件或驱动,属于双向验证。

总之,单向认证用于服务器验证客户端的情况,而双向认证要求双方都进行身份验证,提供更高级别的安全性。选择哪种认证方式取决于您的应用程序的特定需求和安全性要求。

生成自签名证书文件

数字证书一般由数字证书认证机构签发,证书包含了:

  • 公钥。
  • 证书拥有者身份信息。
  • 数字证书认证机构(发行者)信息。
  • 发行者对这份文件的数字签名及使用的算法。
  • 有效期。

生成 CA 根证书、服务器证书和客户端证书的步骤如下:

步骤 1:创建 CA 根密钥和证书

首先,您需要生成一个 CA 根密钥(私钥)和一个 CA 根证书。以下是一些基本的步骤:

  1. 创建 CA 根密钥 (ca-key.pem)。这是用于签署服务器和客户端证书请求的私钥。请确保保护好这个私钥文件,因为它是证书链的根。
openssl genpkey -algorithm RSA -out ca-key.pem
  1. 使用 CA 根密钥创建自签名的 CA 根证书 (ca-cert.pem)。
openssl req -new -x509 -key ca-key.pem -out ca-cert.pem -days 3650

在此过程中,您需要提供一些 CA 根证书的信息,如组织、单位、常用名等。

步骤 2:创建服务器证书请求和证书

接下来,您需要为服务器创建证书请求并签署服务器证书。

  1. 创建服务器密钥 (server-key.pem)。
openssl genpkey -algorithm RSA -out server-key.pem

  1. 使用服务器密钥创建证书请求 (server-csr.pem)。在这一步中,您需要提供服务器的信息,如主机名(通常是服务器的域名)。
openssl req -new -key server-key.pem -out server-csr.pem

(py3.8) root@localhost:/opt/lianhaifeng/test2# openssl req -new -key server-key.pem -out server-csr.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:fujian
Locality Name (eg, city) []:xiamen
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
  1. 使用 CA 根证书和私钥签署服务器证书请求,生成服务器证书 (server-cert.pem)。
openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -out server-cert.pem -CAcreateserial -days 3650
(py3.8) root@localhost:/opt/lianhaifeng/test2# openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -out server-cert.pem -CAcreateserial -days 3650
Signature ok
subject=C = CN, ST = fujian, L = xiamen, O = Internet Widgits Pty Ltd
Getting CA Private Key
  1. 验证生成的服务器证书,如果验证成功,它将不会输出任何错误消息。
openssl verify -CAfile ca-cert.pem client-cert.pem  # 验证证书链完整性
openssl x509 -text -noout -in server-cert.pem  # 验证证书属性

(py3.8) root@localhost:/opt/lianhaifeng/test2# openssl verify -CAfile ca-cert.pem server-cert.pem
server-cert.pem: OK

步骤 3:创建客户端证书请求和证书(可选)

如果您需要客户端证书以进行双向认证,则可以执行以下步骤:

  1. 创建客户端密钥 (client-key.pem)。
openssl genpkey -algorithm RSA -out client-key.pem

(py3.8) root@localhost:/opt/lianhaifeng/test2# openssl genpkey -algorithm RSA -out client-key.pem
....................................................................................................................................................................+++++
........+++++
  1. 使用客户端密钥创建证书请求 (client-csr.pem)。
openssl req -new -key client-key.pem -out client-csr.pem

(py3.8) root@localhost:/opt/lianhaifeng/test2# openssl req -new -key client-key.pem -out client-csr.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:fujian
Locality Name (eg, city) []:qz
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

  1. 使用 CA 根证书和私钥签署客户端证书请求,生成客户端证书 (client-cert.pem)。
openssl x509 -req -in client-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -out client-cert.pem -CAcreateserial -days 3650

注意: 在实际生产环境中,需要更多的安全性和细化控制。此示例仅提供了基本步骤和示例命令,具体的配置可能因您的环境和需求而有所不同。同时,请确保适当地保护和存储您的密钥和证书文件。

  1. 验证生成的服务器证书,如果验证成功,它将不会输出任何错误消息。
openssl verify -CAfile ca-cert.pem client-cert.pem  # 验证证书链完整性
openssl x509 -text -noout -in server-cert.pem  # 验证证书属性

python实现SSL单向认证

通过上面的介绍,我们已经了解了单向认证的一般概念及流程。

下面是一个简单的Python示例代码,演示了如何在客户端认证服务端证书:

ssl_server.py

import socket
import ssl
import threading

class server_ssl:
    def build_listen(self):
        # CA_FILE = "ca-cert.pem"
        KEY_FILE = "server-key.pem"
        CERT_FILE = "server-cert.pem"
        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_NONE  # 不需要客户端提供证书

        # 监听端口
        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', 10036))
                ssock.listen(5)
                print("Server is listening for connections...")

                while True:
                    # 接收客户端连接
                    client_socket, addr = ssock.accept()
                    print(f"Accepted connection from {addr}")

                    # 创建新线程来处理客户端请求
                    client_thread = threading.Thread(target=self.handle_client, args=(client_socket, addr))
                    client_thread.start()

    def handle_client(self, client_socket, addr):
        try:
            while True:
                # 接收客户端信息
                msg = client_socket.recv(1024).decode("utf-8")
                if not msg:
                    break  # 客户端断开连接

                print(f"Received message from client {addr}: {msg}")

                # 向客户端发送信息
                response = f"Received: {msg}".encode("utf-8")
                client_socket.send(response)
        except Exception as e:
            print(f"Error: {str(e)}")
        finally:
            client_socket.close()
            print("Connection closed")

if __name__ == "__main__":
    server = server_ssl()
    server.build_listen()


运行server端代码:

(py3.8) root@localhost:/opt/lianhaifeng# python ssl_server.py
Server is listening for connections...
Accepted connection from ('127.0.0.1', 49814)
Received message from client ('127.0.0.1', 49814): hello
Received message from client ('127.0.0.1', 49814): world

ssl_client.py

import socket
import ssl

class client_ssl:
    def send_hello(self):
        CA_FILE = "ca-cert.pem"
        # CLIENT_KEY_FILE = "client-key.pem"
        # CLIENT_CERT_FILE = "client-cert.pem"

        # 创建SSL上下文对象
        context = ssl.SSLContext(ssl.PROTOCOL_TLS)
        context.check_hostname = False
        # context.load_cert_chain(certfile=CLIENT_CERT_FILE, keyfile=CLIENT_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', 10036))

                while True:
                    # 输入要发送的消息
                    msg = input("Enter a message to send (or 'quit' to exit): ")
                    if msg.lower() == 'quit':
                        break

                    # 向服务端发送消息
                    ssock.send(msg.encode("utf-8"))

                    # 接收并打印服务端返回的消息
                    response = ssock.recv(1024).decode("utf-8")
                    print(f"Received message from the server: {response}")

if __name__ == "__main__":
    client = client_ssl()
    client.send_hello()


运行客户端代码:

(py3.8) root@localhost:/opt/lianhaifeng# python ssl_client.py
Enter a message to send (or 'quit' to exit): hello
Received message from the server: Received: hello
Enter a message to send (or 'quit' to exit): world
Received message from the server: Received: world
Enter a message to send (or 'quit' to exit):

python实现SSL双向认证

下面是一个简单的Python示例代码,演示了如何在服务器和客户端之间进行双向认证:

服务器端代码 server.py

import socket
import ssl
import threading

class server_ssl:
    def build_listen(self):
        CA_FILE = "ca-cert.pem"
        KEY_FILE = "server-key.pem"
        CERT_FILE = "server-cert.pem"
        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  # 如果服务器不验证客户端证书:ssl.CERT_NONE 
        context.check_hostname = False

        # 监听端口
        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', 10028))
                ssock.listen(5)
                print("Server is listening for connections...")

                while True:
                    # 接收客户端连接
                    client_socket, addr = ssock.accept()
                    print(f"Accepted connection from {addr}")

                    # 创建新线程来处理客户端请求
                    client_thread = threading.Thread(target=self.handle_client, args=(client_socket, addr))
                    client_thread.start()

    def handle_client(self, client_socket, addr):
        try:
            while True:
                # 接收客户端信息
                msg = client_socket.recv(1024).decode("utf-8")
                if not msg:
                    break  # 客户端断开连接

                print(f"Received message from client {addr}: {msg}")

                # 向客户端发送信息
                response = f"Received: {msg}".encode("utf-8")
                client_socket.send(response)
        except Exception as e:
            print(f"Error: {str(e)}")
        finally:
            client_socket.close()
            print("Connection closed")

if __name__ == "__main__":
    server = server_ssl()
    server.build_listen()


(py3.8) root@localhost:/opt/lianhaifeng/test2# python server.py
Server is listening for connections...
Accepted connection from ('127.0.0.1', 55364)
Received message from client ('127.0.0.1', 55364): hello
Received message from client ('127.0.0.1', 55364): haige1
Accepted connection from ('127.0.0.1', 55366)
Received message from client ('127.0.0.1', 55366): hello
Received message from client ('127.0.0.1', 55366): haige2

客户端代码 client.py

import socket
import ssl

class client_ssl:
    def send_hello(self):
        CA_FILE = "ca-cert.pem"
        SERVER_CERT_FILE = "server-cert.pem"  # 服务器证书文件路径
        CLIENT_KEY_FILE = "client-key.pem"
        CLIENT_CERT_FILE = "client-cert.pem"

        # 创建SSL上下文对象
        context = ssl.SSLContext(ssl.PROTOCOL_TLS)
        context.check_hostname = False
        context.load_cert_chain(certfile=CLIENT_CERT_FILE, keyfile=CLIENT_KEY_FILE)
        context.load_verify_locations(CA_FILE)
        context.verify_mode = ssl.CERT_REQUIRED
        context.load_verify_locations(cafile=CA_FILE)  # 设置根证书

        # 与服务端建立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', 10026))

                while True:
                    # 输入要发送的消息
                    msg = input("Enter a message to send (or 'quit' to exit): ")
                    if msg.lower() == 'quit':
                        break

                    # 向服务端发送消息
                    ssock.send(msg.encode("utf-8"))

                    # 接收并打印服务端返回的消息
                    response = ssock.recv(1024).decode("utf-8")
                    print(f"Received message from the server: {response}")

if __name__ == "__main__":
    client = client_ssl()
    client.send_hello()

客户端1

(py3.8) root@localhost:/opt/lianhaifeng/test2# python client.py
Enter a message to send (or 'quit' to exit): hello
Received message from the server: Received: hello
Enter a message to send (or 'quit' to exit): haige1
Received message from the server: Received: haige1
Enter a message to send (or 'quit' to exit):

客户端2

(py3.8) root@localhost:/opt/lianhaifeng/test2# python client.py
Enter a message to send (or 'quit' to exit): hello
Received message from the server: Received: hello
Enter a message to send (or 'quit' to exit): haige2
Received message from the server: Received: haige2
Enter a message to send (or 'quit' to exit):

若是服务端要求验证客户端证书,但是客户端没有携带证书,则会引发一个常见的网络错误:ConnectionResetError: [Errno 104] Connection reset by peer,此时在服务器端也会有详细信息:ssl.SSLError: [SSL: PEER_DID_NOT_RETURN_A_CERTIFICATE] peer did not return a certificate (_ssl.c:1131)

如果本篇文章对你所有帮助,欢迎转发、点赞、收藏、在看,非常感谢。

参考

https://www.jianshu.com/p/ffcf1b765d76
https://blog.csdn.net/wuliganggang/article/details/78428866
https://blog.csdn.net/zhangtaoym/article/details/55259889
https://cloud.tencent.com/document/product/214/54254


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1385963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

get_reg_by_offset函数

get_reg_by_offset函数如下: 在建立了寄存器模型后,可以直接通过层次引用的方式访问寄存器: rm.invert.read(...); 但是出于某些原因,如果依然要使用地址来访问寄存器模型,那么此时可以使用get_reg_by_offset函数通过…

载誉前行!科士达新能源荣获光能杯卓越解决方案奖项

1月10日,由索比光伏网主办的“光能杯”光伏行业颁奖典礼在苏州隆重举行,科士达新能源凭借深厚的技术实力、强大的创新力、优质的产品及解决方案、丰富的项目经验等,斩获“卓越光储充一体化解决方案“殊荣。 作为智慧能源领域全场景解决方案供…

IDEA创建springboot+mybatis项目(java8 和java21可行)

IDEA创建springbootmybatis项目(java8 和java21可行) 今天博主讲一下,IDEA创建springbootmybatis项目的文章。 步骤分别是如下几步: 1. 创建maven项目 2. 配置pom.xml文件 3. 创建目录结构 4. 创建配置项目文件 5. 生成创建…

debug之pycharm调试:出现Collecting data......

pycharm调试时,出现Collecting data… 一直在这个界面很久,这是新版本的Pycharm的bug,通常在多线程的情况下发生。 解决方法: File->Setting->Build,Execution,Deployment->Python Debugger。把Gevent compatible勾选…

Scrapy框架自学

配置国内镜像源 # pip设置配置 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip config set trusted-host pypi.tuna.tsinghua.edu.cn创建虚拟环境 # 使用conda创建虚拟环境(具体内容请参考课件) conda create -n py_s…

基于ssm的社区流浪动物救助领养系统的设计与开发+vue论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理,然而,随着近些年信息技术的迅猛发展,让许多比较老套的信息管理模式进行了更新迭代,流浪动物信息因为其管理内容繁杂,管理数量繁多导致手工进行处理不能满足…

D1302 高性能、低功耗并附带RAM的涓流充电实时时钟电路芯片

D1302是一块高性能、低功耗并附带RAM的涓流充电实时时钟电路。 主要特点: ● 实时时钟可提供秒、 分、小时、星期、日期、月 份和年等计时数据,到2100 年止。一个月小于31天时可自动调整,且具有闰年补偿功能 ● 31X8 位通用暂存RAM ● 采用…

下一代 Vue3 Devtools 正式开源

什么是 Vue DevTools Vue DevTools 是一个旨在增强 Vue 开发人员体验的工具,它提供了一些功能来帮助开发者更好地了解 Vue 应用程序。 Vue DevTools:Unleash Vue Developer Experience. Enhance your Vue development journey with an amazing experience! 典型的功能特征包…

艾瑞报告:HR数字化需关注体系化能力,红海云等标杆厂商引领一体化趋势

新全球化时代背景下,企业经营所面临的国内外环境的不确定性增强,如何从不确定性中找到确定性成了大多数企业的关注要点。近日,艾瑞咨询发布《2023中国人力资源数字化研究报告》,从数字化转型的角度切入,探讨数字化如何…

山海鲸:助力企业实现内外数据整合与价值挖掘

作为山海鲸的开发者,我们深知数字化转型对于企业发展的重要性。在不断钻研如何提升山海鲸可视化这款免费产品的实用性同时,也在不断推出各行实用解决方案,本文将介绍山海鲸企业数字化转型发展解决方案,探讨如何通过数据驱动创新&a…

移动端开发进阶之蓝牙通讯(一)

移动端开发进阶之蓝牙通讯(一) 移动端进阶之蓝牙通讯需要综合考虑蓝牙版本选择、协议栈使用、服务匹配、设备连接、安全性和硬件支持等方面。 一、蓝牙版本选择 根据实际需求和应用场景选择合适的蓝牙版本; 1.0,1M/s。 2.0EDR…

目标识别跟踪模块Tofu3

Tofu系列提供了适应不同目标、不同速率的识别跟踪模块产品系列,主要包括Tofu3,4,5,S和其他零配件,可以适配BT.656,Cameralink,网络等不同接口和协议的热红外、可见光视频。 Tofu3 是多波段视频物体识别跟踪…

idea安装go

1.根据系统平台,下载安装Go: 知乎 - 安全中心 2.windows系统,下载安装MinGW(gcc): 知乎 - 安全中心 3.安装后cmd输入一下 go env 4.代理设置 go env -w GOPROXYhttps://goproxy.cn,direct 5.idea插件安装 file->setti…

龙蜥操作系统上安装MySQL:步骤详解与常见问题解决

目录 博客前言 一.下载MySQL 1.官网下载 2.上传文件到龙蜥操作系统中 ​编辑二.安装MySQL 1.检查操作系统中的默认数据库并移除 2.创建文件夹解压 3.开始安装 4.启动服务 ​编辑 5.登录修改密码,进行授权 三.第三方工具连接(naviact&#xff…

微软Office 2019 批量授权版

软件介绍 微软办公软件套件Microsoft Office 2019 专业增强版2024年1月批量许可版更新推送!Office2019正式版2018年10月份推出,主要为多人跨平台办公与团队协作打造。Office2019整合对过去三年在Office365里所有功能,包括对Word、Excel、Pow…

外部晶振、复位按键、唤醒按键、扩展排针原理图详解

前言:本文对外部晶振、复位按键、唤醒按键、扩展排针原理图详解。本文使用的MCU是GD32F103C8T6 目录 外部晶振原理图 复位按键、唤醒按键原理图 扩展排针部分原理图 ​外部晶振原理图 如下图,两个外部晶振,分别是8M(主晶振&a…

RK3568驱动指南|驱动基础进阶篇-进阶4 内核是如何运行ko文件的_insmod

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…

ruoyi后台管理系统部署-2-安装mysql

centos7 mysql 安装 1. 手动安装 安装 首先查看系统是否安装了: rpm -qa|grep mariadb rpm -qa | grep mysql systemctl status mysqld find / -name mysql.cnf卸载自带的 mariadb: rpm -e mariadb-libs-5.5.68-1.el7.x86_64 --nodeps去官网下载 mysql 安装包&…

Chapter 9 运算符重载

目的:运用运算符重载 🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓&…

OpenHarmony4.0适配LVDS屏幕驱动

1.概述 手头有一块RK3568的开发板OK3568-C,但是还没有适配OpenHarmony,用的还是LVDS屏幕,但是官方和网上好像还没有OpenHarmony4.0的LVDS屏幕驱动的通用实现,所以决定尝试了一下适配该开发板,完成LVDS屏幕驱动的适配&…