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

将Rails ActiveSupport解密改写为Go时遇AES-GCM认证失败求助

Rails ActiveSupport 解密转Go失败(认证错误)解决方案

Rails加密核心逻辑(基于ActiveSupport::MessageEncryptor)

Rails端加密流程:

  1. 从SECRET_KEY_BASE通过PBKDF2派生AES密钥:
    • 盐值:默认"encrypted cookie"(若Rails配置修改过需同步调整)
    • 迭代次数:1000次
    • 密钥长度:32字节(对应AES-256-GCM)
  2. 生成12字节随机Nonce
  3. 明文先经Ruby Marshal序列化,再加密
  4. 生成16字节GCM认证标签
  5. 将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:16:04