From 424ad78a7eaeb70ecd7b69119f95a9546b8ba444 Mon Sep 17 00:00:00 2001 From: bvbej Date: Sat, 20 Dec 2025 10:50:23 +0800 Subject: [PATCH] =?UTF-8?q?[=F0=9F=9A=80]=20=E7=94=9F=E6=88=90=E5=B7=A5?= =?UTF-8?q?=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/rsa/rsa.js | 244 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 pkg/rsa/rsa.js 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;