You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确实现Python向Java传递AES加密IV完成跨端加解密

Python客户端对接Java服务端AES-CBC随机IV加解密问题修复

核心错误原因

  • Python端生成16字节随机IV后,未将IV拼入传输内容,仅把密文做base64编码后发给服务端。服务端直接截取base64字符串前16位作为IV,本质是用密文的base64编码片段作为解密IV,必然出现前半段解密乱码。
  • 尝试移除IV时在base64字符串层面截取16位逻辑错误:IV是原始二进制字节,16字节IV经base64编码后长度为24字符,直接截16个字符会破坏密文结构,最终解密时抛出“数据长度需为16倍数”的错误。
  • Python端填充逻辑存在缺陷:直接以字符串长度计算填充字节数,未考虑多字节字符(如中文)的UTF-8编码长度,跨语言对接时会出现填充长度计算错误。
  • 代码存在变量拼写错误,易引发传参异常。

AES-CBC模式随机IV标准传输流程:

  1. 生成16字节密码学安全的随机IV
  2. 对明文做标准PKCS7填充后,用IV+密钥加密得到密文字节
  3. 将原始IV字节拼接在密文字节头部,对拼接后的完整字节数组做urlsafe base64编码后传输
  4. 服务端收到数据后先做base64解码,取前16字节作为IV,剩余字节作为密文做解密即可

修复后的Python客户端代码

import os
import json
import base64
from Crypto.Cipher import AES

block_size = AES.block_size  # 固定为16,无需自行硬编码

def pad(text: str) -> bytes:
    # 先转UTF-8字节再计算填充长度,兼容多字节字符
    text_bytes = text.encode("utf-8")
    pad_len = block_size - len(text_bytes) % block_size
    # 标准PKCS7填充,和Java端PKCS5Padding完全兼容
    padding = bytes([pad_len]) * pad_len
    return text_bytes + padding

# 加密传输逻辑
jsonString = json.dumps(data.__dict__, default=str)
key = b'sixteenssixteens'  # 密钥直接用字节格式,避免编码不一致问题
iv = os.urandom(16)  # 生成16字节随机IV
cipher = AES.new(key, AES.MODE_CBC, iv)
cipher_byte = cipher.encrypt(pad(jsonString))
# 关键操作:将原始IV拼在密文头部后再做base64编码
total_data = iv + cipher_byte
encrypt_text = base64.urlsafe_b64encode(total_data).decode("UTF-8")
# 发送请求
requests.post('server', data=encrypt_text)

修复后的Java服务端代码

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

// 解密逻辑
requestBody = exchange.getRequestBody();
String requestData = readString(requestBody);
byte[] key = "sixteenssixteens".getBytes(StandardCharsets.UTF_8);
// 先对完整请求体做base64解码,不要提前截断字符串
byte[] totalBytes = Base64.getUrlDecoder().decode(requestData);
// 解码后前16字节为IV
byte[] ivBytes = new byte[16];
System.arraycopy(totalBytes, 0, ivBytes, 0, 16);
// 剩余字节为真实密文
byte[] cipherBytes = new byte[totalBytes.length - 16];
System.arraycopy(totalBytes, 16, cipherBytes, 0, cipherBytes.length);

IvParameterSpec iv = new IvParameterSpec(ivBytes);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(cipherBytes);
// 统一指定UTF-8编码转字符串,避免中文乱码
String decryptedResult = new String(original, StandardCharsets.UTF_8);
System.out.println("Decrypted string: " + decryptedResult);
try {
    data = gson.fromJson(decryptedResult, Data.class);
} catch (Exception ex){
    ex.printStackTrace();
    return;
}

生产环境注意事项

  • 不要使用16字节(AES-128)硬编码弱密钥,建议使用32字节密钥启用AES-256,密钥存储在配置中心或专用密钥管理服务,不要硬编码在业务代码中。
  • 所有字符串与字节互转环节统一指定UTF-8编码,不要依赖操作系统默认编码,避免跨环境部署出现乱码。
  • 填充逻辑严格遵循PKCS7标准,不要自行实现字符串层面的填充逻辑,避免多字节字符场景下的计算错误。

内容的提问来源于stack exchange,提问作者Elementswelder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 07:09:27