diff --git a/pkg/crypto/aes.go b/pkg/crypto/aes.go new file mode 100644 index 0000000..e9080a0 --- /dev/null +++ b/pkg/crypto/aes.go @@ -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" +} diff --git a/pkg/crypto/compress.go b/pkg/crypto/compress.go new file mode 100644 index 0000000..c838cca --- /dev/null +++ b/pkg/crypto/compress.go @@ -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) +} diff --git a/pkg/crypto/hmac.go b/pkg/crypto/hmac.go new file mode 100644 index 0000000..1529b9d --- /dev/null +++ b/pkg/crypto/hmac.go @@ -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 +} diff --git a/pkg/crypto/interface.go b/pkg/crypto/interface.go new file mode 100644 index 0000000..17a3b37 --- /dev/null +++ b/pkg/crypto/interface.go @@ -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 +} diff --git a/pkg/crypto/rsa.go b/pkg/crypto/rsa.go new file mode 100644 index 0000000..fb86453 --- /dev/null +++ b/pkg/crypto/rsa.go @@ -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" +} diff --git a/pkg/rsa/rsa.js b/pkg/rsa/rsa.js new file mode 100644 index 0000000..a7cb17a --- /dev/null +++ b/pkg/rsa/rsa.js @@ -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;