dev #31

Merged
bvbej merged 2 commits from dev into main 2026-01-21 16:41:13 +08:00
6 changed files with 527 additions and 0 deletions

71
pkg/crypto/aes.go Normal file
View File

@@ -0,0 +1,71 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
)
// AESEncryptor AES-GCM加密器
type AESEncryptor struct {
key []byte
}
// NewAESEncryptor 创建AES加密器
func NewAESEncryptor(key string) (*AESEncryptor, error) {
// 使用SHA256生成32字节密钥
hash := sha256.Sum256([]byte(key))
return &AESEncryptor{key: hash[:]}, nil
}
func (e *AESEncryptor) Encrypt(plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(e.key)
if err != nil {
return nil, fmt.Errorf("创建cipher失败: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("创建GCM失败: %w", err)
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, fmt.Errorf("生成nonce失败: %w", err)
}
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
return ciphertext, nil
}
func (e *AESEncryptor) Decrypt(ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(e.key)
if err != nil {
return nil, fmt.Errorf("创建cipher失败: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("创建GCM失败: %w", err)
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, fmt.Errorf("密文长度不足")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("解密失败: %w", err)
}
return plaintext, nil
}
func (e *AESEncryptor) Name() string {
return "AES-GCM-256"
}

71
pkg/crypto/compress.go Normal file
View File

@@ -0,0 +1,71 @@
package crypto
import (
"bytes"
"compress/gzip"
"io"
)
// CompressEncryptor 压缩加密器
type CompressEncryptor struct {
baseEncryptor Encryptor
}
func NewCompressEncryptor(encryptor Encryptor) *CompressEncryptor {
return &CompressEncryptor{baseEncryptor: encryptor}
}
// Encrypt 先压缩后加密
func (c *CompressEncryptor) Encrypt(plaintext []byte) ([]byte, error) {
// 1. 压缩
compressed, err := c.compress(plaintext)
if err != nil {
return nil, err
}
// 2. 加密
return c.baseEncryptor.Encrypt(compressed)
}
// Decrypt 先解密后解压
func (c *CompressEncryptor) Decrypt(ciphertext []byte) ([]byte, error) {
// 1. 解密
compressed, err := c.baseEncryptor.Decrypt(ciphertext)
if err != nil {
return nil, err
}
// 2. 解压
return c.decompress(compressed)
}
func (c *CompressEncryptor) Name() string {
return "GZIP-" + c.baseEncryptor.Name()
}
// compress 使用gzip压缩
func (c *CompressEncryptor) compress(data []byte) ([]byte, error) {
var buf bytes.Buffer
writer := gzip.NewWriter(&buf)
if _, err := writer.Write(data); err != nil {
return nil, err
}
if err := writer.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// decompress 使用gzip解压
func (c *CompressEncryptor) decompress(data []byte) ([]byte, error) {
reader, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
return nil, err
}
defer func() { _ = reader.Close() }()
return io.ReadAll(reader)
}

36
pkg/crypto/hmac.go Normal file
View File

@@ -0,0 +1,36 @@
package crypto
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
)
// HMACSigner HMAC签名器
type HMACSigner struct {
key []byte
}
// NewHMACSigner 创建HMAC签名器
func NewHMACSigner(key string) *HMACSigner {
return &HMACSigner{key: []byte(key)}
}
func (s *HMACSigner) Sign(data []byte) ([]byte, error) {
h := hmac.New(sha256.New, s.key)
h.Write(data)
return h.Sum(nil), nil
}
func (s *HMACSigner) Verify(data, signature []byte) error {
expected, err := s.Sign(data)
if err != nil {
return err
}
if !hmac.Equal(expected, signature) {
return fmt.Errorf("签名验证失败")
}
return nil
}

22
pkg/crypto/interface.go Normal file
View File

@@ -0,0 +1,22 @@
package crypto
// Encryptor 加密器接口
type Encryptor interface {
// Encrypt 加密数据
Encrypt(plaintext []byte) ([]byte, error)
// Decrypt 解密数据
Decrypt(ciphertext []byte) ([]byte, error)
// Name 返回加密算法名称
Name() string
}
// Signer 签名器接口
type Signer interface {
// Sign 生成签名
Sign(data []byte) ([]byte, error)
// Verify 验证签名
Verify(data, signature []byte) error
}

83
pkg/crypto/rsa.go Normal file
View File

@@ -0,0 +1,83 @@
package crypto
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
)
// RSAEncryptor RSA加密器
type RSAEncryptor struct {
publicKey *rsa.PublicKey
privateKey *rsa.PrivateKey
}
// NewRSAEncryptor 创建RSA加密器
func NewRSAEncryptor(publicKeyPEM, privateKeyPEM []byte) (*RSAEncryptor, error) {
encryptor := &RSAEncryptor{}
if len(publicKeyPEM) > 0 {
block, _ := pem.Decode(publicKeyPEM)
if block == nil {
return nil, fmt.Errorf("解析公钥失败")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("解析公钥失败: %w", err)
}
var ok bool
encryptor.publicKey, ok = pub.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("不是RSA公钥")
}
} else {
return nil, fmt.Errorf("公钥未设置")
}
if len(privateKeyPEM) > 0 {
block, _ := pem.Decode(privateKeyPEM)
if block == nil {
return nil, fmt.Errorf("解析私钥失败")
}
priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("解析私钥失败: %w", err)
}
var ok bool
encryptor.privateKey, ok = priv.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("不是RSA私钥")
}
} else {
return nil, fmt.Errorf("私钥未设置")
}
return encryptor, nil
}
func (e *RSAEncryptor) Encrypt(plaintext []byte) ([]byte, error) {
if e.publicKey == nil {
return nil, fmt.Errorf("公钥未设置")
}
return rsa.EncryptOAEP(sha256.New(), rand.Reader, e.publicKey, plaintext, nil)
}
func (e *RSAEncryptor) Decrypt(ciphertext []byte) ([]byte, error) {
if e.privateKey == nil {
return nil, fmt.Errorf("私钥未设置")
}
return rsa.DecryptOAEP(sha256.New(), rand.Reader, e.privateKey, ciphertext, nil)
}
func (e *RSAEncryptor) Name() string {
return "RSA-OAEP-SHA256"
}

