




XML必须在发送方本地加密后传输,服务端无法解密明文;需用AES-GCM+RSA-OAEP组合加密,校验auth_tag,避免DOM解析失真,解密后须UTF-8显式解码。
端到端加密的核心是:XML数据在发送方本地完成加密,密文上传后,服务端无法解密或查看明文——这意味着哪怕你用HTTPS上传,也不能把原始XML直接发过去。常见错误是误以为TLS = E2EE,结果服务端日志、数据库、运维权限都能看到XML明文。
实操要点:
crypto.subtle或PyCryptodome),不是由HTTP库或网关代劳xml.etree.ElementTree.canonicalize)取决于签名需求;纯加密可跳过假设你已有public_key.pem和待传XML字符串xml_str:
from Crypto.Cipher import AES, PKCS1_OAEP from Crypto.PublicKey import RSA from Crypto.Random import get_random_bytes import base641. 生成随机AES密钥
aes_key = get_random_bytes(32) # AES-256 cipher_aes = AES.new(aes_key, AES.MODE_GCM) xml_bytes = xml_str.encode('utf-8') ciphertext, auth_tag = cipher_aes.encrypt_and_digest(xml_bytes)
2. 用RSA公钥加密AES密钥
with open('public_key.pem', 'rb') as f: rsa_key = RSA.import_key(f.read()) cipher_rsa = PKCS1_OAEP.new(rsa_key) encrypted_aes_key = cipher_rsa.encrypt(aes_key)
3. 构造上传载荷(JSON或表单字段)
payload = { 'encrypted_xml': base64.b64encode(ciphertext).decode(), 'auth_tag': base64.b64encode(auth_tag).decode(), 'iv': base64.b64encode(cipher_aes.nonce).decode(), 'encrypted_aes_key': base64.b64encode(encrypted_aes_key).decode() }
然后用requests.post(..., json=payload)上传
注意:PKCS1_OAEP比PKCS1_v1_5更安全,避免填充预言攻击;auth_tag必须随密文一起传,服务端验证失败必须拒收。
浏览器里直接加密XML字符串即可,但常见坑是:用DOMParser解析后再序列化,可能引入空白归一化、命名空间重写、编码变更(如&变&),导致解密后XML不可用。
正确做法:
或fetch响应,直接对原始string加密
new XMLSerializer().serializeToString(doc)——它不保证字节级保真crypto.subtle时,用new TextEncoder().encode(str)转Uint8Array,别用atob/btoa处理二进制iterations至少100000
上传后服务端解密报错,八成出在这三处:
InvalidTag(PyCryptodome)或OperationError(Web Crypto):auth_tag长度不对(GCM要求16字节),或base64解码后没还原成原始字节-----BEGIN PUBLIC KEY-----),而非传统PKCS#1(-----BEGIN RSA PUBLIC KEY-----)str.decode('latin-1')或默认系统编码解码,导致乱码——必须显式用.decode('utf-8')
密钥管理、密文存储、审计日志这些环节,一旦脱离“发送方加密→传输→接收方解密”这个闭环,就不是E2EE。真正的难点不在加解密函数调用,而在确保整个链路没有明文落盘、无调试输出、无中间人缓存副本。