python实现ssl通信

SSL通信原理

SSL通信的本质就是客户端拿着认证机关的证书去认证对方网站到底是不是你要访问的。

客户端以后每次访问一个重要的网站时就要拿着发证机关CA(Cerficate Authority)的证书和对方连接的时候验证一下。

客户端连接的时候服务端也有证书,这个证书也是CA发给他们的,里面的内容进行了加密,只要拿CA发给客户的那个证书对接一些就知道对面网站的那个证书是不是真的。这就是SSL的基本逻辑。

同时通信的过程也是加密了的,这样就不太容易被窃听的人看明白(即中间人攻击)。

所以SSL等于解决了两个问题:

  1. 流量劫持

  2. 中间人攻击。

ssl协议的握手步骤:

  • 第1步,SSL客户机连接至SSL服务器,并要求服务器验证它自身的身份;

  • 第2步,服务器通过发送它的数字证书证明其身份。这个交换还可以包括整个证书链,直到某个根证书颁发机构(CA)。通过检查有效日期并确认证书包含可信任CA的数字签名来验证证书的有效性。

  • 第3步,服务器发出一个请求,对客户端的证书进行验证,但是由于缺乏公钥体系结构,当今的大多数服务器不进行客户端认证。

  • 第4步,协商用于加密的消息加密算法和用于完整性检查的哈希函数,通常由客户端提供它支持的所有算法列表,然后由服务器选择最强大的加密算法。

  • 第5步,客户机和服务器通过以下步骤生成会话密钥:

    客户机生成一个随机数,并使用服务器的公钥(从服务器证书中获取)对它加密,以送到服务器上。 服务器用更加随机的数据(客户机的密钥可用时则使用客户机密钥,否则以明文方式发送数据)响应。

SSL通信实现

证书生成

我这里使用的是kali自带的openssl,没有的可以去官网下载openssl,配置环境变量即可使用,官网:Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

# 1.先创建 CA 私钥,这个是用来生成ca.crt用的
openssl genrsa -out ca.key 2048
​
# 2.然后生成自签名CA证书,这个才是我们要用的东西。
# req X.509证书签发请求(CSR)管理
#   -new 新的请求
#   -x509 输出一个X509格式的证书 
#   -days X509证书的有效时间  
#   -key 用于签名待生成的请求证书的私钥文件
#   -subj 参数指定证书信息,避免在终端逐个输入
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=CN/ST=hunan/L=changsha/O=HNU/OU=csee/CN=CA_zino"
​
# 3.生成服务器私钥。注意这个跟刚才那个CA不是一回事了,相当于实际业务中的需要被认证的网站。
openssl genrsa -out server.key 2048
​
# 4.生成要颁发证书的证书签名请求
#127.0.0.1,服务端ip
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=hunan/L=changsha/O=HNU/OU=csee/CN=127.0.0.1"
​
# 5.用刚才第2步创建的 CA 证书给第4步生成的 签名请求 进行签名,表明该证书请求已被CA信任,得到一个被CA签名过的证书。
# x509 证书处理工具。可以用来显示证书的内容,转换其格式,给CSR签名等X.509证书的管理工作
#   -req 表明输入文件是一个"请求签发证书文件(CSR)",等待进行签发
#   -CA arg 指定用于签发请求证书的根CA证书 
#   -CAkey arg 指定用于签发请求证书的CA私钥证书文件,如果这个option没有参数输入,那么缺省认为私有密钥在CA证书文件里有
openssl x509 -req  -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
​
# 之后就是客户端用CA证书来对服务端被CA签名过的证书来进行认证
#用CA验证服务端证书是否可信
openssl  verify  -CAfile  ca.crt  server.crt
​
# 查看证书具体信息
openssl x509 -in server.crt -noout -text
​

服务端实现

