Python-SecureHTTP¶
通过使用RSA+AES让HTTP传输更加安全,即C/S架构的加密通信! (Make HTTP transmissions more secure via RSA+AES, encrypted communication for C/S architecture.)
安装(Installation)¶
使用pip安装:
# 正式版(Release)
$ pip install -U SecureHTTP
# 开发版(Dev)
$ pip install -U git+https://github.com/staugur/Python-SecureHTTP.git@master
关于依赖库:
SecureHTTP依赖pycryptodomex。
PyCryptodome是PyCrypto的一个分支,它为PyCrypto的最后一个正式版本(2.6.1)带来了一些增强功能,如支持pypy。 PyCryptodomex即PyCryptodome,区别在于导入包名不同,前者导入包名是Cryptodome,后者是Crypto(同pycrypto)。
注意:v0.5.0开始,已经弃用PyCrypto/PyCryptodome!
测试用例(Test)¶
温馨提示:完整的测试要求安装php、go以便运行多语言测试
$ git clone https://github.com/staugur/Python-SecureHTTP && cd Python-SecureHTTP
$ make dev && make test
简单示例(Demo)¶
AES加密、解密
from SecureHTTP import AESEncrypt, AESDecrypt # 加密后的密文 ciphertext = AESEncrypt('ThisIsASecretKey', 'Hello World!', output="hex") # 解密后的明文 plaintext = AESDecrypt("ThisIsASecretKey", ciphertext, input="hex)
RSA加密、解密
from SecureHTTP import RSAEncrypt, RSADecrypt, generate_rsa_keys # 生成密钥对 (pubkey, privkey) = generate_rsa_keys(incall=True) # 加密后的密文 ciphertext = RSAEncrypt(pubkey, 'Hello World!') # 解密后的明文 plaintext = RSADecrypt(privkey, ciphertext)
C/S加解密示例: 点此查看以下模拟代码的真实WEB环境示例
# 模拟C/S请求 from SecureHTTP import EncryptedCommunicationClient, EncryptedCommunicationServer, generate_rsa_keys post = {u'a': 1, u'c': 3, u'b': 2, u'data': ["a", 1, None]} resp = {u'msg': None, u'code': 0} # 生成密钥对 (pubkey, privkey) = generate_rsa_keys(incall=True) # 初始化客户端类 client = EncryptedCommunicationClient(pubkey) # 初始化服务端类 server = EncryptedCommunicationServer(privkey) # NO.1 客户端加密数据 c1 = client.clientEncrypt(post) # NO.2 服务端解密数据 s1 = server.serverDecrypt(c1) # NO.3 服务端返回加密数据 s2 = server.serverEncrypt(resp) # NO.4 客户端获取返回数据并解密 c2 = client.clientDecrypt(s2) # 以上四个步骤即完成一次请求/响应
B/S加解密示例: 前端使用AES+RSA加密,后端解密
加密传输通信的流程(Encrypted Transmission Process)¶
总体流程:客户端上传数据加密 ==> 服务端获取数据解密 ==> 服务端返回数据加密 ==> 客户端获取数据解密
NO.1 客户端上传数据加密流程:
1. 客户端随机产生一个16位的字符串,用以之后AES加密的秘钥,AESKey。
2. 使用RSA对AESKey进行公钥加密,RSAKey。
3. 参数加签,参考"加签、验签规则流程"。
4. 将明文的要上传的数据包(字典/Map)转为Json字符串,使用AESKey加密,得到JsonAESEncryptedData。
5. 封装为{key : RSAKey, value : JsonAESEncryptedData}的字典上传服务器,服务器只需要通过key和value,然后解析,获取数据即可。
NO.2 服务端获取数据解密流程:
1. 获取到RSAKey后用服务器私钥解密,获取到AESKey
2. 获取到JsonAESEncriptedData,使用AESKey解密,得到明文的客户端上传上来的数据。
3. 验签,参考"加签、验签规则流程"
4. 返回明文数据
NO.3 服务端返回数据加密流程:
1. 将要返回给客户端的数据(字典/Map)进行加签并将签名附属到数据中
2. 上一步得到的数据转成Json字符串,用AESKey加密处理,记为AESEncryptedResponseData
3. 封装数据{data : AESEncryptedResponseData}的形式返回给客户端
NO.4 客户端获取数据解密流程:
1. 客户端获取到数据后通过key为data得到服务器返回的已经加密的数据AESEncryptedResponseData
2. 对AESEncryptedResponseData使用AESKey进行解密,得到明文服务器返回的数据。
加签、验签规则流程(Signature Rule)¶
@加签、验签规则:
加签,即
EncryptedCommunicationClient.clientEncrypt
和EncryptedCommunicationServer.serverEncrypt
方法,签名已经内置,支持传入signIndex
参数生成不同签名。验签,即
EncryptedCommunicationClient.clientDecrypt
和EncryptedCommunicationServer.serverDecrypt
方法,验签已经内置,验签失败触发SignError
错误。signIndex:
False, 不签名、不验签 None, 签名数据中所有字段(目前版本,如果嵌套了无序数据类型,可能会验签失败) str, 指定参与签名的字段,格式是"key1,key2",这是目前建议的一种方法,只针对部分核心字段签名和验签
@签名步骤:
构造规范化的请求字符串
按照字母升序,对参数名称进行排序。
排序后的参数以”参数名=值&”的形式连接,其中参数名和值要进行URL编码,使用UTF-8字符集,编码规则是:
2.1 对于字符 A-Z、a-z、0-9以及字符“-”、“_”、“.”、“~”不编码; 2.2 对于其他字符编码成“%XY”的格式,其中XY是字符对应ASCII码的16进制表示。比如英文的双引号(”)对应的编码就是%22. 2.3 英文空格( )编码为%20,而不是加号(+)。对以上规范化的字符串使用摘要算法得到签名
@验签步骤:
验签同签名类似。
@注意事项:
签名规则可以参考阿里云API签名
CLI Documentation¶
命令行工具用于辅助性功能,目前主要是用于生成RSA密钥对,有两种方法。
1. generate_rsa_keys.py¶
这是Python自身生成的RSA密钥对,它支持输出到控制台或写入文件、设置私钥密码等,请查看命令帮助:
# generate_rsa_keys.py -h
usage: generate_rsa_keys.py [-h] [-v] [-l {1024,2048,3072,4096}]
[-p PASSPHRASE] [-w]
optional arguments:
-h, --help show this help message and exit
-v, --version Print the SecureHTTP Version
-l {1024,2048,3072,4096}, --length {1024,2048,3072,4096}
Key length, default is 2048.
-p PASSPHRASE, --passphrase PASSPHRASE
The pass phrase used for protecting the private key.
-w, --write Write a key pair file in PEM format
2. generate_rsa_keys.sh¶
这是使用系统OpenSSL生成的RSA密钥对,此命令可以传递一个位置:密钥长度(默认1024),在当前目录保存4个文件,分别是pkcs1格式密钥对和pkcs8密钥对,比如:
generate_rsa_keys.sh 2048
SecureHTTP.js¶
请参考: SecureHTTP.js
API Documentation¶
- PS:
接口中函数返回值,正常情况下,Python2.7返回unicode,Python3.x返回str。
函数参数要求字符串的,一般建议py2中使用unicode,py3中使用bytes,请注意编码问题。
另,RSADecrypt解密中,新增一个sentinel参数,一个挺重要的解密失败的标记,关于此参数的建议,请参考:Pycryptodomex接口文档,注意文档中
Warning
部分。
Python-SecureHTTP¶
关于通信过程加密算法的说明:
AES加解密:
模式:CBC 密钥长度:128,192,256bit 密钥key:16,24,32bytes(建议使用ASCII编码密钥),初始偏移向量iv固定为key前16个字节 补码方式:PKCS7Padding(在AES中理论上同PKCS5Padding) 加密结果编码方式:十六进制或base64编码
RSA加解密:
算法:RSA 填充:RSA_PKCS5_PADDING 密钥格式:符合PKCS#1规范,密钥对采用PEM形式,公钥要求pkcs1或pkcs8格式,私钥要求pkcs1格式
签名:
可选对请求参数或数据添加公共参数后排序再使用摘要算法签名(MD5、SHA1等)
关于参数中字符串说明,若无指定,则:
- py2: str, unicode
- py3: str, bytes
copyright: |
|
---|---|
license: | BSD 3-Clause, see LICENSE for more details. |
-
SecureHTTP.
RSAEncrypt
(pubkey, plaintext, output='base64')[源代码] RSA公钥加密
参数: - pubkey – str,bytes: pkcs1或pkcs8格式公钥
- plaintext – str,bytes: 准备加密的文本消息
- output – str: Output format: base64 (default), hex (hexadecimal)
返回: str,unicode: base64编码的字符串
-
SecureHTTP.
RSADecrypt
(privkey, ciphertext, passphrase=None, sentinel='ERROR', input='base64')[源代码] RSA私钥解密
参数: - privkey – str,bytes: pkcs1格式私钥
- ciphertext – str,bytes: 已加密的消息
- passphrase – str,bytes: 私钥保护的密码短语
- sentinel – any type: 检测到错误时返回的标记,默认返回ERROR字符串
- input – str: Input format: base64 (default) or hex (hexadecimal), refer to the output parameter of
RSAEncrypt()
返回: str,unicode: 消息原文
-
SecureHTTP.
AESEncrypt
(key, plaintext, output='base64', output_type=None)[源代码] AES Encryption Function.
参数: - key – basestring: 16 24 32 bytes with ASCII.
- plaintext – basestring: Plaintext message to be encrypted
- output – str: Output format: base64 (default), hex (hexadecimal)
- output_type – basestring: The type of encrypted string to be output, refer to the dst_type parameter of
required_string()
Raises: AESError,ValueError
返回: Encrypted ciphertext
-
SecureHTTP.
AESDecrypt
(key, ciphertext, input='base64', output_type=None)[源代码] AES Decryption Function.
参数: - key – basestring: Refer to the key parameter of
AESEncrypt()
- ciphertext – basestring: Ciphertext message to be decrypted
- input – str: Input format: base64 (default) or hex (hexadecimal), refer to the output parameter of
AESEncrypt()
- output_type – basestring: The type of decrypted string to be output, refer to the dst_type parameter of
required_string()
Raises: AESError,binascii.Error,ValueError,TypeError
返回: Decrypted plaintext
- key – basestring: Refer to the key parameter of
-
class
SecureHTTP.
EncryptedCommunicationClient
(PublicKey)[源代码] 基类:
SecureHTTP.EncryptedCommunicationMix
客户端:主要是公钥加密
-
clientDecrypt
(encryptedRespData)[源代码] 客户端获取服务端返回的加密数据并解密 for NO.4
参数: encryptedRespData – dict: 服务端返回的加密数据,其格式应该是 {data: AES加密数据} Raises: TypeError,SignError 返回: 解密验签成功后,返回服务端的消息原文
-
clientEncrypt
(post, **signargs)[源代码] 客户端发起加密请求通信 for NO.1
参数: - post – dict: 请求的数据
- signIndex – str: 参与排序加签的键名,False表示不签名,None时表示加签post中所有数据,非空时请用逗号分隔键名(字符串)
- signMethod – str: 签名算法,可选md5、sha1、sha256
返回: dict: {key=RSAKey, value=加密数据}
-
-
class
SecureHTTP.
EncryptedCommunicationServer
(PrivateKey)[源代码] 基类:
SecureHTTP.EncryptedCommunicationMix
服务端:主要是私钥解密
-
serverDecrypt
(encryptedPostData)[源代码] 服务端获取请求数据并解密 for NO.2
参数: encryptedPostData – dict: 请求的加密数据 Raises: TypeError,SignError 返回: 解密后的请求数据原文
-
serverEncrypt
(resp, **signargs)[源代码] 服务端返回加密数据 for NO.3
参数: - resp – dict: 服务端返回的数据,目前仅支持dict
- signIndex – tuple,list: 参与排序加签的键名,False表示不签名,None时表示加签resp中所有数据,非空时请用逗号分隔键名(字符串)
- signMethod – str: 签名算法,可选md5、sha1、sha256
Raises: TypeError,ValueError
返回: dict: 返回dict,格式是 {data: AES加密数据}
-
-
SecureHTTP.
generate_rsa_keys
(incall=False, length=2048, passphrase=None)[源代码] 生成RSA所需的公钥和私钥,公钥格式pkcs8,私钥格式pkcs1。
参数: - incall – bool: 是否内部调用,默认False表示提供给脚本调用直接打印密钥,True不打印密钥改为return返回
- length – int: 指定密钥长度,默认1024,需要更强加密可设置为2048
- passphrase – str: 私钥保护的密码短语
返回: tuple(public_key, private_key)
-
exception
SecureHTTP.
SignError
[源代码] 基类:
SecureHTTP.SecureHTTPException
签名异常:加签异常、验签不匹配等
-
exception
SecureHTTP.
AESError
[源代码] 基类:
SecureHTTP.SecureHTTPException
AES异常:加密、解密时参数错误
-
exception
SecureHTTP.
RSAError
[源代码] 基类:
SecureHTTP.SecureHTTPException
RSA异常:密钥错误、加密解密错误
更新日志(CHANGELOG)¶
V0.5.0¶
Released in 2019-04-25
- fix: 修复AES加密key长度问题 (#2)
- feat: 新增
required_string()
转化不同py版本的字符串 - feat: 新增AES加密参数,可以定义返回加密的字符串的类型
- feat: 新增RSA加密输出/解密输入的编码参数(hex、base64)
- feat: 更新
EncryptedCommunicationMix
中生成AESKey的函数,现在生成的key默认为16字节 - chore: AES加密解密函数调整,填充方法改为pkcs7,理论上兼容pkcs5,但可能会遇到与旧版本不兼容
- chore: 不再支持pycrypto和pycryptodome,一律使用pycryptodomex!
- chore: Update README.md to README.rst
- chore: 更新文档
- todo: RSA加解密调整
V0.4.1¶
Released in 2019-04-21
- fix: 修复python2.7下RSAEncrypt的plaintext参数编码
- feat: 新增RSADecrypt参数sentinel
- docs: 更新js和api说明
V0.3.0¶
Released in 2019-01-22
- 生成、导入RSA私钥时可以设置密码
- 命令行参数化,可设置长度、密码并写入PEM文件
- 自定义签名算法:md5、sha1、sha256
- RSA加密、解密弃用rsa包,改用pycryptdome的PKCS1_v1_5
- 注意:Pycrypto、Pycryptodome将弃用!
V0.1.0¶
Released in 2019-01-04
- 首发版本
- RSA密钥对生成
- RSA加密、解密
- AES加密、解密
- 加密通信流程初步完成:客户端上传数据加密 ==> 服务端获取数据解密 ==> 服务端返回数据加密 ==> 客户端获取数据解密