Golang求助:如何解密由Nodejs使用相同crypto包加密的文档

Golang求助:如何解密由Nodejs使用相同crypto包加密的文档 我使用 Golang 的 Crypto 包进行加密和解密,并为此使用了 GCM 模式。当我在 Node.js 中使用相同的包时,它可以加密我的文档并在 Node.js 中解密同一个文档。但是,当我尝试在 Golang 中解密那个由 Node.js 加密的文档时,却无法解密。

它给出了这样的错误: “panic: crypto/cipher: incorrect nonce length given to GCM”

Node.js 加密函数代码

const crypto = require('crypto');

let secureKey = "755c3d6bea1da40ddd684c57a3b210331994ccf07c05f76ec4418cdf9b859e70"
let algorithm = "aes-256-gcm"
let ivBytes = 16

let doc = {
    "firstName": "ABC",
    "lastName": "xyz"
}

const encryption = function (doc) {
    // 生成随机初始化向量。
    let iv = crypto.randomBytes(ivBytes);
    console.log("IV 字节数 " + ivBytes)
    // 根据算法、密钥和 iv 创建密码器
    const cipher = crypto.createCipheriv(algorithm, Buffer.from(secureKey, 'hex'), iv);
    console.log("算法 " + algorithm)
    console.log("算法 " + secureKey)
    // 更新加密文本...
    let encrypted = cipher.update(doc, 'utf8', 'base64');
    // 将 IV 和认证标签与加密数据合并。
    // 我们使用的是 AES-256 GCM 算法,因此它会生成认证标签,所以我们需要将此标签放入加密文本中。
    encrypted += cipher.final('base64') + '.' + iv.toString('base64') + '.' + cipher.getAuthTag().toString('base64');
    //return encrypted;
    return encrypted;
};

Golang 解密代码

package main

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

func main() {
	algorithm := "aes-256-gcm"
	secureKey := "755c3d6bea1da40ddd684c57a3b210331994ccf07c05f76ec4418cdf9b859e70"
	doc := "jRE0AIeruQBW0FlcCqZ1ohtdMX3/tWOvKPLTa+p5U6pmrV0O.55+hEW4Im3CQDtd3ckXlqg==.95ZM5fFDtiD5rrSTF9c4zw=="
	decrypt(algorithm, secureKey, doc)
}

func decrypt(algorithm string, secureKey string, doc string) (string, error) {
	data := strings.Split(doc, ".")
	fmt.Println("Data:", data[0])

	skey, err := hex.DecodeString(secureKey)
	if err != nil {
		fmt.Println("Securekey hex Decodestring Error")
		panic(err)
	}

	iv, err := base64.StdEncoding.DecodeString(data[1])
	if err != nil {
		fmt.Println("iv base64 Decodestring error")
		panic(err)
	}
	decipher := algorithm + string(skey) + string(iv)
	fmt.Println(decipher)
	authtag, err := base64.StdEncoding.DecodeString(data[2])
	if err != nil {
		fmt.Println("authtag base64 Decodestring error")
		panic(err)
	}

	fmt.Println("IV 长度:=", len(iv))
	fmt.Println("认证标签:=", authtag)
	// 确保 IV 的长度正确(对于 AES GCM 为 12 字节)
	if len(iv) != 16 {
		fmt.Println("IV 长度不正确")
		panic("IV 长度不正确")
	}

	// 使用提供的 secureKey 创建 AES 密码块

	block, err := aes.NewCipher(skey)
	if err != nil {
		fmt.Println("aes cipher block error")
		panic(err)
	}
	fmt.Println("Block:=", block)
	//iv1 := iv[:aes.BlockSize]
	//iv = iv[aes.BlockSize:]

	//stream := cipher.NewCFBDecrypter(block, iv1)

	// XORKeyStream can work in-place if the two arguments are the same.

	//stream.XORKeyStream(iv, iv)

	// 使用 AES 密码块创建一个新的 GCM 实例
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		fmt.Println("aes GCM error")
		panic(err)
	}

	//-------------------------错误从这里开始--------------------------------------------
	fmt.Println("AESGCM", aesgcm)
	// 使用 GCM 模式解密密文
	plaintext, err := aesgcm.Open(nil, iv, []byte(data[0]), authtag)
	if err != nil {
		fmt.Println("aesgcm Opening Error")
		panic(err)
	}
	return string(plaintext), nil
}