import socket
import ssl
ip_port = ('127.0.0.1', 9999)
# 创建了一个 SSL上下文,ssl.PROTOCOL_TLS表示选择客户端和服务器均支持的最高协议版本
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
# 加载一个私钥及对应的证书
context.load_cert_chain(certfile="server.crt", keyfile="server.key")
​
sk = socket.socket()            # 创建套接字
sk.bind(ip_port)                # 绑定服务地址
sk.listen(5)                    # 监听连接请求
​
print('启动socket服务,等待客户端连接...')
​
connect_sock, address = sk.accept()     # 等待连接,此处自动阻塞
# 包装一个现有的 Python socket,并返回一个ssl socket,server_side为true表示为服务器行为,默认为false则表示客户端
ssl_connect_sock = context.wrap_socket(connect_sock, server_side=True)
while True:     # 一个死循环,直到客户端发送‘exit’的信号,才关闭连接
    client_data = ssl_connect_sock.recv(1024).decode()      # 接收信息
    if client_data == "exit":       # 判断是否退出连接
        sk.close()  # 关闭连接
        exit("通信结束")
    print("来自%s的客户端向你发来信息:%s" % (address, client_data))
    ssl_connect_sock.sendall('服务器已经收到你的信息'.encode())    # 回馈信息给客户端
​

客户端实现

import socket
import ssl
import pprint
# 创建了一个 SSL上下文,ssl.PROTOCOL_TLS表示选择客户端和服务器均支持的最高协议版本
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
# 设置模式为CERT_REQUIRED,在此模式下,需要从套接字连接的另一端获取证书;如果未提供证书或验证失败则将引发 SSLError。
context.verify_mode = ssl.CERT_REQUIRED
# 加载一组用于验证服务器证书的CA证书
context.load_verify_locations("ca.crt")
# 设置端口
ip_port = ('127.0.0.1', 9999)
# 创建套接字
s = socket.socket()
# 包装一个现有的 Python 套接字 sock 并返回一个 SSLContext.sslsocket_class 的实例 (默认为 SSLSocket)。
# 返回的 SSL 套接字会绑定上下文、设置以及证书
ssl_sock = context.wrap_socket(s, server_hostname='127.0.0.1')
​
# 连接服务器
ssl_sock.connect(ip_port)
# 输出证书信息
pprint.pprint(ssl_sock.getpeercert())
while True:     # 通过一个死循环不断接收用户输入,并发送给服务器
    inp = input("请输入要发送的信息: ").strip()
    if not inp:     # 防止输入空信息,导致异常退出
        continue
    ssl_sock.sendall(inp.encode())
​
    if inp == "exit":   # 如果输入的是‘exit’,表示断开连接
        print("结束通信!")
        break
​
    server_reply = ssl_sock.recv(1024).decode()
    print(server_reply)
s.close()    # 关闭连接
​

最后运行的时候记得把生成的ca证书以及服务器私钥保存到对应目录。

基于线程的实现

import threading
import socket
import ssl
import pprint
​
def client():
    # 创建了一个 SSL上下文,ssl.PROTOCOL_TLS表示选择客户端和服务器均支持的最高协议版本
    context = ssl.SSLContext(ssl.PROTOCOL_TLS)
    # 设置模式为CERT_REQUIRED,在此模式下,需要从套接字连接的另一端获取证书;如果未提供证书或验证失败则将引发 SSLError。
    context.verify_mode = ssl.CERT_REQUIRED
    # 加载一组用于验证其他对等方证书的CA证书
    context.load_verify_locations("ca.crt")
    # 设置端口
    ip_port = ('127.0.0.1', 9999)
    # 创建套接字
    s = socket.socket()
    # 包装一个现有的 Python 套接字 sock 并返回一个 SSLContext.sslsocket_class 的实例 (默认为 SSLSocket)。
    # 返回的 SSL 套接字会绑定上下文、设置以及证书
    ssl_sock = context.wrap_socket(s, server_hostname='127.0.0.1')
