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

使用CryptoJS在JS加密消息,FastAPI(Python)解密遇填充错误

AES跨语言(JS-Python)解密填充错误解决方案

问题场景

使用CryptoJS在前端加密消息,通过POST请求发送到FastAPI后端解密时,出现ValueError: Padding is incorrect.错误,核心代码如下:

前端JS代码(原错误版本)

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<script>
var message = 'Message';
var key = '1234567812345678';
var encryptedAES = CryptoJS.AES.encrypt(message, key, {mode: CryptoJS.mode.ECB});
var url2 = new URL('http://localhost:8000/decrypt/');
$("#encrypt").on("click", evt => {
  fetch(url2, {
    method: 'POST',
    body: encryptedAES
  }).then().catch((error) => { console.log(error.message); });
});
</script>

后端Python代码(原版本)

import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from fastapi import FastAPI, Request

app = FastAPI()

@app.post('/decrypt/')
async def decrypt(request: Request):
    key = '1234567812345678'
    data = await request.body()
    new_data = base64.b64decode(data)
    cipher = AES.new(key.encode('utf-8'), AES.MODE_ECB)
    final = unpad(cipher.decrypt(new_data), 16)
    return final

核心问题及解决办法

1. 前端JS端的关键修正

(1)正确传递密钥

CryptoJS直接传入字符串密钥时,会默认用PBKDF2算法推导密钥,而非直接使用字符串字节内容,导致前后端密钥不一致。需将字符串密钥转为WordArray:

var key = CryptoJS.enc.Utf8.parse('1234567812345678');

(2)正确获取加密后的密文

CryptoJS.AES.encrypt()返回的是CipherParams对象,直接作为请求体发送会被序列化为[object Object],导致后端解码出错误密文。需调用toString()获取base64格式密文:

var encryptedAES = CryptoJS.AES.encrypt(message, key, {mode: CryptoJS.mode.ECB}).toString();

修正后的完整JS代码

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<script>
var message = 'Message';
var key = CryptoJS.enc.Utf8.parse('1234567812345678');
var encryptedAES = CryptoJS.AES.encrypt(message, key, {mode: CryptoJS.mode.ECB}).toString();
var url2 = new URL('http://localhost:8000/decrypt/');
$("#encrypt").on("click", evt => {
  fetch(url2, {
    method: 'POST',
    headers: {
      'Content-Type': 'text/plain'
    },
    body: encryptedAES
  }).then(response => response.json())
    .then(data => console.log(data))
    .catch((error) => { console.log(error.message); });
});
</script>

2. 后端Python端的补充调整

(1)增加密文长度校验

AES密文长度必须是16的倍数,解码后可增加合法性校验:

new_data = base64.b64decode(data)
if len(new_data) % 16 != 0:
    return {"error": "Invalid ciphertext length"}

(2)返回可读字符串

原代码返回字节数组,需转为UTF-8字符串:

return final.decode('utf-8')

修正后的完整Python代码

import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from fastapi import FastAPI, Request

app = FastAPI()

@app.post('/decrypt/')
async def decrypt(request: Request):
    key = '1234567812345678'
    data = await request.body()
    try:
        new_data = base64.b64decode(data.strip())
        if len(new_data) % 16 != 0:
            return {"error": "Invalid ciphertext length"}
        cipher = AES.new(key.encode('utf-8'), AES.MODE_ECB)
        final = unpad(cipher.decrypt(new_data), AES.block_size)
        return final.decode('utf-8')
    except Exception as e:
        return {"error": str(e)}

3. 其他注意事项

  • ECB模式安全性:ECB模式不提供语义安全性,生产环境建议使用CBC/GCM等带IV的模式,GCM还支持自动校验完整性
  • 填充方式一致性:CryptoJS默认使用PKCS#7填充,Python的unpad函数默认也是PKCS#7,无需额外配置;若手动指定填充,需确保前后端一致
  • 请求头设置:发送纯文本密文时,建议设置Content-Type: text/plain,避免后端解析错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 13:35:44