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 }
关键改进点
- 安全模式:AES-GCM同时保证机密性和数据完整性,避免篡改;
- 自动适配长度:无需手动填充明文,支持任意长度的数据;
- 随机Nonce:每次加密生成随机12字节nonce,相同明文加密结果不同,提升安全性,需将nonce与密文一同存储/传输。
内容的提问来源于stack exchange,提问作者Serge Hulne
相关产品推荐
相关产品推荐