​
    # 连接服务器
    ssl_sock.connect(ip_port)
    # 输出证书信息
    print('#客户端消息#:客户端成功验证服务端证书,已成功连接,服务端证书信息如下')
    pprint.pprint(ssl_sock.getpeercert())
    while True:  # 通过一个死循环不断接收用户输入,并发送给服务器
        inp = input("#客户端消息#:请输入要发送的信息: ").strip()
        if not inp:  # 防止输入空信息,导致异常退出
            continue
        ssl_sock.sendall(inp.encode())
        if inp == "exit":  # 如果输入的是‘exit’,表示断开连接
            print("#客户端消息#:结束通信!")
            break
​
        server_reply = ssl_sock.recv(1024).decode()
        print("#客户端消息#:来自%s的服务端向你发来信息:%s" % (ip_port, server_reply))
    s.close()  # 关闭连接
​
​
def server():
    ip_port = ('127.0.0.1', 9999)
    # 创建了一个 SSL上下文,ssl.PROTOCOL_TLS表示选择客户端和服务器均支持的最高协议版本
    context = ssl.SSLContext(ssl.PROTOCOL_TLS)
    # 加载一个私钥及对应的证书
    context.load_cert_chain(certfile="server.crt", keyfile="server.key")
​
    sk = socket.socket()  # 创建套接字
    sk.bind(ip_port)  # 绑定服务地址
    sk.listen(5)  # 监听连接请求
​
    print('#服务端消息#:启动socket服务,等待客户端连接...')
​
    connect_sock, address = sk.accept()  # 等待连接,此处自动阻塞
    # 包装一个现有的 Python socket,并返回一个ssl socket,server_side为true表示为服务器行为,默认为false则表示客户端
    ssl_connect_sock = context.wrap_socket(connect_sock, server_side=True)
    while True:  # 一个死循环,直到客户端发送‘exit’的信号,才关闭连接
        client_data = ssl_connect_sock.recv(1024).decode()  # 接收信息
        # print(client_data)
        if client_data == "exit":  # 判断是否退出连接
            sk.close()  # 关闭连接
            print("#服务端消息#:客户端已结束通信!")
            exit("通信结束")
        print("#服务端消息#:来自%s的客户端向你发来信息:%s" % (address, client_data))
        ssl_connect_sock.sendall('服务器已经收到你的信息'.encode())  # 回馈信息给客户端
​
​
thread_server = threading.Thread(target=server)
thread_client = threading.Thread(target=client)
thread_server.start()
thread_client.start()
thread_server.join()
thread_client.join()
print("退出主线程")

openssl命令参考

1. openssl list-standard-commands(标准命令)
    1) asn1parse: asn1parse用于解释用ANS.1语法书写的语句(ASN一般用于定义语法的构成) 
    2) ca: ca用于CA的管理 
    openssl ca [options]:
        2.1) -selfsign
        使用对证书请求进行签名的密钥对来签发证书。即"自签名",这种情况发生在生成证书的客户端、签发证书的CA都是同一台机器(也是我们大多数实验中的情况),我们可以使用同一个
密钥对来进行"自签名"
        2.2) -in file
        需要进行处理的PEM格式的证书
        2.3) -out file
        处理结束后输出的证书文件
        2.4) -cert file
        用于签发的根CA证书
        2.5) -days arg 
        指定签发的证书的有效时间
        2.6) -keyfile arg   
        CA的私钥证书文件
        2.7) -keyform arg
        CA的根私钥证书文件格式:
            2.7.1) PEM
            2.7.2) ENGINE 
        2.8) -key arg   
        CA的根私钥证书文件的解密密码(如果加密了的话)
        2.9) -config file    
        配置文件
    example1: 利用CA证书签署请求证书
    openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key  
