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

Golang实现AES-128 ECB加密后JS端解密失败问题排查

Golang实现AES-128加密跨端解密失败问题

现有实现代码

Go端加密工具函数

func EncryptAES(key []byte, plaintext string) (string, error) {
    // create cipher
    c, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }

    // allocate space for ciphered data
    out := make([]byte, len(plaintext))

    for i := 1; i <= len(plaintext)/16; i++ {
        tempBuf := make([]byte, 16)

        offset := (i - 1) * 16
        limit := offset + 16
        // encrypt
        c.Encrypt(tempBuf, []byte(plaintext[offset:limit]))

        for j := 0; j < len(tempBuf); j++ {
            out[offset+j] = tempBuf[j]
        }
    }
    // return hex string
    return hex.EncodeToString(out), nil
}

Go端加密调用流程

hasher := sha256.New()
hasher.Write([]byte(word))
sha := hasher.Sum(nil)

cypherText := word + userSessionKey

for len([]byte(cypherText))%16 != 0 {
    cypherText += "0"
}

sha128 := sha[:len(sha)/2]

captchaKey, err := utils.EncryptAES([]byte(hex.EncodeToString(sha128)), cypherText)
if err != nil {
    SendErrorResponse(ctx, fasthttp.StatusInternalServerError, []byte("error generating aes session key "+err.Error()))
    return
}

该流程逻辑:

  • 对word做SHA256哈希
  • 拼接word与userSessionKey作为待加密明文
  • 明文末尾追加字符"0",将长度补齐为16的倍数
  • 取SHA256哈希结果的前半段转十六进制字符串作为AES密钥
  • 调用加密函数生成十六进制格式密文

JS端解密函数

async function decryptAES128(key, cipherText){
    let hash = CryptoJS.SHA256(key).toString();
    hash = hash.substring(0, hash.length/2);

    console.log(hash);
    console.log(cipherText)

    const bytes = await CryptoJS.AES.decrypt(CryptoJS.enc.Hex.parse(cipherText), CryptoJS.enc.Hex.parse(hash), { mode: CryptoJS.mode.ECB });

    return CryptoJS.enc.Utf8.stringify(bytes.words)
}

故障表现

生成的密钥和密文在AES在线加解密网站测试时报错,JS端解密函数执行后无任何输出,无法正常解密得到明文。

问题原因

  • 密钥长度不符合规范:AES-128要求密钥长度固定为16字节,原Go代码将16字节的SHA256片段转成32字符的十六进制字符串作为密钥传入,实际密钥长度为32字节,会被Go标准库识别为AES-256算法,和预期的AES-128不匹配。
  • 填充逻辑非标准:手动追加字符"0"的填充方式不符合块加密通用的PKCS#7填充规则,且当明文长度刚好为16的倍数时不会追加填充,会导致解密端校验块长度失败。
  • 两端逻辑不匹配:JS端解密时,CryptoJS默认将传入的字符串作为口令派生密钥,而非直接作为原始密钥使用;同时JS端密文解析、结果序列化的逻辑都存在错误,且CryptoJS.AES.decrypt是同步方法,不需要await调用。

修正后代码

Go端修正

首先引入必要依赖:

import (
    "bytes"
    "crypto/aes"
    "crypto/sha256"
    "encoding/hex"
    "errors"
)

修正加密函数,加入标准PKCS7填充,增加密钥长度校验:

func EncryptAES(key []byte, plaintext string) (string, error) {
    // 校验AES-128密钥长度必须为16字节
    if len(key) != 16 {
        return "", errors.New("aes-128 key length must be 16 bytes")
    }
    c, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }
    // 标准PKCS7填充
    plainBytes := []byte(plaintext)
    padLen := 16 - len(plainBytes)%16
    plainBytes = append(plainBytes, bytes.Repeat([]byte{byte(padLen)}, padLen)...)

    out := make([]byte, len(plainBytes))
    // ECB模式分块加密
    blockCount := len(plainBytes) / 16
    for i := 0; i < blockCount; i++ {
        offset := i * 16
        c.Encrypt(out[offset:offset+16], plainBytes[offset:offset+16])
    }
    return hex.EncodeToString(out), nil
}

修正加密调用流程,去掉手动补0逻辑,直接取SHA256前16字节作为密钥:

hasher := sha256.New()
hasher.Write([]byte(word))
sha := hasher.Sum(nil)
// 直接取前16字节作为AES-128密钥,不需要转十六进制
aesKey := sha[:16]

cypherText := word + userSessionKey
// 加密函数内部已实现标准PKCS7填充,无需手动补"0"
captchaKey, err := utils.EncryptAES(aesKey, cypherText)
if err != nil {
    SendErrorResponse(ctx, fasthttp.StatusInternalServerError, []byte("error generating aes session key "+err.Error()))
    return
}

JS端修正

对齐Go端密钥生成逻辑,修正CryptoJS调用参数:

function decryptAES128(key, cipherText){
    // 和Go端对齐:对原始key做SHA256,取前16字节(对应十六进制前32位)作为密钥
    const hash = CryptoJS.SHA256(key).toString();
    const aesKey = CryptoJS.enc.Hex.parse(hash.substring(0, 32));

    // ECB模式,PKCS7填充(CryptoJS默认填充规则为PKCS7,可显式声明)
    const bytes = CryptoJS.AES.decrypt(
        CryptoJS.enc.Hex.parse(cipherText).toString(CryptoJS.enc.Base64),
        aesKey,
        {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        }
    );

    return bytes.toString(CryptoJS.enc.Utf8);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:51:23