如何使用Python解密由tweet-nacl(JavaScript)加密的数据?
用PyNaCl解密tweet-nacl(JavaScript)加密内容的实现方案
我之前刚好踩过这个坑,其实PyNaCl和tweet-nacl的box操作是完全兼容的——毕竟两者底层都是基于libsodium的x25519-xsalsa20-poly1305加密标准,只是API封装方式不同而已。你提到的临时公钥(ephemPubKey)解密的问题,核心是要搞清楚两者的参数对应关系和编码转换逻辑。
先理清楚对应关系
JavaScript端的tweet-nacl.box是非对称密封盒加密:
- 加密时用接收方公钥+发送方密钥对+随机nonce生成密文
- 解密时需要发送方的临时公钥(ephemPubKey)+接收方私钥+nonce+密文
PyNaCl的nacl.public.Box类就是对应这个功能,只是参数传递方式稍有不同。
完整的Python解密代码
下面是直接对应你提供的JS解密逻辑的PyNaCl实现:
from nacl.public import Box, PrivateKey, PublicKey from nacl.encoding import Base64Encoder def decrypt_tweetnacl_ciphertext(encrypted_base64, nonce_base64, ephem_pubkey_base64, receiver_privkey_base64): # 1. 将JS端传来的base64字符串解码为字节(对应JS的naclutil.decodeBase64) encrypted_bytes = Base64Encoder.decode(encrypted_base64) nonce_bytes = Base64Encoder.decode(nonce_base64) # 2. 加载发送方的临时公钥和接收方的私钥 ephem_pubkey = PublicKey(ephem_pubkey_base64, encoder=Base64Encoder) receiver_privkey = PrivateKey(receiver_privkey_base64, encoder=Base64Encoder) # 3. 创建解密用的Box实例:接收方私钥 + 发送方临时公钥 box = Box(receiver_privkey, ephem_pubkey) try: # 4. 执行解密(对应JS的nacl.box.open) decrypted_bytes = box.decrypt(encrypted_bytes, nonce=nonce_bytes) # 5. 转换为UTF-8字符串(对应JS的naclutil.encodeUTF8) return decrypted_bytes.decode('utf-8') except Exception as e: print(f"解密失败:{str(e)}") return None
关键细节说明
- 编码一致性:JS端用base64编码所有二进制数据,所以PyNaCl必须用
Base64Encoder来解码,不要误用HexEncoder或者其他编码。 - Box实例的创建:PyNaCl的
Box初始化需要接收方私钥和发送方的临时公钥,这正好对应JS解密时传入的最后两个参数。 - nonce的传递:PyNaCl的
decrypt方法需要把nonce作为单独参数传入,而不是和密文拼接(tweet-nacl的box.open也是分开传入的,逻辑一致)。 - 异常处理:如果密钥不匹配、nonce错误或者密文被篡改,解密会抛出异常,一定要捕获处理。
测试验证
你可以用JS端加密后的密文、nonce、临时公钥和接收方私钥代入这个函数,就能得到和JS端解密一致的明文结果。
内容的提问来源于stack exchange,提问作者WestCoastProjects
相关产品推荐
相关产品推荐

