2026-05-25 14:31:58 +08:00

147 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

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.

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;
}