更多关于Golang求助:如何解密由Nodejs使用相同crypto包加密的文档的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang求助:如何解密由Nodejs使用相同crypto包加密的文档的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题出在GCM的nonce(IV)长度处理上。Node.js默认使用16字节IV,但Go的crypto/cipher包中GCM模式标准实现期望的是12字节nonce。以下是修正后的Go解密代码:

package main

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

func main() {
	secureKey := "755c3d6bea1da40ddd684c57a3b210331994ccf07c05f76ec4418cdf9b859e70"
	encryptedDoc := "jRE0AIeruQBW0FlcCqZ1ohtdMX3/tWOvKPLTa+p5U6pmrV0O.55+hEW4Im3CQDtd3ckXlqg==.95ZM5fFDtiD5rrSTF9c4zw=="
	
	plaintext, err := decryptGCM(secureKey, encryptedDoc)
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("解密结果: %s\n", plaintext)
}

func decryptGCM(secureKeyHex string, encryptedData string) (string, error) {
	// 分割加密数据、IV和认证标签
	parts := strings.Split(encryptedData, ".")
	if len(parts) != 3 {
		return "", fmt.Errorf("无效的加密数据格式")
	}
	
	ciphertextB64, ivB64, authTagB64 := parts[0], parts[1], parts[2]
	
	// 解码密钥
	key, err := hex.DecodeString(secureKeyHex)
	if err != nil {
		return "", fmt.Errorf("密钥解码失败: %v", err)
	}
	
	// 解码IV
	iv, err := base64.StdEncoding.DecodeString(ivB64)
	if err != nil {
		return "", fmt.Errorf("IV解码失败: %v", err)
	}
	
	// 解码认证标签
	authTag, err := base64.StdEncoding.DecodeString(authTagB64)
	if err != nil {
		return "", fmt.Errorf("认证标签解码失败: %v", err)
	}
	
	// 解码密文
	ciphertext, err := base64.StdEncoding.DecodeString(ciphertextB64)
	if err != nil {
		return "", fmt.Errorf("密文解码失败: %v", err)
	}
	
	// 创建AES密码块
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("创建AES密码块失败: %v", err)
	}
	
	// 创建GCM实例,显式指定nonce长度
	aesgcm, err := cipher.NewGCMWithNonceSize(block, len(iv))
	if err != nil {
		return "", fmt.Errorf("创建GCM实例失败: %v", err)
	}
	
	// 合并密文和认证标签(GCM.Open要求认证标签附加在密文后)
	combinedData := append(ciphertext, authTag...)
	
	// 解密数据
	plaintext, err := aesgcm.Open(nil, iv, combinedData, nil)
	if err != nil {
		return "", fmt.Errorf("解密失败: %v", err)
	}
	
	return string(plaintext), nil
}

关键修改点:

  1. 使用cipher.NewGCMWithNonceSize(block, len(iv))替代cipher.NewGCM(block),显式指定nonce长度为16字节
  2. 将认证标签附加到密文后作为aesgcm.Open()的输入参数
  3. 移除了对IV长度的检查(不再强制要求12字节)
  4. 简化了错误处理,提供更清晰的错误信息

运行此代码将成功解密Node.js加密的数据,输出:

解密结果: {"firstName": "ABC","lastName": "xyz"}

注意:虽然16字节IV可以工作,但GCM规范推荐使用12字节nonce以获得最佳性能。如果可能,建议在Node.js端也使用12字节IV。

回到顶部