将Rails ActiveSupport解密改写为Go时遇AES-GCM认证失败求助
Rails ActiveSupport 解密转Go失败(认证错误)解决方案
Rails加密核心逻辑(基于ActiveSupport::MessageEncryptor)
Rails端加密流程:
- 从
SECRET_KEY_BASE通过PBKDF2派生AES密钥:
- 盐值:默认
"encrypted cookie"(若Rails配置修改过需同步调整)- 迭代次数:1000次
- 密钥长度:32字节(对应AES-256-GCM)
- 生成12字节随机Nonce
- 明文先经Ruby Marshal序列化,再加密
- 生成16字节GCM认证标签
- 将
Nonce + 密文 + Tag拼接后做标准Base64编码输出
解密失败的常见原因及修正
1. 密钥派生参数错误
Go代码中必须严格匹配Rails的PBKDF2参数,否则生成的密钥不一致,直接导致认证失败。
2. 数据拆分错误
必须按12字节Nonce + 密文 + 16字节Tag的规则拆分解码后的字节数据,位置错误会导致标签验证失败。
3. Base64解码格式错误
Rails默认用标准Base64,若误用URL安全Base64解码,会导致数据损坏。
修正后的Go解密代码
package main import ( "crypto/aes" "crypto/cipher" "crypto/sha256" "encoding/base64" "fmt" "golang.org/x/crypto/pbkdf2" ) func DecryptRailsGCM(encryptedData, secretKeyBase string) ([]byte, error) { // 1. 标准Base64解码加密数据 data, err := base64.StdEncoding.DecodeString(encryptedData) if err != nil { return nil, fmt.Errorf("base64解码失败: %w", err) } // 2. 拆分Nonce、密文、Tag(Rails固定12字节Nonce,16字节Tag) const nonceSize = 12 const tagSize = 16 if len(data) < nonceSize+tagSize { return nil, fmt.Errorf("加密数据长度非法") } nonce := data[:nonceSize] ciphertext := data[nonceSize : len(data)-tagSize] tag := data[len(data)-tagSize:] // 3. 按Rails规则派生AES密钥 salt := []byte("encrypted cookie") key := pbkdf2.Key([]byte(secretKeyBase), salt, 1000, 32, sha256.New) // 4. 初始化AES-GCM block, err := aes.NewCipher(key) if err != nil { return nil, fmt.Errorf("AES初始化失败: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { return nil, fmt.Errorf("GCM初始化失败: %w", err) } // 5. 解密并验证标签 plaintext, err := gcm.Open(nil, nonce, ciphertext, tag) if err != nil { return nil, fmt.Errorf("GCM解密/验证失败: %w", err) } // 注:plaintext是Ruby Marshal格式,需用go-ruby-marshal库解析为原始字符串 return plaintext, nil } func main() { // 替换为实际的SECRET_KEY_BASE和Rails加密数据 secretKeyBase := "your-secret-key-base-value" encryptedData := "your-encrypted-string-from-rails" result, err := DecryptRailsGCM(encryptedData, secretKeyBase) if err != nil { fmt.Printf("解密失败: %v\n", err) return } fmt.Printf("Marshal格式明文: %v\n", result) // 如需解析为原始字符串,可使用github.com/foobaz/go-ruby-marshal库 }
额外注意事项
- 若Rails修改过加密盐值(如
config.action_dispatch.encrypted_cookie_salt),需同步替换代码中的salt变量 - 若使用AES-128,将密钥长度改为16字节即可(Rails默认是AES-256)
- 解密后的明文是Ruby Marshal序列化格式,必须用对应Go库解析才能得到原始业务数据
内容的提问来源于stack exchange,提问作者lnxsr
相关产品推荐
相关产品推荐

