e2ee-adapter
Version:
Plug-and-play End-to-End Encryption middleware for Express.js and NestJS using hybrid AES-CBC + RSA encryption with secure key exchange
195 lines • 7.14 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateKeyPair = generateKeyPair;
exports.encrypt = encrypt;
exports.decryptAESKey = decryptAESKey;
exports.decrypt = decrypt;
exports.encryptAES = encryptAES;
exports.decryptAES = decryptAES;
exports.generateMultipleKeyPairs = generateMultipleKeyPairs;
const crypto = __importStar(require("node:crypto"));
/**
* Generate RSA key pair
* @param keySize - Key size in bits (default: 2048)
* @returns Promise<KeyPair>
*/
async function generateKeyPair(keySize = 2048) {
return new Promise((resolve, reject) => {
crypto.generateKeyPair('rsa', {
modulusLength: keySize,
publicKeyEncoding: {
type: 'spki',
format: 'pem',
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
},
}, (err, publicKey, privateKey) => {
if (err) {
reject(new Error(`Key generation failed: ${err.message}`));
}
else {
resolve({ publicKey, privateKey });
}
});
});
}
/**
* Encrypt data using hybrid encryption (AES-CBC + RSA)
* @param data - Data to encrypt
* @param publicKey - RSA public key
* @returns Promise<{ encryptedData: string, aesKey: Buffer, iv: Buffer, originalAesKey: Buffer }>
*/
async function encrypt(data, publicKey) {
try {
// 1. Generate AES key (32 bytes) and IV (16 bytes)
const aesKey = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
// 2. Encrypt the data using AES-CBC
const cipher = crypto.createCipheriv('aes-256-cbc', aesKey, iv);
let encrypted = cipher.update(data, 'utf8', 'base64');
encrypted += cipher.final('base64');
// 3. Encrypt the AES key using RSA
const encryptedKey = crypto.publicEncrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256',
}, aesKey);
return {
encryptedData: encrypted,
aesKey: encryptedKey,
iv: iv,
originalAesKey: aesKey, // Return the original AES key for response decryption
};
}
catch (error) {
throw new Error(`Encryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Decrypt only the AES key from the encrypted key header (for empty request bodies)
* @param encryptedKey - Encrypted AES key (base64)
* @param privateKey - RSA private key
* @returns Promise<{ aesKey: Buffer, iv: Buffer }>
*/
async function decryptAESKey(encryptedKey, iv, privateKey) {
try {
// Decrypt only the AES key using RSA
const aesKey = crypto.privateDecrypt({
key: privateKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256',
}, Buffer.from(encryptedKey, 'base64'));
return {
aesKey: aesKey,
iv: Buffer.from(iv, 'base64'),
};
}
catch (error) {
throw new Error(`AES key decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Decrypt data using hybrid decryption (AES-CBC + RSA)
* @param encryptedData - Encrypted data (base64)
* @param encryptedKey - RSA encrypted AES key (base64)
* @param iv - Initialization vector (base64)
* @param privateKey - RSA private key
* @returns Promise<DecryptionResult>
*/
async function decrypt(encryptedData, encryptedKey, iv, privateKey) {
try {
// 1. Decrypt the AES key using RSA
const aesKey = crypto.privateDecrypt({
key: privateKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256',
}, Buffer.from(encryptedKey, 'base64'));
// 2. Decrypt the data using AES-CBC
const decipher = crypto.createDecipheriv('aes-256-cbc', aesKey, Buffer.from(iv, 'base64'));
let decrypted = decipher.update(encryptedData, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return {
decryptedData: decrypted,
aesKey: aesKey,
iv: Buffer.from(iv, 'base64'),
};
}
catch (error) {
throw new Error(`Decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Encrypt data using AES-CBC (for server responses)
* @param data - Data to encrypt
* @param aesKey - AES key
* @param iv - Initialization vector
* @returns string - Encrypted data (base64)
*/
function encryptAES(data, aesKey, iv) {
const cipher = crypto.createCipheriv('aes-256-cbc', aesKey, iv);
let encrypted = cipher.update(data, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
/**
* Decrypt data using AES-CBC (for client responses)
* @param encryptedData - Encrypted data (base64)
* @param aesKey - AES key
* @param iv - Initialization vector
* @returns string - Decrypted data
*/
function decryptAES(encryptedData, aesKey, iv) {
const decipher = crypto.createDecipheriv('aes-256-cbc', aesKey, iv);
let decrypted = decipher.update(encryptedData, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
/**
* Generate multiple RSA key pairs for multi-domain support
* @param keyIds - Array of key IDs to generate
* @param keySize - Key size in bits (default: 2048)
* @returns Promise<{ [keyId: string]: KeyPair }>
*/
async function generateMultipleKeyPairs(keyIds, keySize = 2048) {
const keyPairs = {};
for (const keyId of keyIds) {
keyPairs[keyId] = await generateKeyPair(keySize);
}
return keyPairs;
}
//# sourceMappingURL=crypto.js.map