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

Go语言中JSON编组后AES256加密失效问题求助

问题

我尝试在Go中实现AES256加解密文本,代码对普通字符串有效,但对JSON编码的结构体无效。我困惑的点在于JSON编码后的数据本质也是字符串,不明白为什么会失效。

有效代码段(普通字符串)

// Using strings
pt := "This is a secret"
c := EncryptAES([]byte(key), []byte(pt))
fmt.Printf("Initial string: %#v\n", pt)
fmt.Printf("Coded: %v\n", c)
decoded := DecryptAES([]byte(key), c)
fmt.Printf("Decoded: %s\n", decoded)

无效代码段(JSON字符串)

// Using JSON strings
p2 := []record{{Name: "John", Age: 20}, {Name: "Jane", Age: 25}}
m2, _ := json.Marshal(p2)
fmt.Printf("m2 = %s\n", string(m2))
fmt.Printf("m2 = %#v\n", string(m2))
coded := EncryptAES([]byte(key), m2)
decoded = DecryptAES([]byte(key), coded)
fmt.Printf("Decoded: %s\n", decoded)

使用Go版本:go version go1.18 darwin/arm64

完整代码:

package main

import (
    "crypto/aes"
    "encoding/json"
    "fmt"
)

func CheckError(err error) {
    if err != nil {
        panic(err)
    }
}

type record struct {
    Name string `json:"first_name"`
    Age  int    `json:"age"`
}

func main() {

    // cipher key
    key := "thisis32bitlongpassphraseimusing"
    fmt.Printf("len of key %d\n", len(key))

    // Using strings
    pt := "This is a secret"
    c := EncryptAES([]byte(key), []byte(pt))
    fmt.Printf("Initial string: %#v\n", pt)
    fmt.Printf("Coded: %v\n", c)
    decoded := DecryptAES([]byte(key), c)
    fmt.Printf("Decoded: %s\n", decoded)

    // Using JSON strings
    p2 := []record{{Name: "John", Age: 20}, {Name: "Jane", Age: 25}}
    m2, _ := json.Marshal(p2)
    fmt.Printf("m2 = %s\n", string(m2))
    fmt.Printf("m2 = %#v\n", string(m2))
    coded := EncryptAES([]byte(key), m2)
    decoded = DecryptAES([]byte(key), coded)
    fmt.Printf("Decoded: %s\n", decoded)

}

func EncryptAES(key []byte, plaintext []byte) []byte {
    c, err := aes.NewCipher(key)
    CheckError(err)
    out := make([]byte, len(plaintext))
    c.Encrypt(out, []byte(plaintext))
    return out
}

func DecryptAES(key []byte, ct []byte) []byte {
    c, err := aes.NewCipher(key)
    CheckError(err)
    pt := make([]byte, len(ct))
    c.Decrypt(pt, ct)
    return pt
}

解答

问题根源

你当前使用的是AES-ECB模式(直接调用aes.NewCipher的Encrypt/Decrypt方法,本质是ECB实现),该模式要求明文长度必须是AES块大小的整数倍(AES块大小固定为16字节):

  • 普通字符串"This is a secret"刚好是16字节,符合ECB的长度要求,因此能正常加解密;
  • JSON编码后的m2长度为58字节,不是16的整数倍,c.Encrypt只会处理前48字节(3个完整块),剩余10字节被丢弃,解密后数据缺失导致JSON格式损坏,无法正常显示。

另外,ECB模式本身不安全,相同明文会生成相同密文,容易被破解。

修复方案

改用AES-GCM模式(安全的认证加密模式),该模式无需手动填充明文,可处理任意长度的数据,同时能验证数据完整性。以下是修复后的完整代码:

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/json"
    "fmt"
    "io"
)

func CheckError(err error) {
    if err != nil {
        panic(err)
    }
}

type record struct {
    Name string `json:"first_name"`
    Age  int    `json:"age"`
}

func main() {
    // 32字节密钥,对应AES-256
    key := "thisis32bitlongpassphraseimusing"
    fmt.Printf("len of key %d\n", len(key))

    // 普通字符串测试
    pt := "This is a secret"
    c, nonce := EncryptAES([]byte(key), []byte(pt))
    fmt.Printf("Initial string: %#v\n", pt)
    fmt.Printf("Coded: %v\n", c)
    decoded := DecryptAES([]byte(key), nonce, c)
    fmt.Printf("Decoded: %s\n", decoded)

    // JSON字符串测试
    p2 := []record{{Name: "John", Age: 20}, {Name: "Jane", Age: 25}}
    m2, _ := json.Marshal(p2)
    fmt.Printf("m2 = %s\n", string(m2))
    fmt.Printf("m2 = %#v\n", string(m2))
    coded, nonce2 := EncryptAES([]byte(key), m2)
    decoded = DecryptAES([]byte(key), nonce2, coded)
    fmt.Printf("Decoded: %s\n", decoded)

    // 验证JSON可解析性
    var records []record
    err := json.Unmarshal(decoded, &records)
    CheckError(err)
    fmt.Printf("Parsed records: %+v\n", records)
}

// EncryptAES 使用AES-GCM模式加密,自动生成随机nonce
func EncryptAES(key []byte, plaintext []byte) ([]byte, []byte) {
    block, err := aes.NewCipher(key)
    CheckError(err)

    // GCM模式推荐使用12字节随机nonce
    nonce := make([]byte, 12)
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        panic(err)
    }

    gcm, err := cipher.NewGCM(block)
    CheckError(err)

    ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
    return ciphertext, nonce
}

// DecryptAES 使用AES-GCM模式解密,需传入加密时的nonce
func DecryptAES(key []byte, nonce []byte, ciphertext []byte) []byte {
    block, err := aes.NewCipher(key)
    CheckError(err)

    gcm, err := cipher.NewGCM(block)
    CheckError(err)

    plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
    CheckError(err)
    return plaintext
}

关键改进点

  1. 安全模式:AES-GCM同时保证机密性和数据完整性,避免篡改;
  2. 自动适配长度:无需手动填充明文,支持任意长度的数据;
  3. 随机Nonce:每次加密生成随机12字节nonce,相同明文加密结果不同,提升安全性,需将nonce与密文一同存储/传输。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 04:45:35