244
pkg/rsa/rsa.js Normal file
View File

@@ -0,0 +1,244 @@
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
class KeyPairGenerator {
constructor(options = {}) {
this.options = {
modulusLength: 2048, // 2048位在安全性和性能间取得平衡
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc', // 可选加密私钥
passphrase: options.passphrase || '' // 私钥密码
},
...options
};
}
// 生成密钥对
generateKeyPair() {
return new Promise((resolve, reject) => {
crypto.generateKeyPair('rsa', this.options, (err, publicKey, privateKey) => {
if (err) {
reject(err);
return;
}
resolve({ publicKey, privateKey });
});
});
}
// 保存密钥到文件
saveKeyToFile(key, filename, directory = './keys') {
if (!fs.existsSync(directory)) {
fs.mkdirSync(directory, { recursive: true });
}
const filePath = path.join(directory, filename);
fs.writeFileSync(filePath, key);
console.log(`${filename} 已保存到: ${filePath}`);
return filePath;
}
// 验证密钥对
validateKeyPair(publicKey, privateKey, passphrase = '') {
try {
// 使用公钥加密测试数据
const testData = 'test-signature-validation';
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(testData));
// 使用私钥解密
const decrypted = crypto.privateDecrypt(
{ key: privateKey, passphrase },
encrypted
);
return decrypted.toString() === testData;
} catch (error) {
console.error('❌ 密钥对验证失败:', error.message);
return false;
}
}
// 生成完整的密钥对文件
async generateAndSaveKeyPair(outputDir = './keys', keyName = 'app') {
try {
console.log('🔐 正在生成 RSA 密钥对...');
// 生成密钥对
const { publicKey, privateKey } = await this.generateKeyPair();
// 验证密钥对
console.log('🔍 验证密钥对...');
const isValid = this.validateKeyPair(publicKey, privateKey, this.options.privateKeyEncoding.passphrase);
if (!isValid) {
throw new Error('密钥对验证失败');
}
console.log('✅ 密钥对验证成功');
// 保存文件
const timestamp = new Date().toISOString().split('T')[0]+"-"+(new Date()).getTime();
const publicKeyFile = this.saveKeyToFile(
publicKey,
`${keyName}-public-key-${timestamp}.pem`,
outputDir
);
const privateKeyFile = this.saveKeyToFile(
privateKey,
`${keyName}-private-key-${timestamp}.pem`,
outputDir
);
// 生成配置文件示例
this.generateConfigExample(publicKey, privateKey, outputDir, keyName);
return {
publicKey,
privateKey,
publicKeyFile,
privateKeyFile,
isValid
};
} catch (error) {
console.error('❌ 生成密钥对失败:', error.message);
throw error;
}
}
// 生成配置文件示例
generateConfigExample(publicKey, privateKey, outputDir, keyName) {
const frontendConfig = `
// 前端配置 (JavaScript/TypeScript)
const RSA_PUBLIC_KEY = \`${publicKey}\`;
// 或者从文件导入
// import publicKey from './${keyName}-public-key.pem';
`;
const backendConfig = `
// 后端配置 (Go)
package config
const (
RSAPrivateKey = \`${privateKey}\`
)
// 或者从环境变量读取
// privateKey := os.Getenv("RSA_PRIVATE_KEY")
`;
const envExample = `
# 环境变量示例
RSA_PRIVATE_KEY="你的私钥内容"
RSA_PUBLIC_KEY="你的公钥内容"
KEY_PASSPHRASE="你的私钥密码(如果有)"
`;
const timestamp = new Date().toISOString().split('T')[0]+"-"+(new Date()).getTime();
this.saveKeyToFile(frontendConfig.trim(), 'frontend-config-example-'+timestamp+'.js', outputDir);
this.saveKeyToFile(backendConfig.trim(), 'backend-config-example-'+timestamp+'.go', outputDir);
this.saveKeyToFile(envExample.trim(), '.env.example', outputDir);
console.log('📝 配置文件示例已生成');
}
// 显示密钥信息
displayKeyInfo(publicKey, privateKey) {
const publicKeyInfo = crypto.createPublicKey(publicKey);
const privateKeyInfo = crypto.createPrivateKey({
key: privateKey,
passphrase: this.options.privateKeyEncoding.passphrase
});
console.log('\n🔑 密钥信息:');
console.log('──────────────────────────────');
console.log(`算法: ${publicKeyInfo.asymmetricKeyType}`);
console.log(`模数长度: ${publicKeyInfo.asymmetricKeySize}`);
console.log(`格式: PEM`);
if (this.options.privateKeyEncoding.passphrase) {
console.log(`私钥加密: ${this.options.privateKeyEncoding.cipher}`);
} else {
console.log('⚠️ 私钥未加密,建议在生产环境中使用加密私钥');
}
}
}
// 命令行界面
function main() {
const args = process.argv.slice(2);
const outputDir = args[0] || './keys';
const keyName = args[1] || 'app';
const useEncryption = args.includes('--encrypt');
const passphrase = process.env.KEY_PASSPHRASE || '';
const generator = new KeyPairGenerator({
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
...(useEncryption && {
cipher: 'aes-256-cbc',
passphrase: passphrase
})
}
});
console.log(`
🔐 RSA 密钥对生成器
──────────────────────────────
输出目录: ${outputDir}
密钥名称: ${keyName}
私钥加密: ${useEncryption ? '是' : '否'}
密钥长度: 2048 位 (平衡安全性与性能)
`);
if (useEncryption && !passphrase) {
console.log('❌ 请设置 KEY_PASSPHRASE 环境变量来加密私钥');
console.log(' 例如: KEY_PASSPHRASE=your-secret-password node generate-keys.js');
process.exit(1);
}
generator.generateAndSaveKeyPair(outputDir, keyName)
.then((result) => {
generator.displayKeyInfo(result.publicKey, result.privateKey);
console.log('\n🎉 密钥对生成完成!');
console.log('──────────────────────────────');
console.log('📁 生成的文件:');
console.log(` 公钥: ${result.publicKeyFile}`);
console.log(` 私钥: ${result.privateKeyFile}`);
console.log(` 配置文件示例: ${outputDir}/`);
console.log('\n💡 使用建议:');
console.log(' • 将公钥用于前端加密');
console.log(' • 将私钥安全地存储在后端');
console.log(' • 定期轮换密钥建议每1-2年');
console.log(' • 不要将私钥提交到版本控制系统');
if (!useEncryption) {
console.log('\n⚠ 安全警告:');
console.log(' 当前私钥未加密,建议在生产环境中使用加密私钥');
console.log(' 重新运行并添加 --encrypt 参数和 KEY_PASSPHRASE 环境变量');
}
})
.catch((error) => {
console.error('❌ 生成失败:', error.message);
process.exit(1);
});
}
// 如果直接运行此文件
if (require.main === module) {
main();
}
module.exports = KeyPairGenerator;