-
-
Save btxtiger/e8eaee70d6e46729d127f1e384e755d6 to your computer and use it in GitHub Desktop.
/** | |
* Cryptography Functions | |
* | |
* Forked from AndiDittrich/AesUtil.js | |
* https://gist.github.com/AndiDittrich/4629e7db04819244e843 | |
*/ | |
import crypto, { CipherGCM, CipherGCMTypes, DecipherGCM } from 'crypto'; | |
import { Password } from './types'; | |
/** | |
* Get encryption/decryption algorithm | |
*/ | |
function getAlgorithm(): CipherGCMTypes { | |
return 'aes-256-gcm'; | |
} | |
/** | |
* Get encrypted string prefix | |
*/ | |
function getEncryptedPrefix(): string { | |
return 'enc::'; | |
} | |
/** | |
* 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): Buffer { | |
return crypto.pbkdf2Sync(password, salt, iterations, 32, 'sha512'); | |
} | |
/** | |
* Encrypt AES 256 GCM | |
* @param plainText | |
* @param password | |
*/ | |
export function encryptAesGcm(plainText: string | object, password: Password): string | undefined { | |
try { | |
if (typeof plainText === 'object') { | |
plainText = JSON.stringify(plainText); | |
} else { | |
plainText = String(plainText); | |
} | |
const algorithm: CipherGCMTypes = getAlgorithm(); | |
// 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 | |
// @ts-ignore: TS expects the wrong createCipher return type here | |
const cipher: CipherGCM = 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 getEncryptedPrefix() + output; | |
} catch (error) { | |
console.error('Encryption failed!'); | |
console.error(error); | |
return void 0; | |
} | |
} | |
/** | |
* Decrypt AES 256 GCM | |
* @param cipherText | |
* @param password | |
*/ | |
export function decryptAesGcm(cipherText: string, password: Password): string | undefined { | |
try { | |
const algorithm: CipherGCMTypes = getAlgorithm(); | |
const cipherTextParts = cipherText.split(getEncryptedPrefix()); | |
// If it's not encrypted by this, reject with undefined | |
if (cipherTextParts.length !== 2) { | |
console.error('Could not determine the beginning of the cipherText. Maybe not encrypted by this method.'); | |
return void 0; | |
} else { | |
cipherText = cipherTextParts[1]; | |
} | |
const inputData: Buffer = Buffer.from(cipherText, 'hex'); | |
// Split cipherText into partials | |
const salt: Buffer = inputData.slice(0, 64); | |
const iv: Buffer = inputData.slice(64, 80); | |
const authTag: Buffer = inputData.slice(80, 96); | |
const iterations: number = parseInt(inputData.slice(96, 101).toString('utf-8'), 10); | |
const encryptedData: Buffer = inputData.slice(101); | |
// Derive key | |
const decryptionKey = deriveKeyFromPassword(password, salt, Math.floor(iterations * 0.47 + 1337)); | |
// Create decipher | |
// @ts-ignore: TS expects the wrong createDecipher return type here | |
const decipher: DecipherGCM = crypto.createDecipheriv(algorithm, decryptionKey, iv); | |
decipher.setAuthTag(authTag); | |
// Decrypt data | |
// @ts-ignore: TS expects the wrong createDecipher return type here | |
const decrypted = decipher.update(encryptedData, 'binary', 'utf-8') + decipher.final('utf-8'); | |
try { | |
return JSON.parse(decrypted); | |
} catch (error) { | |
return decrypted; | |
} | |
} catch (error) { | |
console.error('Decryption failed!'); | |
console.error(error); | |
return void 0; | |
} | |
} |
export type Password = string | Buffer | NodeJS.TypedArray | DataView;
- nodejs/node#50624 you might use
buffer.subarray
instead, soinputData.subarray
@btxtiger Thank you, i am on it, i'll let you know
Hi there, I've resolve it, no error but i haven't tested it yet. I'm trying to encrypt a json from request.body api request, the body must be encrypted and as that code shows it might be viable for the encrypting to failed, what do you think i ought to do. thanks you.
@btxtiger Thank you, my man, both the Encryption and Decryption work like a charm
const fs = require('fs');
const crypto = require('crypto');
function decryptAES128CBC(encryptedText, key, iv) {
const decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(key, 'hex'), Buffer.from(iv, 'hex'));
let decrypted = decipher.update(encryptedText, 'hex', 'utf-8');
decrypted += decipher.final('utf-8');
return decrypted;
}
// Example usage:
const encryptedFilePath = 'bg16V5qCkfbkQ4nGZmWhurld0Tj8BArwyTRhafgiNkM=';
const encryptionKey = '0db19bb5d246964ea386c1c85e981264';
const initializationVector = Buffer.alloc(16).fill(0);
try {
const decryptedMessage = decryptAES128CBC(encryptedFilePath, encryptionKey, initializationVector);
console.log('Decrypted Message:', decryptedMessage);
} catch (error) {
console.error('Error:', error.message);
}
I am getting Error: error:1C80006B:Provider routines::wrong final block length
Can anyone help
@harshul21 Please ask your question on Stackoverflow, not here
Hi there, i am having some issue here
import { Password } from './types';
inputData.slice()
is deprecatedPlease i need this ASAP, thank you, GOD BLESS