Last active
June 5, 2025 20:08
-
-
Save fox3000foxy/a47adb4bfa347d89751897ab27cae1ec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import crypto from "crypto"; | |
import fs from "fs"; | |
import path from "path"; | |
// ===== Configuration ===== | |
const AES_SECRET_PATH = path.join(process.cwd(), "aes-secret.key"); | |
// ===== AES Secret Initialization ===== | |
let AES_SECRET: Buffer; | |
try { | |
if (fs.existsSync(AES_SECRET_PATH)) { | |
// Load existing secret | |
AES_SECRET = fs.readFileSync(AES_SECRET_PATH); | |
} else { | |
// Generate new 256-bit key and save it | |
AES_SECRET = crypto.randomBytes(32); | |
fs.writeFileSync(AES_SECRET_PATH, AES_SECRET, { flag: "wx" }); | |
} | |
} catch (error) { | |
console.error("Error initializing AES secret:", error); | |
throw error; | |
} | |
// ===== Encryption / Decryption Utilities ===== | |
/** | |
* Encrypts a JSON-serializable object using AES-256-GCM. | |
* @param data Object to encrypt. | |
* @param secret 256-bit encryption key. | |
* @returns Base64-encoded encrypted string (IV + Tag + Ciphertext). | |
*/ | |
export function encryptWithAES(data: object, secret: Buffer): string { | |
const iv = crypto.randomBytes(12); // 96-bit IV recommended for GCM | |
const cipher = crypto.createCipheriv("aes-256-gcm", secret, iv); | |
const json = JSON.stringify(data); | |
const encrypted = Buffer.concat([ | |
cipher.update(json, "utf8"), | |
cipher.final(), | |
]); | |
const authTag = cipher.getAuthTag(); | |
// Final payload: IV (12) + Tag (16) + Encrypted | |
return Buffer.concat([iv, authTag, encrypted]).toString("base64"); | |
} | |
/** | |
* Decrypts a base64 AES-GCM-encrypted string back into an object. | |
* @param encrypted Encrypted base64 string. | |
* @param secret 256-bit encryption key. | |
* @returns Decrypted object or null on failure. | |
*/ | |
export function decryptWithAES(encrypted: string, secret: Buffer): object | null { | |
try { | |
const buf = Buffer.from(encrypted, "base64"); | |
if (buf.length < 28) return null; // IV (12) + Tag (16) minimum | |
const iv = buf.subarray(0, 12); | |
const tag = buf.subarray(12, 28); | |
const ciphertext = buf.subarray(28); | |
const decipher = crypto.createDecipheriv("aes-256-gcm", secret, iv); | |
decipher.setAuthTag(tag); | |
const decrypted = | |
decipher.update(ciphertext, undefined, "utf8") + | |
decipher.final("utf8"); | |
return JSON.parse(decrypted); | |
} catch (error) { | |
console.error("Error decrypting with AES:", error); | |
return null; | |
} | |
} | |
// ===== License Key Management ===== | |
/** | |
* Generates a single encrypted license key with optional credit. | |
* @param credits Number of credits or null for unlimited. | |
* @returns Encrypted license key (base64 without padding). | |
*/ | |
export function generateKey(credits: number | null): string { | |
const payload = { | |
id: crypto.randomBytes(8).toString("base64"), // Unique ID | |
credit: credits, | |
}; | |
return encryptWithAES(payload, AES_SECRET).replace(/=+$/, ""); // Remove padding | |
} | |
/** | |
* Decrypts and parses an encrypted license key. | |
* @param encryptedKey Base64 license key string. | |
* @returns Parsed payload or null. | |
*/ | |
export function decryptKey(encryptedKey: string): Record<string, unknown> | null { | |
return decryptWithAES(encryptedKey, AES_SECRET) as Record<string, unknown> | null; | |
} | |
/** | |
* Generates and saves multiple license keys with different credit amounts. | |
* Each credit value gets its own output `.txt` file. | |
* @param credits Array of credit amounts (or null for unlimited). | |
* @param filePathPrefix Prefix for the output file names. | |
* @param numberOfKeys Number of keys per credit group. | |
*/ | |
export function generateKeysAndSave( | |
credits: (number | null)[], | |
filePathPrefix: string, | |
numberOfKeys = 100, | |
): void { | |
credits.forEach((credit) => { | |
const keys = Array.from({ length: numberOfKeys }, () => | |
generateKey(credit) | |
); | |
const fileName = `${filePathPrefix}${credit ?? "lifetime"}.txt`; | |
const filePath = path.join(process.cwd(), fileName); | |
fs.writeFileSync(filePath, keys.join("\n")); | |
}); | |
} | |
// ===== Example (commented out) ===== | |
// const credits = [10, 20, 50, 100, null]; | |
// generateKeysAndSave(credits, "keys_credits_", 100); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
🔐 AES Key Generator for Encrypted Credit-Based Licenses
This Node.js module provides a simple utility to generate, encrypt, and decrypt license keys tied to a credit system using AES-256-GCM encryption. Each key encodes a small JSON payload, including an ID and credit value, and is secured using a secret key stored on disk.
📦 Features
aes-secret.key
file.number | null
) embedded in each key.📁 File Structure
aes-secret.key
: Generated once and reused, holds your AES encryption key.keys_credits_<credit>.txt
: Output files generated with multiple license keys.🔧 Usage
Generate a Single Key
Decrypt a Key
Batch Generate Keys and Save to File
This will create:
keys_credits_10.txt
keys_credits_20.txt
keys_credits_lifetime.txt
Each file contains 100 unique, encrypted license keys.
🔐 How Encryption Works
{ id, credit }
IV + AuthTag + Ciphertext
, base64-encoded (padding=
removed)Example key:
❗ Important Notes
aes-secret.key
in the project root.aes-secret.key
secure! Anyone with access can decrypt license keys.=
) from base64 is safe but makes the keys slightly shorter.✅ Requirements
crypto
,fs
, andpath
modules.