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

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

err encrypted byte return 847    来源:    2025-03-14

在Node.js中,一些加密方法(如createCiphercreateDecipher)已经被废弃,推荐使用更安全的替代方法(如createCipherivcreateDecipheriv)。如果你需要将这些加密逻辑迁移到Golang中,可以使用Golang的crypto包来实现相同的功能。

1. Node.js中的加密示例(使用createCipheriv

假设你在Node.js中使用createCipheriv进行AES-256-CBC加密:

const crypto = require('crypto');

const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32); // 256-bit key
const iv = crypto.randomBytes(16);  // 128-bit IV

function encrypt(text) {
    let cipher = crypto.createCipheriv(algorithm, key, iv);
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return encrypted;
}

function decrypt(encrypted) {
    let decipher = crypto.createDecipheriv(algorithm, key, iv);
    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
}

const text = 'Hello, World!';
const encrypted = encrypt(text);
console.log('Encrypted:', encrypted);

const decrypted = decrypt(encrypted);
console.log('Decrypted:', decrypted);

2. Golang中的等效实现

在Golang中,你可以使用crypto/aescrypto/cipher包来实现相同的AES-256-CBC加密和解密。

package main

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

func encrypt(text string, key []byte, iv []byte) (string, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }

    plaintext := []byte(text)
    ciphertext := make([]byte, len(plaintext))

    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(ciphertext, plaintext)

    return hex.EncodeToString(ciphertext), nil
}

func decrypt(encrypted string, key []byte, iv []byte) (string, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }

    ciphertext, err := hex.DecodeString(encrypted)
    if err != nil {
        return "", err
    }

    plaintext := make([]byte, len(ciphertext))

    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(plaintext, ciphertext)

    return string(plaintext), nil
}

func main() {
    key := make([]byte, 32) // 256-bit key
    if _, err := io.ReadFull(rand.Reader, key); err != nil {
        panic(err)
    }

    iv := make([]byte, aes.BlockSize) // 128-bit IV
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }

    text := "Hello, World!"
    encrypted, err := encrypt(text, key, iv)
    if err != nil {
        panic(err)
    }
    fmt.Println("Encrypted:", encrypted)

    decrypted, err := decrypt(encrypted, key, iv)
    if err != nil {
        panic(err)
    }
    fmt.Println("Decrypted:", decrypted)
}

3. 关键点解释

  • 密钥和IV生成:在Golang中,我们使用crypto/rand包生成随机的密钥和IV,确保安全性。
  • 加密和解密:使用crypto/aescrypto/cipher包来实现AES-256-CBC加密和解密。
  • 编码:在Node.js中,加密后的数据通常以十六进制字符串形式存储,因此在Golang中我们也使用hex.EncodeToStringhex.DecodeString来进行编码和解码。

4. 注意事项

  • 密钥和IV的安全性:确保密钥和IV的安全性,不要硬编码在代码中,最好从安全的配置管理系统或环境变量中获取。
  • 填充:AES-CBC模式需要数据块大小对齐,因此在Node.js中默认使用PKCS7填充。在Golang中,你需要手动处理填充或使用支持填充的库。

5. 处理填充

如果你需要处理填充,可以使用以下代码:

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

func pkcs7Unpad(data []byte) ([]byte, error) {
    length := len(data)
    if length == 0 {
        return nil, fmt.Errorf("invalid padding")
    }
    padding := int(data[length-1])
    if padding > length {
        return nil, fmt.Errorf("invalid padding")
    }
    return data[:length-padding], nil
}

在加密和解密时使用这些函数来处理填充。

6. 完整示例(包含填充处理)

package main

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

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

func pkcs7Unpad(data []byte) ([]byte, error) {
    length := len(data)
    if length == 0 {
        return nil, fmt.Errorf("invalid padding")
    }
    padding := int(data[length-1])
    if padding > length {
        return nil, fmt.Errorf("invalid padding")
    }
    return data[:length-padding], nil
}

func encrypt(text string, key []byte, iv []byte) (string, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }

    plaintext := pkcs7Pad([]byte(text), aes.BlockSize)
    ciphertext := make([]byte, len(plaintext))

    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(ciphertext, plaintext)

    return hex.EncodeToString(ciphertext), nil
}

func decrypt(encrypted string, key []byte, iv []byte) (string, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }

    ciphertext, err := hex.DecodeString(encrypted)
    if err != nil {
        return "", err
    }

    plaintext := make([]byte, len(ciphertext))

    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(plaintext, ciphertext)

    plaintext, err = pkcs7Unpad(plaintext)
    if err != nil {
        return "", err
    }

    return string(plaintext), nil
}

func main() {
    key := make([]byte, 32) // 256-bit key
    if _, err := io.ReadFull(rand.Reader, key); err != nil {
        panic(err)
    }

    iv := make([]byte, aes.BlockSize) // 128-bit IV
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }

    text := "Hello, World!"
    encrypted, err := encrypt(text, key, iv)
    if err != nil {
        panic(err)
    }
    fmt.Println("Encrypted:", encrypted)

    decrypted, err := decrypt(encrypted, key, iv)
    if err != nil {
        panic(err)
    }
    fmt.Println("Decrypted:", decrypted)
}

通过这种方式,你可以安全地将Node.js中的废弃加密方法迁移到Golang中,并确保加密过程的安全性。