• python2.7建立https C/S交互 && 实现openssl enc加解密


    前言

      https是基于SSL/TLS的http协议,能够保证数据传输的安全性,避免如http报文传输过程中数据被劫持篡改的风险。

      本文基于python2的ssl库以及httplib库模拟https客户端以及服务端进行通信。

    SSL/TLS认证模式

    1. 双向认证:客户端与服务端互相认证,两者之间将会交换证书;
    2. 单向认证:客户端会认证服务器身份,而服务器不会对客户端身份进行验证

    SSL/TLS握手过程

    Client Server
    1.Client Hello  
     

    2.Server Hello

    3.Certificate

    4.(Server_Key_Exchange)

    5.(Certificate_Request)

    6.Server_Hello_Done

    7.(Certificate)

    8.Client_Key_Exchange

    9.(Certificate_Verify)

    10.Change_Cypher_Spec

    ----finished----

     
     

    11.Change_Cypher_Spec

    ----finished----

     

     

     

     

     

     

     

     

     

      1.Client Hello:

    (1)支持的协议版本,比如TLS 1.0

    (2)支持的加密算法(Cipher Specs)

    (3)客户端生成的随机数1(Challenge),稍后用于生成"对话密钥"。

      2.Server Hello

    (1) 确认使用的协议版本
    (2) 服务器生成的随机数2,稍后用于生成"对话密钥"
    (3) session id
    (4) 确认使用的加密算法

      3.Certificate

    服务器证书

      4.(Server_Key_Exchange)

    如果是DH算法,这里发送服务器使用的DH参数。RSA算法不需要这一步。

      5.(Certificate_Request)

    要求客户端提供证书,包括
    (1) 客户端可以提供的证书类型
    (2)服务器接受的证书distinguished name列表,可以是root CA或者subordinate CA。如果服务器配置了trust keystore, 这里会列出所有在trust keystore中的证书的distinguished name。

      6.Server_Hello_Done

    server hello结束

      7.(Certificate)

    客户端证书

      8.Client_Key_Exchange

    包含pre-master secret。客户端生成第三个随机数。如果是采用RSA算法,会生成一个48字节随机数,然后用server的公钥加密之后再放入报文中;如果是DH算法,这里发送的就是客户端的DH参数,之后服务器和客户端根据DH算法,各自计算出相同的pre-master secret。

      9.(Certificate_Verify)

    发送使用客户端证书给到这一步为止收到和发送的所有握手消息签名结果。

      10.Change_Cypher_Spec

    客户端通知服务器开始使用加密方式发送报文。客户端使用上面的3个随机数client random, server random, pre-master secret, 计算出48字节的master secret, 这个就是对称加密算法的密钥。

      finished

    客户端发送第一个加密报文。使用HMAC算法计算收到和发送的所有握手消息的摘要,然后通过RFC5246中定义的一个伪函数PRF计算出结果,加密后发送。

            11.Change_Cypher_Spec

            ----finished----

    服务器端发送change_cipher_specfinished消息。到这里握手结束

    HTTPS客户端

     1 import httplib
     2 import ssl
     3 import socket
     4 
     5 def start_client(ip_addr):
     6     print("启动https客户端......")
     7     ssl._create_default_https_context = ssl._create_unverified_context;#重要,忽略证书认证
     8     conn = httplib.HTTPSConnection(ip_addr) 
     9     print(str(conn.host) + str(conn.port))
    10     sock = socket.create_connection((conn.host, conn.port))
    11     conn.sock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv23)#忽略服务器证书认证
    12     print '33[0;31;40mIX success to connect to ' + ip_addr + '33[0m'
    13     #conn.sock = ssl.wrap_socket(sock, ca_certs='ca/rsa_ca.pem', cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv23)#验证模式
    14     return conn

    HTTPS服务端

     1 import ssl
     2 import hashlib
     3 from BaseHTTPServer import HTTPServer,BaseHTTPRequestHandler
     4 
     5 def start_server():
     6     print("启动https服务端......")
     7     ip_addr = get_server_role().split(':')
     8     ip = ip_addr[0]
     9     port = int(ip_addr[1])
    10     addr = (ip, port)
    11     context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    12     context.load_cert_chain("ca/rsa_local.pem","ca/rsa_local.key")
    13     server = HTTPServer(addr,RequestHandler)
    14     server.socket = context.wrap_socket(server.socket, server_side = True)
    15     sa = server.socket.getsockname()
    16     print("Serving HTTPS on %s:%d" % (sa[0],sa[1])) 
    17     server.serve_forever()

    加密算法(openssl enc加密)

      使用的加密算法为aes-256-cbc对称加密算法

      对应openssl对称加密算法命令:

    1. 加密:openssl enc -e -aes-256-cbc -base64 -pass password
    2. 解密:openssl enc -d -aes-256-cbc -base64 -pass password
    3. openssl enc相关参数:-iv IV、-K key、-nopad、-pass arg

      实现难点:如果需要实现的openssl enc命令指定的参数是-K、-iv,则可以生成需要的key或iv值直接用于加解密;由于需要实现的是指定-pass,所以需要根据pass生成key以及iv值;

    在C语言中openssl函数库提供函数EVP_BytesToKey(),通过pass生成对应的key以及iv值;python没有库(或者我不知道)提供该函数的功能,需要自己去实现;EVP_BytesToKey()实现

    原理是通过md5算法生成key、iv,另外通过阅读openssl源码得知,指定-salt时存在魔术字"Salted__";

      salt相关信息:https://blog.csdn.net/kkxgx/article/details/12879367(转)

     1 # -*- coding: utf-8 -*-
     2 """使用md5算法生成key和iv"""
     3 import base64
     4 from hashlib import md5
     5 from Crypto import Random
     6 from Crypto.Cipher import AES
     7 from binascii import b2a_hex, a2b_hex
     8 #相关源码文件enc.c
     9 #EVP_BytesToKey()函数的实现原理
    10 #如果未指定salt,下面改为d_i = md5(d_i + password).digest(),有关salt的都删除,根据实验,进行一次md5加密,将生成16位结果
    11 #md5加密原理 https://blog.csdn.net/weixin_34362790/article/details/87039959
    12 #hash1_128 = MD5(Passphrase+salt)
    13 #hash2_128 = MD5(hash1_128 + Passphrase + salt)
    14 #hash3_128 = MD5(hash2_128 + Passphrase + salt)
    15 #Key = hash1_128 + hash2_128
    16 #IV = hash3_128
    17 
    18 #根据pass参数生成key,iv
    19 def encode(s):
    20     return ' '.join([bin(ord(c)).replace('0b', '') for c in s])
    21 
    22 def derive_key_and_iv(password, salt, key_length, iv_length):
    23     d = d_i = ''
    24     while len(d) < key_length + iv_length:
    25         d_i = md5(d_i + password + salt).digest()
    26         d += d_i
    27     return d[:key_length], d[key_length:key_length+iv_length]
    28 
    29 def encrypt(data, password):
    30     bs = AES.block_size
    31     salt = Random.new().read(bs - len('Salted__'))
    32     key, iv = derive_key_and_iv(password, salt, 32, 16)
    33     #填充算法如下:重点chr(bs - len(s) % bs)
    34     pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
    35     cipher = AES.new(key, AES.MODE_CBC, iv)
    36     data = cipher.encrypt(pad(data))
    37     print('encrypt:len')
    38     print(len(data))
    39     #important:'Salted__' + salt不能有任何修改
    40     #在解密时-S指定的salt是无效的
    41     return base64.b64encode('Salted__' + salt + data)
    42     
    43 def decrypt(data, password):
    44     data = base64.b64decode(data)
    45     print(len(data))
    46     salt = data[8:16]
    47     data = data[16:]
    48     bs = AES.block_size
    49     key, iv = derive_key_and_iv(password, salt, 32, 16)
    50     print(key, iv)
    51     unpad = lambda s : s[0:-ord(s[-1])]
    52     cipher = AES.new(key, AES.MODE_CBC, iv)
    53     data = unpad(cipher.decrypt(data))
    54     return data

      

  • 相关阅读:
    puppeteer自动化测试系列之三---端对端测试中常用的 Puppeteer 操作
    puppeteer自动化测试系列之二---puppeteer常用方法
    团队作业8—团队项目用户验收评审
    Beta冲刺--第四天
    Beta冲刺--第三天
    Beta冲刺--第二天
    Beta冲刺--第一天
    Spring_Four -- 团队项目设计完善&编码测试
    Alpha冲刺
    团队作业6—《Spring_Four》团队项目系统设计改进与详细设计
  • 原文地址:https://www.cnblogs.com/taouu/p/13031958.html
Copyright © 2020-2023  润新知