​
    3) req: X.509证书签发请求(CSR)管理
    openssl req [options] <infile >outfile
        3.1) -inform arg
        输入文件格式
            3.1.1) DER
            3.1.2) PEM
        3.2) -outform arg   
        输出文件格式
            3.2.1) DER
            3.2.2) PEM
        3.3) -in arg
        待处理文件
        3.4) -out arg
        待输出文件
        3.5) -passin        
        用于签名待生成的请求证书的私钥文件的解密密码
        3.6) -key file
        用于签名待生成的请求证书的私钥文件
        3.7) -keyform arg  
            3.7.1) DER
            3.7.2) NET
            3.7.3) PEM
        3.8) -new
        新的请求
        3.9) -x509          
        输出一个X509格式的证书 
        3.10) -days
        X509证书的有效时间  
        3.11) -newkey rsa:bits 
        生成一个bits长度的RSA私钥文件,用于签发  
        3.12) -[digest]
        HASH算法
            3.12.1) md5
            3.12.2) sha1
            3.12.3) md2
            3.12.4) mdc2
            3.12.5) md4
        3.13) -config file   
        指定openssl配置文件
        3.14) -text: text显示格式
    example1: 利用CA的RSA密钥创建一个自签署的CA证书(X.509结构) 
    openssl req -new -x509 -days 3650 -key server.key -out ca.crt 
    example2: 用server.key生成证书签署请求CSR(这个CSR用于之外发送待CA中心等待签发)
    openssl req -new -key server.key -out server.csr
    example3: 查看CSR的细节
    openssl req -noout -text -in server.csr
​
    4) genrsa: 生成RSA参数
    openssl genrsa [args] [numbits]
        [args]
        4.1) 对生成的私钥文件是否要使用加密算法进行对称加密:
            4.1.1) -des: CBC模式的DES加密
            4.1.2) -des3: CBC模式的DES加密
            4.1.3) -aes128: CBC模式的AES128加密
            4.1.4) -aes192: CBC模式的AES192加密
            4.1.5) -aes256: CBC模式的AES256加密
        4.2) -passout arg: arg为对称加密(des、des、aes)的密码(使用这个参数就省去了console交互提示输入密码的环节)
        4.3) -out file: 输出证书私钥文件
        [numbits]: 密钥长度
    example: 生成一个1024位的RSA私钥,并用DES加密(密码为1111),保存为server.key文件
    openssl genrsa -out server.key -passout pass:1111 -des3 1024 
​
    5) rsa: RSA数据管理
    openssl rsa [options] <infile >outfile
        5.1) -inform arg
        输入密钥文件格式:
            5.1.1) DER(ASN1)
            5.1.2) NET
            5.1.3) PEM(base64编码格式)
         5.2) -outform arg
         输出密钥文件格式
            5.2.1) DER
            5.2.2) NET
            5.2.3) PEM
        5.3) -in arg
        待处理密钥文件 
        5.4) -passin arg
        输入这个加密密钥文件的解密密钥(如果在生成这个密钥文件的时候,选择了加密算法了的话)
        5.5) -out arg
        待输出密钥文件
        5.6) -passout arg  
        如果希望输出的密钥文件继续使用加密算法的话则指定密码 
        5.7) -des: CBC模式的DES加密
        5.8) -des3: CBC模式的DES加密
        5.9) -aes128: CBC模式的AES128加密
        5.10) -aes192: CBC模式的AES192加密
        5.11) -aes256: CBC模式的AES256加密
        5.12) -text: 以text形式打印密钥key数据 
        5.13) -noout: 不打印密钥key数据 
        5.14) -pubin: 检查待处理文件是否为公钥文件
        5.15) -pubout: 输出公钥文件
    example1: 对私钥文件进行解密
    openssl rsa -in server.key -passin pass:111 -out server_nopass.key
    example:2: 利用私钥文件生成对应的公钥文件
    openssl rsa -in server.key -passin pass:111 -pubout -out server_public.key
