Files
base-golang/pkg/rsa/rsa.js
2025-12-20 10:50:23 +08:00

245 lines
7.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;