[TOC]
### RSA 加密与签名介绍
RSA是一种非对称加密算法,简单理解就是两个密钥:一个公钥,一个私钥。
加密:公钥加密,私钥解密;
签名:私钥签名,公钥验签。
**用作加密时**,密文泄露是无所谓的(相对而言),重要的是用于解密的密钥必须安全,所以用不公开的私钥来解密,用公钥来加密;
**用作签名时**,目的是防止别人伪造我的身份发信息,所以用私钥来签名,用公钥来验签。
#### python签名示例
`requirements.txt`:
```python
pycryptodome==3.6.6
```
`utils.py`:
```python
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import MD5
import base64
def rsa_sign(encrData):
privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMJSignj27blOez6i6YCM1b4AC9CtgubD0B9gmTskENJNxzg9i59hCpIibkTg1rvMDdSuKFvlHtxkOgSID8Qgm1h3AxpMKpYT58FnApqY7img85xEZo+uNB1GHTbrhuCGQFf2P8U1hE01Y6miFsESsFTQ09BpNy+1wPKt/KbW75fAgMBAAECgYA/04iPkw4Z1tTd57Vyw4pFaJP28fyFd1rdHdx0ddc0opm9nI5/2q5MjSLfbW9ZsPKvWTZXoCSvHzAvabS5whx0aYpZlfWCd6QRNAuoaP0FSCWH/ty7I3nHQJK8LQQhP3nfekAfiyMpvGGK4KrderEP37/MG1202iycR4X6fZnMkQJBAPqaKChFYAqStADg3owoux5Gc3rAf2zRXIBZXPNEYgE0owpwtP2tYPdcIy9l01Yv1nIDr2O7x8JrbUOuNe/4/4sCQQDGggz4XHqZAwDBvea3g9FBCnojzyqQMtHO54TxL6NXIr7maBoBb0XCXuh6Uz2F7O8an1Bi/adQXVXUhvErG9b9AkBJhU6AuhG6KF4M3+wKnKyA7lRU0ALSTv3fXdhKOmaySdoHZxeCUQpgp7Re5HXDFFfKrVAYZ2/slw3ATGzgkWGPAkBW5b1px4n/i3n8VfY2paSntT9sh5bZUvXXfjALKNB3J4Wr9SxVLnG6ObPJQMEw7FxrKgyVmPZyTrlw9LWEKoa9AkBz9OU/BFhg9wIcHiFWQSOQdKQ4touyTF3T3EbROt34oXEhp1b3/eEGlwvNF2dUrfi39b5rKph63G6d3rxb+GVG"
private_key_bytes = base64.b64decode(privateKey)
priKey = RSA.importKey(private_key_bytes)
# priKey = RSA.importKey(privateKey)
signer = PKCS1_v1_5.new(priKey)
hash_obj = MD5.new(encrData.encode('utf-8'))
signature = base64.b64encode(signer.sign(hash_obj))
return signature
if __name__ == '__main__':
encrData = "Hello,Milton"
print(rsa_sign(encrData))
````
**注意内容**:
1. 密钥如果是读取自.pem文件,密钥会有开始行和结束行,叫做头标注信息和尾标注信息。常见的长这样:
```
'''-----BEGIN PRIVATE KEY-----
#密钥内容#
-----END PRIVATE KEY-----'''
```
此时直接priKey = RSA.importKey(privateKey)(见被注释掉的部分)即可,不用对私钥进行base64解码;
2. 哈希算法可以采用MD5,也可以用别的比如SHA;
<br>
### 参考例子
#### 公共参数定义
| 参数名 | 是否必须 | 描述 |
| --- | --- | --- |
| timestamp | 是 | 每个请求都要带上当前请求时间戳,单位秒 |
| sign_type | 是 | 签名类型, RSA, 需要应用(应用自行生成)与平台各一套密钥对, 应用把公钥提供给平台在调用接口时验签, 平台把公钥提供给应用在接口回调时验签 |
| sign | 是 | 请求签名, 签名规则: 将除sign参数外的所有非空参数按key进行ASCII字典升序排列, 再将排序后的参数(key=value)以&拼接起来, 生成类似: access_token=kdfdskl×tamp=25684512565的字符串, 再经过urlencode,使用RSA私钥加密(加密算法: SHA256)并生成base64的签名字符串, 所有请求都需要签名。 |
#### python 签名
业务工具类 `sign_utils.py`:
```python
# -*-coding:utf-8-*-
from urllib import parse
import time
import requests
from Crypto.PublicKey import RSA
import base64
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
import json
def send_post_request(url, params_dict):
"""
发送http post请求
:param url: 接口地址
:param params_dict:接口参数
:return:
"""
sign = coffee_rsa_sign(params_dict)
params_dict['sign'] = sign.decode('utf8')
resp = requests.post(url, data=params_dict)
print("-" * 150)
print("case_url:", resp.url)
print("case_data:", json.dumps(params_dict, indent=4, ensure_ascii=False))
print("case_resp:", json.dumps(resp.json(), indent=4, ensure_ascii=False))
print("")
return resp
def rsa_sign(sign_raw, key_raw, encryption="SHA256"):
"""
RSA签名
:param sign_raw:待签名字符串
:param key_raw:签名私钥
:param encryption:签名算法,默认SHA256
:return:
"""
if encryption == "SHA256":
# 将签名串哈希 SHA256
hash_obj = SHA256.new(sign_raw.encode('utf-8'))
else:
print("加密算法未指定")
return False
# 加载签名私钥
private_key = RSA.importKey(base64.b64decode(key_raw))
signer = PKCS1_v1_5.new(private_key)
# 使用RSA私钥对签名串(必须先哈希)签名
signature = signer.sign(hash_obj)
return signature
def get_rsa_sign(params_dict):
"""
获取业务API签名
:param params_dict:除了sign字段的所有其他参数字典
:return:
"""
# step0:将非空参数按key进行ASCII字典升序排列, 再将排序后的参数(key=value)以&拼接起来
key_lists = sorted(params_dict.keys())
sign_raw = "&".join(list(map(lambda key: key + "=" + str(params_dict.get(key)), key_lists)))
# step1:将签名原串 urldecode
sign_raw = parse.quote(sign_raw)
# step2:RSA签名
key_raw = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMJSignj27blOez6i6YCM1b4AC9CtgubD0B9gmTskENJNxzg9i59hCpIibkTg1rvMDdSuKFvlHtxkOgSID8Qgm1h3AxpMKpYT58FnApqY7img85xEZo+uNB1GHTbrhuCGQFf2P8U1hE01Y6miFsESsFTQ09BpNy+1wPKt/KbW75fAgMBAAECgYA/04iPkw4Z1tTd57Vyw4pFaJP28fyFd1rdHdx0ddc0opm9nI5/2q5MjSLfbW9ZsPKvWTZXoCSvHzAvabS5whx0aYpZlfWCd6QRNAuoaP0FSCWH/ty7I3nHQJK8LQQhP3nfekAfiyMpvGGK4KrderEP37/MG1202iycR4X6fZnMkQJBAPqaKChFYAqStADg3owoux5Gc3rAf2zRXIBZXPNEYgE0owpwtP2tYPdcIy9l01Yv1nIDr2O7x8JrbUOuNe/4/4sCQQDGggz4XHqZAwDBvea3g9FBCnojzyqQMtHO54TxL6NXIr7maBoBb0XCXuh6Uz2F7O8an1Bi/adQXVXUhvErG9b9AkBJhU6AuhG6KF4M3+wKnKyA7lRU0ALSTv3fXdhKOmaySdoHZxeCUQpgp7Re5HXDFFfKrVAYZ2/slw3ATGzgkWGPAkBW5b1px4n/i3n8VfY2paSntT9sh5bZUvXXfjALKNB3J4Wr9SxVLnG6ObPJQMEw7FxrKgyVmPZyTrlw9LWEKoa9AkBz9OU/BFhg9wIcHiFWQSOQdKQ4touyTF3T3EbROt34oXEhp1b3/eEGlwvNF2dUrfi39b5rKph63G6d3rxb+GVG"
signature = rsa_sign(sign_raw, key_raw, "SHA256")
# step3:对签名结果base64加密
signature = base64.b64encode(signature)
return signature
if __name__ == '__main__':
params_dict = {
'app_id': '12996762',
'app_secret': '902ac71d8fdbdfc097f53070afbcaefb',
'platform': '1',
'timestamp': str(int(time.time())),
'sign_type': 'RSA',
}
# send_post_request("http://xx.x.xxx.net/oauth2/access_token", params_dict)
sign = coffee_rsa_sign(params_dict)
print(sign)
```
<hr style="margin-top:100px">
:-: ![](https://box.kancloud.cn/2ff0bc02ec938fef8b6dd7b7f16ee11d_258x258.jpg)
***微信扫一扫,关注“python测试开发圈”,了解更多测试教程!***