如何使用Signal Protocol解密自定义WhatsApp Web客户端接收的密文
解密WhatsApp Web返回密文的实现方案
核心原理
WhatsApp Web端的业务消息均采用AES-256-GCM加密,加密密钥由你生成的Curve25519私钥和手机端返回的公钥通过ECDH密钥协商后派生得到。
具体实现步骤
- 补充保存必要参数
你当前的登录代码仅保存了本地私钥和浏览器ID,扫码成功后手机端返回的临时公钥也需要写入token文件,后续密钥协商必须用到该公钥。 - 派生加密密钥
扫码成功后用本地私钥和手机端公钥做ECDH计算得到共享秘密,再通过HKDF-SHA256派生接收和发送密钥:
from cryptography.hazmat.primitives.kdf.hkdf import HKDF from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.ciphers.aead import AESGCM import signal_protocol # 入参为你保存的本地私钥、扫码后从服务端返回值中提取的手机端公钥 def derive_keys(local_privkey: signal_protocol.curve.PrivateKey, phone_pubkey: signal_protocol.curve.PublicKey): # ECDH计算共享密钥 shared_secret = local_privkey.calculate_agreement(phone_pubkey) # HKDF派生收发密钥,info参数为WhatsApp Web约定固定值 hkdf = HKDF( algorithm=hashes.SHA256(), length=64, salt=None, info=b"WHATSAPP WEB KEYS" ) derived = hkdf.derive(shared_secret) receive_key = derived[:32] # 解密收到的消息用密钥 send_key = derived[32:] # 发送消息加密用密钥 return receive_key, send_key
- 解密收到的加密消息
登录成功后收到的非控制类websocket payload结构为:12字节IV + 密文 + 16字节GCM标签,拆分后用派生的接收密钥解密即可:
def decrypt_whatsapp_payload(encrypted_data: bytes, receive_key: bytes) -> bytes: aes_gcm = AESGCM(receive_key) iv = encrypted_data[:12] tag = encrypted_data[-16:] ciphertext = encrypted_data[12:-16] # 解密同时会校验GCM标签,校验失败会抛出异常 return aes_gcm.decrypt(iv, ciphertext + tag, associated_data=None)
- 解析明文内容
解密后得到的是Protobuf格式的二进制数据,匹配WhatsApp Web公开的Protobuf结构定义做反序列化,就能得到结构化的消息、联系人等业务数据。
注意事项
- 只有登录完成后的业务消息是加密的,init、challenge、login等控制信令仍是明文JSON格式,不需要解密。
- 注意校验你使用的signal_protocol库的Curve25519运算兼容性,部分第三方库的序列化格式和WhatsApp约定不一致会导致密钥派生错误,解密失败。
- 你当前代码中写死的版本号
[2,2138,13]对应旧版WhatsApp Web,若服务端强制升级版本会导致握手失败,需要同步更新版本参数。
内容的提问来源于stack exchange,提问作者Lima
相关产品推荐
相关产品推荐