​
    6) x509:
    本指令是一个功能很丰富的证书处理工具。可以用来显示证书的内容,转换其格式,给CSR签名等X.509证书的管理工作
    openssl x509 [args]    
        6.1) -inform arg
        待处理X509证书文件格式
            6.1.1) DER
            6.1.2) NET
            6.1.3) PEM
        6.2) -outform arg   
        待输出X509证书文件格式
            6.2.1) DER
            6.2.2) NET
            6.2.3) PEM
        6.3) -in arg 
        待处理X509证书文件
        6.4) -out arg       
        待输出X509证书文件
        6.5) -req            
        表明输入文件是一个"请求签发证书文件(CSR)",等待进行签发 
        6.6) -days arg       
        表明将要签发的证书的有效时间 
        6.7) -CA arg 
        指定用于签发请求证书的根CA证书 
        6.8) -CAform arg     
        根CA证书格式(默认是PEM) 
        6.9) -CAkey arg      
        指定用于签发请求证书的CA私钥证书文件,如果这个option没有参数输入,那么缺省认为私有密钥在CA证书文件里有
        6.10) -CAkeyform arg  
        指定根CA私钥证书文件格式(默认为PEM格式)
        6.11) -CAserial arg   
        指定序列号文件(serial number file)
        6.12) -CAcreateserial 
        如果序列号文件(serial number file)没有指定,则自动创建它     
    example1: 转换DER证书为PEM格式
    openssl x509 -in cert.cer -inform DER -outform PEM -out cert.pem
    example2: 使用根CA证书对"请求签发证书"进行签发,生成x509格式证书
    openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
    example3: 打印出证书的内容
    openssl x509 -in server.crt -noout -text 
​
    7) crl: crl是用于管理CRL列表 
    openssl crl [args]
        7.1) -inform arg
        输入文件的格式
            7.1.1) DER(DER编码的CRL对象)
            7.1.2) PEM(默认的格式)(base64编码的CRL对象)
        7.2) -outform arg
        指定文件的输出格式 
            7.2.1) DER(DER编码的CRL对象)
            7.2.2) PEM(默认的格式)(base64编码的CRL对象)
        7.3) -text: 
        以文本格式来打印CRL信息值。
        7.4) -in filename
        指定的输入文件名。默认为标准输入。
        7.5) -out filename
        指定的输出文件名。默认为标准输出。
        7.6) -hash
        输出颁发者信息值的哈希值。这一项可用于在文件中根据颁发者信息值的哈希值来查询CRL对象。
        7.7) -fingerprint
        打印CRL对象的标识。
        7.8) -issuer
        输出颁发者的信息值。
        7.9) -lastupdate
        输出上一次更新的时间。
        7.10) -nextupdate
        打印出下一次更新的时间。 
        7.11) -CAfile file
        指定CA文件,用来验证该CRL对象是否合法。 
        7.12) -verify
        是否验证证书。        
    example1: 输出CRL文件,包括(颁发者信息HASH值、上一次更新的时间、下一次更新的时间)
    openssl crl -in crl.crl -text -issuer -hash -lastupdate –nextupdate 
    example2: 将PEM格式的CRL文件转换为DER格式
    openssl crl -in crl.pem -outform DER -out crl.der  
​
    8) crl2pkcs7: 用于CRL和PKCS#7之间的转换 
    openssl crl2pkcs7 [options] <infile >outfile
    转换pem到spc
    openssl crl2pkcs7 -nocrl -certfile venus.pem -outform DER -out venus.spc
    https://www.openssl.org/docs/apps/crl2pkcs7.html
​
    9) pkcs12: PKCS#12数据的管理
    pkcs12文件工具,能生成和分析pkcs12文件。PKCS#12文件可以被用于多个项目,例如包含Netscape、 MSIE 和 MS Outlook
    openssl pkcs12 [options] 
    http://blog.csdn.net/as3luyuan123/article/details/16105475
    https://www.openssl.org/docs/apps/pkcs12.html
