- 新增许可证服务器 (license-server/) - 新增 Agent 部署模块 (flnxi-agent/) - 添加许可证验证和激活页面 - 添加 Harbor 镜像同步脚本 - 更新素材系统支持许可证验证
94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
import * as crypto from 'crypto';
|
|
|
|
// RSA 公钥(从许可证服务器获取,嵌入客户端)
|
|
const RSA_PUBLIC_KEY = process.env.LICENSE_PUBLIC_KEY?.replace(/\\n/g, '\n') || `-----BEGIN PUBLIC KEY-----
|
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwQNKx95FWT0XKXcskHhv
|
|
MmBluxHYpmPfKSPETyCO1cMDhf1TuFYggwP51b2BkkAiCS1sD3Hswb9VEzEJwbG/
|
|
nRF1cBNAGBLcDudnAWp5y8GTVr6voEEgsk88o0xVeQdNWwi3016XffB47Q235ixb
|
|
mr6cb0q9FEMMEQULKY9RFRcpVc2GvJup+c6/wIVV8Z0d7pGn+mD2eF+mucg+N+tJ
|
|
ocUfjgf9aE3dGitGYmA4xMU5OMEYXhydhNkOA+5YpHb7yrwQNuLAileGiq6aMp0i
|
|
RmWy9Pty+rygNRUK9kFDn0Fw4oZBNoVBkVebTqeeSZmrM8CJtE3KK0BrC5/TpFon
|
|
QQIDAQAB
|
|
-----END PUBLIC KEY-----`;
|
|
|
|
// 本地加密密钥(用于加密本地存储的数据)
|
|
const LOCAL_ENCRYPTION_KEY = process.env.LOCAL_ENCRYPTION_KEY || 'flnxi-local-encryption-key-32bytes!';
|
|
|
|
/**
|
|
* 使用 RSA 公钥验证签名
|
|
*/
|
|
export function verifySignature(data: string, signature: string): boolean {
|
|
try {
|
|
const verify = crypto.createVerify('RSA-SHA256');
|
|
verify.update(data);
|
|
verify.end();
|
|
return verify.verify(RSA_PUBLIC_KEY, signature, 'base64');
|
|
} catch (error) {
|
|
console.error('签名验证失败:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证签名后的许可证数据
|
|
*/
|
|
export function verifyLicenseSignature(licenseData: object, signature: string): boolean {
|
|
const dataString = JSON.stringify(licenseData);
|
|
return verifySignature(dataString, signature);
|
|
}
|
|
|
|
/**
|
|
* 使用 AES-256-GCM 加密数据(用于本地存储)
|
|
*/
|
|
export function encryptLocal(plaintext: string): string {
|
|
const iv = crypto.randomBytes(12);
|
|
const key = crypto.scryptSync(LOCAL_ENCRYPTION_KEY, 'salt', 32);
|
|
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
|
|
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
|
|
encrypted += cipher.final('base64');
|
|
|
|
const authTag = cipher.getAuthTag();
|
|
|
|
// 格式: iv:authTag:encrypted
|
|
return `${iv.toString('base64')}:${authTag.toString('base64')}:${encrypted}`;
|
|
}
|
|
|
|
/**
|
|
* 使用 AES-256-GCM 解密数据(用于本地存储)
|
|
*/
|
|
export function decryptLocal(ciphertext: string): string {
|
|
try {
|
|
const [ivBase64, authTagBase64, encrypted] = ciphertext.split(':');
|
|
|
|
const iv = Buffer.from(ivBase64, 'base64');
|
|
const authTag = Buffer.from(authTagBase64, 'base64');
|
|
const key = crypto.scryptSync(LOCAL_ENCRYPTION_KEY, 'salt', 32);
|
|
|
|
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
decipher.setAuthTag(authTag);
|
|
|
|
let decrypted = decipher.update(encrypted, 'base64', 'utf8');
|
|
decrypted += decipher.final('utf8');
|
|
|
|
return decrypted;
|
|
} catch (error) {
|
|
console.error('解密失败:', error);
|
|
throw new Error('数据解密失败');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 计算数据哈希
|
|
*/
|
|
export function hashData(data: string): string {
|
|
return crypto.createHash('sha256').update(data).digest('hex');
|
|
}
|
|
|
|
/**
|
|
* 生成随机 ID
|
|
*/
|
|
export function generateId(): string {
|
|
return crypto.randomBytes(16).toString('hex');
|
|
}
|