[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">
:-: 
***微信掃一掃,關注“python測試開發圈”,了解更多測試教程!***