如何在Go中实现CryptoJS.AES.decrypt解密功能
问题:用Go实现CryptoJS AES解密失败
我有一段可用的CryptoJS AES解密代码,能通过指定密钥解密密文:
var CryptoJS = require('crypto-js'); let encrypted = 'U2FsdGVkX19dGrnVQb3BYZ9SWtQBCt2XZDeWFBEfWoi3qnSHg26aQnADUKjTDtSgpPUDzXc59XrIBsTZp9IRg4dYy9Lo0/GjSVn8qlINhE8NJXozQvUNPJSs6nvWSiivCsh9KHGsWFEusMeTefM9V2hKRpQHnFp5VHDLPgGImjHXOQO71eO4qFoQ3ESqUdnDO94nlTjcJU8AP+GabRMjHP08OVz87uyV4SfvT7AZhAYuOPOumHbJaV5nHz2ouqBZgXR3OK07AUk6V+Faz43BT3Qd2+xGLyz8C+teekmudVhmGlnzvMK/VUs0bNDUttdVVnkz/VbXVOj++07uhDsJoMKzZQYTNLkIl9HnuXDmhKOrLSYs2EYpNrH9T/2ZP3nwqJaI7bbLBCfCpNFGNLY4jyNou4Bl5GEK6sxg0zraUCZTgvfuuvBpKWFcFhPvYf8eJGoI3dOWTRWyRnS6e4j3bg=='; let key = 'KBlgcIOrad7EYpiz9LNNTMSg98QoqSVK'; var decrypted = CryptoJS.AES.decrypt(encrypted, key); let data = decrypted.toString(CryptoJS.enc.Utf8); console.log(data);
我尝试用Go实现相同逻辑但得到乱码,我的Go代码如下:
package main import ( "crypto/aes" "crypto/cipher" "encoding/base64" "fmt" ) func main() { encrypted := "U2FsdGVkX19dGrnVQb3BYZ9SWtQBCt2XZDeWFBEfWoi3qnSHg26aQnADUKjTDtSgpPUDzXc59XrIBsTZp9IRg4dYy9Lo0/GjSVn8qlINhE8NJXozQvUNPJSs6nvWSiivCsh9KHGsWFEusMeTefM9V2hKRpQHnFp5VHDLPgGImjHXOQO71eO4qFoQ3ESqUdnDO94nlTjcJU8AP+GabRMjHP08OVz87uyV4SfvT7AZhAYuOPOumHbJaV5nHz2ouqBZgXR3OK07AUk6V+Faz43BT3Qd2+xGLyz8C+teekmudVhmGlnzvMK/VUs0bNDUttdVVnkz/VbXVOj++07uhDsJoMKzZQYTNLkIl9HnuXDmhKOrLSYs2EYpNrH9T/2ZP3nwqJaI7bbLBCfCpNFGNLY4jyNou4Bl5GEK6sxg0zraUCZTgvfuuvBpKWFcFhPvYf8eJGoI3dOWTRWyRnS6e4j3bg==" key := "KBlgcIOrad7EYpiz9LNNTMSg98QoqSVK" e, _ := base64.StdEncoding.DecodeString(encrypted) k := []byte(key) dst, err := decryptAES(e, k) if err != nil { panic(err) } fmt.Println(string(dst)) } func decryptAES(ciphertext []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } if len(ciphertext) < aes.BlockSize { return nil, fmt.Errorf("ciphertext is too short") } iv := ciphertext[:aes.BlockSize] ciphertext = ciphertext[aes.BlockSize:] stream := cipher.NewCFBDecrypter(block, iv) plaintext := make([]byte, len(ciphertext)) stream.XORKeyStream(plaintext, ciphertext) return plaintext, nil }
运行后得到乱码结果:
�9�������ff��ܱ�������jTY:���SV�PZ_LDw%�z��0���&���j@'��_H��0+����b�8�b}�;Yڸ�d���0�^P얿L�aC�3�2�_m��d�h� -��%Z(0��c�v�r�:�=�>}���b��n������g�lu}�������&�3�.�%|M���fxdGW27z�D��J���"(�[D�+<��n�z�4:����� ��iZs�ix��G�$��.= �3J��5{"g��#z ���Z�#���@���Xs��#�ZR;���VI������ ��w�X/�bp�r ��
解决方案
你的Go代码无法解密的核心原因是CryptoJS的默认行为和你手动实现的AES逻辑不匹配:
- CryptoJS处理的密文是OpenSSL格式(开头为
Salted__),它不会直接用你传入的key作为AES密钥,而是通过EVP_BytesToKey算法(基于MD5哈希,迭代1次),结合密文中的盐值生成真正的AES密钥和IV。 - CryptoJS默认使用AES-CBC模式+PKCS7填充,而你的Go代码用了CFB模式,这也是导致乱码的原因之一。
修复后的Go代码如下:
package main import ( "crypto/aes" "crypto/cipher" "crypto/md5" "encoding/base64" "fmt" "strings" ) // EVP_BytesToKey 实现OpenSSL的EVP_BytesToKey算法,生成AES密钥和IV func evpBytesToKey(password, salt []byte, keyLen, ivLen int) ([]byte, []byte) { var key, iv []byte var digest []byte for len(key) < keyLen || len(iv) < ivLen { hash := md5.New() if len(digest) > 0 { hash.Write(digest) } hash.Write(password) hash.Write(salt) digest = hash.Sum(nil) key = append(key, digest[:]...) if len(key) > keyLen { key = key[:keyLen] } if len(iv) < ivLen { remaining := ivLen - len(iv) if len(digest) > remaining { iv = append(iv, digest[:remaining]...) } else { iv = append(iv, digest[:]...) } } } return key, iv } // pkcs7Unpadding 去除PKCS7填充 func pkcs7Unpadding(data []byte) []byte { if len(data) == 0 { return data } padding := data[len(data)-1] return data[:len(data)-int(padding)] } func decryptAES(ciphertext []byte, password []byte) ([]byte, error) { // 解析OpenSSL格式密文:Salted__ + 8字节盐 + 密文 if !strings.HasPrefix(string(ciphertext), "Salted__") { return nil, fmt.Errorf("invalid OpenSSL format ciphertext") } salt := ciphertext[8:16] actualCiphertext := ciphertext[16:] // 生成AES-256密钥和IV(CryptoJS默认AES-256) key, iv := evpBytesToKey(password, salt, 32, aes.BlockSize) // 创建AES块 block, err := aes.NewCipher(key) if err != nil { return nil, err } // CBC模式解密 mode := cipher.NewCBCDecrypter(block, iv) plaintext := make([]byte, len(actualCiphertext)) mode.CryptBlocks(plaintext, actualCiphertext) // 去除PKCS7填充 plaintext = pkcs7Unpadding(plaintext) return plaintext, nil } func main() { encrypted := "U2FsdGVkX19dGrnVQb3BYZ9SWtQBCt2XZDeWFBEfWoi3qnSHg26aQnADUKjTDtSgpPUDzXc59XrIBsTZp9IRg4dYy9Lo0/GjSVn8qlINhE8NJXozQvUNPJSs6nvWSiivCsh9KHGsWFEusMeTefM9V2hKRpQHnFp5VHDLPgGImjHXOQO71eO4qFoQ3ESqUdnDO94nlTjcJU8AP+GabRMjHP08OVz87uyV4SfvT7AZhAYuOPOumHbJaV5nHz2ouqBZgXR3OK07AUk6V+Faz43BT3Qd2+xGLyz8C+teekmudVhmGlnzvMK/VUs0bNDUttdVVnkz/VbXVOj++07uhDsJoMKzZQYTNLkIl9HnuXDmhKOrLSYs2EYpNrH9T/2ZP3nwqJaI7bbLBCfCpNFGNLY4jyNou4Bl5GEK6sxg0zraUCZTgvfuuvBpKWFcFhPvYf8eJGoI3dOWTRWyRnS6e4j3bg==" key := "KBlgcIOrad7EYpiz9LNNTMSg98QoqSVK" e, err := base64.StdEncoding.DecodeString(encrypted) if err != nil { panic(err) } k := []byte(key) dst, err := decryptAES(e, k) if err != nil { panic(err) } fmt.Println(string(dst)) }
这段代码做了以下关键调整:
- 实现了
evpBytesToKey函数,和CryptoJS的密钥生成逻辑对齐 - 解析OpenSSL格式的密文,提取盐值
- 使用AES-CBC模式解密,之后去除PKCS7填充
- 处理了所有可能的错误(比如密文格式校验)
运行这段代码就能得到和CryptoJS一致的解密结果。
内容的提问来源于stack exchange,提问作者PhiHungTF
相关产品推荐
相关产品推荐

