-
-
Save rahultrivedi180/65670917473c196e631418c97b4bef21 to your computer and use it in GitHub Desktop.
Node.js - AES Encryption/Decryption with AES-256-GCM using random Initialization Vector + Salt
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
/** | |
* Cryptography Functions | |
* | |
* Forked from btxtiger/AesUtil.ts | |
* https://gist.github.com/btxtiger/e8eaee70d6e46729d127f1e384e755d6 | |
*/ | |
import crypto from 'crypto'; | |
import { Password } from './types'; | |
/** | |
* encryption/decryption algorithm | |
*/ | |
const ALGORITHM = 'aes-256-gcm' | |
/** | |
* Derive 256 bit encryption key from password, using salt and iterations -> 32 bytes | |
* @param password | |
* @param salt | |
* @param iterations | |
*/ | |
function deriveKeyFromPassword(password: Password, salt: Buffer, iterations: number) { | |
return crypto.pbkdf2Sync(password, salt, iterations, 32, 'sha512'); | |
} | |
/** | |
* Encrypt AES 256 GCM | |
* @param plainText | |
* @param password | |
*/ | |
export function encryptAesGcm(plainText: string | object, password: Password) { | |
try { | |
if (typeof plainText === 'object') { | |
plainText = JSON.stringify(plainText); | |
} | |
// Generate random salt -> 64 bytes | |
const salt = crypto.randomBytes(64); | |
// Generate random initialization vector -> 16 bytes | |
const iv = crypto.randomBytes(16); | |
// Generate random count of iterations between 10.000 - 99.999 -> 5 bytes | |
const iterations = Math.floor(Math.random() * (99999 - 10000 + 1)) + 10000; | |
// Derive encryption key | |
const encryptionKey = deriveKeyFromPassword(password, salt, Math.floor(iterations * 0.47 + 1337)); | |
// Create cipher | |
const cipher = crypto.createCipheriv(ALGORITHM, encryptionKey, iv); | |
// Update the cipher with data to be encrypted and close cipher | |
const encryptedData = Buffer.concat([cipher.update(plainText, 'utf8'), cipher.final()]); | |
// Get authTag from cipher for decryption // 16 bytes | |
const authTag = cipher.getAuthTag(); | |
// Join all data into single string, include requirements for decryption | |
const output = Buffer.concat([salt, iv, authTag, Buffer.from(iterations.toString()), encryptedData]).toString('hex'); | |
return output; | |
} catch (error) { | |
console.error("Encryption failed!", error); | |
return | |
} | |
} | |
/** | |
* Decrypt AES 256 GCM | |
* @param cipherText | |
* @param password | |
*/ | |
export function decryptAesGcm(cipherText: string, password: Password) { | |
try { | |
const inputData = Buffer.from(cipherText, 'hex'); | |
// Split cipherText into partials | |
const salt = inputData.subarray(0, 64); | |
const iv = inputData.subarray(64, 80); | |
const authTag = inputData.subarray(80, 96); | |
const iterations = parseInt(inputData.subarray(96, 101).toString('utf-8'), 10); | |
const encryptedData = inputData.subarray(101); | |
// Derive key | |
const decryptionKey = deriveKeyFromPassword(password, salt, Math.floor(iterations * 0.47 + 1337)); | |
// Create decipher | |
const decipher = crypto.createDecipheriv(ALGORITHM, decryptionKey, iv); | |
decipher.setAuthTag(authTag); | |
// Decrypt data | |
const decrypted = decipher.update(encryptedData) + decipher.final('utf-8'); | |
try { | |
return JSON.parse(decrypted); | |
} catch (error) { | |
return decrypted; | |
} | |
} catch (error) { | |
console.error("Decryption failed!", error); | |
return | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment