插件窝 干货文章 Node.js废弃加密方法如何用Golang安全地重构?

Node.js废弃加密方法如何用Golang安全地重构?

加密 encrypted crypto return 489    来源:    2025-03-11

在Node.js中,一些加密方法(如crypto.createCiphercrypto.createDecipher)已经被废弃,推荐使用更安全的替代方法(如crypto.createCipherivcrypto.createDecipheriv)。如果你需要将这些废弃的加密方法用Golang重构,以下是详细的步骤和代码示例。

1. 理解Node.js中的加密方法

假设你在Node.js中使用的是crypto.createCiphercrypto.createDecipher,这些方法使用的是AES加密算法,并且默认使用ECB模式(不推荐)或CBC模式。

例如,以下是一个使用crypto.createCipher的Node.js代码示例:

const crypto = require('crypto');

function encrypt(text, key) {
    const cipher = crypto.createCipher('aes-256-cbc', key);
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return encrypted;
}

function decrypt(encrypted, key) {
    const decipher = crypto.createDecipher('aes-256-cbc', key);
    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
}

const key = 'my-secret-key';
const text = 'Hello, World!';
const encrypted = encrypt(text, key);
console.log('Encrypted:', encrypted);
const decrypted = decrypt(encrypted, key);
console.log('Decrypted:', decrypted);

2. Golang中的加密方法

在Golang中,你可以使用crypto/aescrypto/cipher包来实现相同的加密和解密功能。需要注意的是,Golang中的AES加密需要明确的IV(初始化向量),并且通常使用CBC模式。

以下是一个使用Golang实现相同功能的示例:

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/md5"
    "crypto/rand"
    "encoding/hex"
    "errors"
    "fmt"
    "io"
)

// 生成密钥的MD5哈希
func createHash(key string) string {
    hasher := md5.New()
    hasher.Write([]byte(key))
    return hex.EncodeToString(hasher.Sum(nil))
}

// 加密函数
func encrypt(data []byte, passphrase string) ([]byte, error) {
    block, err := aes.NewCipher([]byte(createHash(passphrase)))
    if err != nil {
        return nil, err
    }

    // 生成随机的IV
    iv := make([]byte, aes.BlockSize)
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return nil, err
    }

    // 使用CBC模式加密
    stream := cipher.NewCBCEncrypter(block, iv)
    paddedData := pkcs7Pad(data, aes.BlockSize)
    encrypted := make([]byte, len(paddedData))
    stream.CryptBlocks(encrypted, paddedData)

    // 将IV和加密数据合并
    return append(iv, encrypted...), nil
}

// 解密函数
func decrypt(data []byte, passphrase string) ([]byte, error) {
    block, err := aes.NewCipher([]byte(createHash(passphrase)))
    if err != nil {
        return nil, err
    }

    if len(data) < aes.BlockSize {
        return nil, errors.New("ciphertext too short")
    }

    // 提取IV
    iv := data[:aes.BlockSize]
    data = data[aes.BlockSize:]

    // 使用CBC模式解密
    stream := cipher.NewCBCDecrypter(block, iv)
    decrypted := make([]byte, len(data))
    stream.CryptBlocks(decrypted, data)

    // 去除填充
    return pkcs7Unpad(decrypted, aes.BlockSize)
}

// PKCS7填充
func pkcs7Pad(data []byte, blockSize int) []byte {
    padding := blockSize - len(data)%blockSize
    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(data, padtext...)
}

// PKCS7去填充
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
    length := len(data)
    if length == 0 {
        return nil, errors.New("invalid padding")
    }
    padding := int(data[length-1])
    if padding > blockSize || padding == 0 {
        return nil, errors.New("invalid padding")
    }
    for i := length - padding; i < length; i++ {
        if int(data[i]) != padding {
            return nil, errors.New("invalid padding")
        }
    }
    return data[:length-padding], nil
}

func main() {
    key := "my-secret-key"
    text := "Hello, World!"

    encrypted, err := encrypt([]byte(text), key)
    if err != nil {
        fmt.Println("Encryption error:", err)
        return
    }
    fmt.Println("Encrypted:", hex.EncodeToString(encrypted))

    decrypted, err := decrypt(encrypted, key)
    if err != nil {
        fmt.Println("Decryption error:", err)
        return
    }
    fmt.Println("Decrypted:", string(decrypted))
}

3. 关键点解释

  • 密钥生成:在Node.js中,crypto.createCipher会自动处理密钥的哈希。在Golang中,我们使用MD5哈希来生成一个32字节的密钥。
  • IV(初始化向量):在Golang中,IV是必需的,并且应该是随机的。我们将IV与加密数据一起存储,以便在解密时使用。
  • 填充:AES加密要求数据长度是块大小的倍数。我们使用PKCS7填充来确保数据长度正确。
  • 加密模式:我们使用CBC模式,这是Node.js中crypto.createCipher的默认模式。

4. 安全性考虑

  • 密钥管理:确保密钥的安全性,避免硬编码在代码中。
  • IV的随机性:每次加密时都应生成一个新的随机IV,以增强安全性。
  • 填充方案:使用标准的填充方案(如PKCS7)来确保数据长度正确。

通过以上步骤,你可以将Node.js中废弃的加密方法安全地重构为Golang实现。