Python中关于服务端客户端验签以及加解密问题如何解决?
最近在爬取“马蜂窝”的 app 攻略部分,爬取不同城市的时候发现每次请求的时候需要加上不同的 oauth_signature, 也就是服务端和客户端验证的签名。
例如,得到的是“ oauth_signature=2Kj6MQi6oZ%2FkNPye4xYUGm%2BRD%2Bc%3D ”
URL 解码之后得到的是“ 2Kj6MQi6oZ/kNPye4xYUGm+RD+c=”
这个是使用的 HMAC-SHA1 方法,也就是 sha1,就是使用消息+类似密码的东西得到的
大致公式总结如下,
消息=未知
密码=''.join( [ oauth_timestamp,oauth_token,oauth_nonce ] .sort())->base64 编码
sha1 (消息+密码) = oauth_signature = “ oauth_signature ”
结果总是算不对,现在想问问可不可以直接去得到消息是什么,以及每次的 oauth_nonce 随机数怎么得到
Python中关于服务端客户端验签以及加解密问题如何解决?
在Python里搞服务端客户端的验签和加解密,核心就是选对库和理清流程。验签这块,通常用非对称加密,比如RSA。客户端用私钥签名,服务端用公钥验签。加解密的话,看场景,对称的AES速度快,非对称的RSA/ECC适合传密钥。
直接上代码例子,我们用 cryptography 库,这是现在比较主流和安全的。
1. 生成密钥对(RSA)
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
# 生成私钥
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
# 提取公钥
public_key = private_key.public_key()
# 序列化私钥(PKCS8格式,PEM编码)
pem_private = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
# 序列化公钥(SubjectPublicKeyInfo格式,PEM编码)
pem_public = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
# 保存到文件(示例)
with open('private_key.pem', 'wb') as f:
f.write(pem_private)
with open('public_key.pem', 'wb') as f:
f.write(pem_public)
2. 客户端:签名数据
假设你要发送的数据是 data。
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
import json
# 准备数据
data = {"user_id": 123, "action": "login"}
data_str = json.dumps(data, sort_keys=True).encode('utf-8') # 排序保证一致性
# 加载私钥
with open('private_key.pem', 'rb') as f:
private_key = serialization.load_pem_private_key(
f.read(),
password=None,
)
# 签名
signature = private_key.sign(
data_str,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
# 将数据和签名一起发送(例如Base64编码)
import base64
payload = {
"data": data,
"signature": base64.b64encode(signature).decode('utf-8')
}
# 现在可以发送 payload 给服务端
3. 服务端:验证签名
from cryptography.exceptions import InvalidSignature
# 接收到的 payload
received_payload = payload # 假设从网络接收
# 加载公钥
with open('public_key.pem', 'rb') as f:
public_key = serialization.load_pem_public_key(f.read())
# 重构待验证的数据字符串
data_to_verify = json.dumps(received_payload['data'], sort_keys=True).encode('utf-8')
signature_to_verify = base64.b64decode(received_payload['signature'])
try:
# 验签
public_key.verify(
signature_to_verify,
data_to_verify,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("验签成功!数据完整且可信。")
# 处理业务逻辑...
except InvalidSignature:
print("验签失败!数据可能被篡改。")
# 拒绝请求...
4. 关于加解密(AES对称加密示例) 通常用RSA传AES密钥,然后用AES加密实际数据。
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding as sym_padding
import os
# 生成一个随机的AES密钥(256位)
aes_key = os.urandom(32) # 32字节 = 256位
# 客户端:用服务端的RSA公钥加密这个AES密钥
from cryptography.hazmat.primitives.asymmetric import padding as asym_padding
with open('server_public.pem', 'rb') as f: # 假设有服务端的公钥
server_public_key = serialization.load_pem_public_key(f.read())
encrypted_aes_key = server_public_key.encrypt(
aes_key,
asym_padding.OAEP(
mgf=asym_padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# 客户端:用AES密钥加密数据
iv = os.urandom(16) # 生成随机IV
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
encryptor = cipher.encryptor()
# 先对数据做PKCS7填充
padder = sym_padding.PKCS7(128).padder()
padded_data = padder.update(data_str) + padder.finalize()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
# 将 encrypted_aes_key, iv, ciphertext 发送给服务端
# 服务端:用自己的RSA私钥解密出AES密钥
with open('server_private.pem', 'rb') as f:
server_private_key = serialization.load_pem_private_key(f.read(), password=None)
decrypted_aes_key = server_private_key.decrypt(
encrypted_aes_key,
asym_padding.OAEP(
mgf=asym_padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# 服务端:用AES密钥解密数据
cipher = Cipher(algorithms.AES(decrypted_aes_key), modes.CBC(iv))
decryptor = cipher.decryptor()
decrypted_padded_data = decryptor.update(ciphertext) + decryptor.finalize()
# 去除填充
unpadder = sym_padding.PKCS7(128).unpadder()
original_data = unpadder.update(decrypted_padded_data) + unpadder.finalize()
print("解密后的数据:", original_data.decode('utf-8'))
总结一下关键点:
- 验签:用非对称加密(如RSA PSS)保证身份和完整性。私钥签名,公钥验签。
- 加密:混合使用。用RSA加密传递临时的对称密钥(如AES),再用AES加密实际数据,兼顾安全与效率。
- 库选择:用
cryptography,别用旧的pycrypto或PyCryptodome,除非有历史包袱。 - 密钥管理:私钥绝不能泄露,公钥可以分发。生产环境要用硬件安全模块(HSM)或密钥管理服务(KMS)来管私钥。
- 数据一致性:签名前的数据序列化(如JSON)要确定顺序(
sort_keys=True),否则两端对不上。
简单说就是:验签用RSA,加密用AES,密钥管理要严格。

