147 lines
3.9 KiB
TypeScript
147 lines
3.9 KiB
TypeScript
import * as crypto from 'crypto';
|
||
|
||
// RSA 密钥
|
||
const RSA_PRIVATE_KEY = process.env.RSA_PRIVATE_KEY?.replace(/\\n/g, '\n') || '';
|
||
const RSA_PUBLIC_KEY = process.env.RSA_PUBLIC_KEY?.replace(/\\n/g, '\n') || '';
|
||
|
||
// AES 密钥
|
||
const AES_SECRET_KEY = process.env.AES_SECRET_KEY || '';
|
||
|
||
/**
|
||
* 使用 RSA 私钥签名数据
|
||
*/
|
||
export function signData(data: string): string {
|
||
const sign = crypto.createSign('RSA-SHA256');
|
||
sign.update(data);
|
||
sign.end();
|
||
return sign.sign(RSA_PRIVATE_KEY, 'base64');
|
||
}
|
||
|
||
/**
|
||
* 使用 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 {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 使用 AES-256-GCM 加密数据
|
||
*/
|
||
export function encryptAES(plaintext: string): string {
|
||
const iv = crypto.randomBytes(12);
|
||
const key = Buffer.from(AES_SECRET_KEY, 'hex');
|
||
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 decryptAES(ciphertext: string): string {
|
||
const [ivBase64, authTagBase64, encrypted] = ciphertext.split(':');
|
||
|
||
const iv = Buffer.from(ivBase64, 'base64');
|
||
const authTag = Buffer.from(authTagBase64, 'base64');
|
||
const key = Buffer.from(AES_SECRET_KEY, 'hex');
|
||
|
||
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;
|
||
}
|
||
|
||
/**
|
||
* 生成许可证密钥 (XXXX-XXXX-XXXX-XXXX 格式) - 旧格式,保留兼容
|
||
*/
|
||
export function generateLicenseKey(): string {
|
||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // 排除容易混淆的字符
|
||
const segments: string[] = [];
|
||
|
||
for (let i = 0; i < 4; i++) {
|
||
let segment = '';
|
||
for (let j = 0; j < 4; j++) {
|
||
segment += chars[crypto.randomInt(chars.length)];
|
||
}
|
||
segments.push(segment);
|
||
}
|
||
|
||
return segments.join('-');
|
||
}
|
||
|
||
/**
|
||
* 生成新格式激活码
|
||
* 格式:FLNXI-{TYPE}-{XXXX}-{XXXX}-{XXXX}-{XXXX}
|
||
* TYPE: FREE (免费版) / STD (标准版) / PRO (专业版)
|
||
*/
|
||
export function generateActivationCode(type: 'FREE' | 'STANDARD' | 'PRO'): string {
|
||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||
const typeCode = type === 'FREE' ? 'FREE' : type === 'STANDARD' ? 'STD' : 'PRO';
|
||
|
||
const segments: string[] = [];
|
||
for (let i = 0; i < 4; i++) {
|
||
let segment = '';
|
||
for (let j = 0; j < 4; j++) {
|
||
segment += chars.charAt(crypto.randomInt(chars.length));
|
||
}
|
||
segments.push(segment);
|
||
}
|
||
|
||
return `FLNXI-${typeCode}-${segments.join('-')}`;
|
||
}
|
||
|
||
/**
|
||
* 验证许可证密钥格式
|
||
* 格式:FLNXI-{TYPE}-XXXX-XXXX-XXXX-XXXX(TYPE: FREE/STD/PRO)
|
||
*/
|
||
export function isValidLicenseKeyFormat(key: string): boolean {
|
||
return /^FLNXI-(FREE|STD|PRO)-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(key);
|
||
}
|
||
|
||
/**
|
||
* 计算硬件指纹哈希
|
||
*/
|
||
export function hashFingerprint(fingerprint: object): string {
|
||
const data = JSON.stringify(fingerprint);
|
||
return crypto.createHash('sha256').update(data).digest('hex');
|
||
}
|
||
|
||
/**
|
||
* 签名许可证数据
|
||
*/
|
||
export function signLicense(licenseData: object): { data: object; signature: string; issuedAt: string } {
|
||
const issuedAt = new Date().toISOString();
|
||
const dataWithTimestamp = { ...licenseData, issuedAt };
|
||
const dataString = JSON.stringify(dataWithTimestamp);
|
||
const signature = signData(dataString);
|
||
|
||
return {
|
||
data: dataWithTimestamp,
|
||
signature,
|
||
issuedAt,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 获取公钥(供客户端使用)
|
||
*/
|
||
export function getPublicKey(): string {
|
||
return RSA_PUBLIC_KEY;
|
||
}
|