​
    10) pkcs7: PCKS#7数据的管理 
    用于处理DER或者PEM格式的pkcs#7文件
    openssl pkcs7 [options] <infile >outfile
    http://blog.csdn.net/as3luyuan123/article/details/16105407
    https://www.openssl.org/docs/apps/pkcs7.html
 
2. openssl list-message-digest-commands(消息摘要命令)
    1) dgst: dgst用于计算消息摘要 
    openssl dgst [args]
        1.1) -hex           
        以16进制形式输出摘要
        1.2) -binary        
        以二进制形式输出摘要
        1.3) -sign file    
        以私钥文件对生成的摘要进行签名
        1.4) -verify file    
        使用公钥文件对私钥签名过的摘要文件进行验证 
        1.5) -prverify file  
        以私钥文件对公钥签名过的摘要文件进行验证
        verify a signature using private key in file
        1.6) 加密处理
            1.6.1) -md5: MD5 
            1.6.2) -md4: MD4         
            1.6.3) -sha1: SHA1 
            1.6.4) -ripemd160
    example1: 用SHA1算法计算文件file.txt的哈西值,输出到stdout
    openssl dgst -sha1 file.txt
    example2: 用dss1算法验证file.txt的数字签名dsasign.bin,验证的private key为DSA算法产生的文件dsakey.pem
    openssl dgst -dss1 -prverify dsakey.pem -signature dsasign.bin file.txt
​
    2) sha1: 用于进行RSA处理
    openssl sha1 [args] 
        2.1) -sign file
        用于RSA算法的私钥文件 
        2.2) -out file
        输出文件爱你
        2.3) -hex   
        以16进制形式输出
        2.4) -binary
        以二进制形式输出  
    example1: 用SHA1算法计算文件file.txt的HASH值,输出到文件digest.txt
    openssl sha1 -out digest.txt file.txt
    example2: 用sha1算法为文件file.txt签名,输出到文件rsasign.bin,签名的private key为RSA算法产生的文件rsaprivate.pem
    openssl sha1 -sign rsaprivate.pem -out rsasign.bin file.txt
​
3. openssl list-cipher-commands (Cipher命令的列表)
    1) aes-128-cbc
    2) aes-128-ecb
    3) aes-192-cbc
    4) aes-192-ecb
    5) aes-256-cbc
    6) aes-256-ecb
    7) base64
    8) bf
    9) bf-cbc
    10) bf-cfb
    11) bf-ecb
    12) bf-ofb
    13) cast
    14) cast-cbc
    15) cast5-cbc
    16) cast5-cfb
    17) cast5-ecb
    18) cast5-ofb
    19) des
    20) des-cbc
    21) des-cfb
    22) des-ecb
    23) des-ede
    24) des-ede-cbc
    25) des-ede-cfb
    26) des-ede-ofb
    27) des-ede3
    28) des-ede3-cbc
    29) des-ede3-cfb
    30) des-ede3-ofb
    31) des-ofb
    32) des3
    33) desx
    34) rc2
    35) rc2-40-cbc
    36) rc2-64-cbc
    37) rc2-cbc
    38) rc2-cfb
    39) rc2-ecb
    40) rc2-ofb
    41) rc4
    42) rc4-40
​

参考链接

http://t.csdn.cn/K5PKz

openssl实现双向认证教程(服务端代码+客户端代码+证书生成) - 诸子流 - 博客园

ssl — TLS/SSL wrapper for socket objects — Python 3.11.0 documentation

Python3+ssl实现加密通信 - 诸子流 - 博客园

总结

实现过程踩了很多坑,其中借鉴了大佬的代码,在学习各种东西之前最重要的还是弄懂原理,然后很多问题就可以解决了,而且最最需要注意的就是细节,在理解的前提下才容易 发现细节。

只要不失去你的崇高,整个世界都会为你敞开

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜yesec

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值