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

使用Go解密SubtleCrypto AES-GCM加密字符串遇认证失败问题

解决Go解密浏览器SubtleCrypto AES-GCM时的"cipher: message authentication failed"问题

最可能的问题出在编码转换、数据拼接的细节差异上,以下是逐点排查和修正方案:

1. 密钥生成一致性检查

前端用SHA-256哈希密码生成AES密钥,需确保Go端密钥字节与前端完全一致:

  • 前端正确实现:
    const password = "your-pass";
    const encoder = new TextEncoder();
    const hash = await crypto.subtle.digest("SHA-256", encoder.encode(password));
    // 直接将SHA-256哈希结果作为raw格式导入AES密钥
    const key = await crypto.subtle.importKey("raw", hash, {name: "AES-GCM"}, false, ["encrypt"]);
    
  • Go端对应实现:
    password := "your-pass"
    hash := sha256.Sum256([]byte(password))
    key := hash[:] // 直接用SHA-256哈希结果作为AES密钥
    
  • 验证方式:把两端的哈希结果转十六进制字符串对比,确保完全相同。

2. IV与密文的编码/拼接错误(核心坑点)

前端不能直接将Uint8Array(IV/密文)转UTF-8字符串——无效UTF-8字节会被替换为�,导致数据损坏。正确做法是直接对字节数组做Base64编码,再用分隔符拼接:

  • 前端正确编码逻辑:
    // 工具函数:Uint8Array转标准Base64
    function uint8ToBase64(arr) {
      return btoa(String.fromCharCode(...new Uint8Array(arr)));
    }
    
    // 生成12字节IV
    const iv = crypto.getRandomValues(new Uint8Array(12));
    // 加密得到密文+标签(SubtleCrypto自动附加16字节GCM标签)
    const ciphertextWithTag = await crypto.subtle.encrypt(
      {name: "AES-GCM", iv: iv},
      key,
      encoder.encode("plaintext")
    );
    // 用分隔符拼接IV和密文的Base64
    const encryptedStr = `${uint8ToBase64(iv)}:${uint8ToBase64(ciphertextWithTag)}`;
    
  • 错误示例(要避免):
    // 错误:Uint8Array转UTF-8字符串会损坏数据
    const ivStr = new TextDecoder().decode(iv);
    const encryptedStr = btoa(ivStr + new TextDecoder().decode(ciphertextWithTag));
    

3. Go端解密的对应处理

Go端需要拆分前端传来的拼接字符串,分别解码IV和密文+标签,再做GCM解密:

import (
  "crypto/aes"
  "crypto/cipher"
  "crypto/sha256"
  "encoding/base64"
  "strings"
  "fmt"
)

func decrypt(password, encrypted string) (string, error) {
  // 拆分IV和密文的Base64
  parts := strings.Split(encrypted, ":")
  if len(parts) != 2 {
    return "", fmt.Errorf("invalid format")
  }

  // 解码IV(必须是12字节)
  iv, err := base64.StdEncoding.DecodeString(parts[0])
  if err != nil || len(iv) != 12 {
    return "", fmt.Errorf("invalid IV: %v", err)
  }

  // 解码密文+标签
  ciphertextWithTag, err := base64.StdEncoding.DecodeString(parts[1])
  if err != nil {
    return "", fmt.Errorf("invalid ciphertext: %v", err)
  }

  // 生成密钥
  hash := sha256.Sum256([]byte(password))
  block, err := aes.NewCipher(hash[:])
  if err != nil {
    return "", err
  }

  // GCM解密(自动验证标签)
  gcm, err := cipher.NewGCM(block)
  if err != nil {
    return "", err
  }
  plaintext, err := gcm.Open(nil, iv, ciphertextWithTag, nil)
  if err != nil {
    return "", fmt.Errorf("auth failed: %v", err)
  }
  return string(plaintext), nil
}

4. 其他排查点

  • 附加数据(AAD):如果前端加密时传入了additionalData参数,Go解密时必须传入完全相同的字节数据。
  • Base64编码类型:前端用btoa对应Go的base64.StdEncoding;如果前端用Base64URL编码,Go要改用base64.URLEncoding。
  • 标签长度:SubtleCrypto默认用16字节GCM标签,Go的GCM默认也是16字节,无需修改;若前端自定义标签长度,Go需用cipher.NewGCMWithTagSize对应设置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:50:23