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中关于服务端客户端验签以及加解密问题如何解决?

1 回复

在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'))

总结一下关键点:

  1. 验签:用非对称加密(如RSA PSS)保证身份和完整性。私钥签名,公钥验签。
  2. 加密:混合使用。用RSA加密传递临时的对称密钥(如AES),再用AES加密实际数据,兼顾安全与效率。
  3. 库选择:用 cryptography,别用旧的 pycryptoPyCryptodome,除非有历史包袱。
  4. 密钥管理:私钥绝不能泄露,公钥可以分发。生产环境要用硬件安全模块(HSM)或密钥管理服务(KMS)来管私钥。
  5. 数据一致性:签名前的数据序列化(如JSON)要确定顺序(sort_keys=True),否则两端对不上。

简单说就是:验签用RSA,加密用AES,密钥管理要严格。

回到顶部