UNPKG

@robotti.io/escrypt

Version:

Enterprise Secret Encryptor

115 lines (103 loc) 3.61 kB
const crypto = require('crypto'); const fs = require('fs'); const { Validator } = require('jsonschema'); const keySchema = { id: '/keySchema', type: 'object', additionalFields: false, properties: { publicKey: { type: 'string' }, privateKey: { type: 'string' } } }; class Cryptor { /** * Private static field declarations */ #privateKeyFile; #publicKeyFile; #passphrase; /** * Secret encryption / decryption class constructor * @param {Object} keys - Object containing private and/or public key paths * @param {string} keys.publicKey - Relative or absolute path to the public key file * @param {string} keys.privateKey - Relative or absolute path to the private key file * @param {string} passphrase - Passphrase for the private key */ constructor(keys, passphrase = false) { /** * Parameter Validation */ // Key Validation const validator = new Validator(); const validationResults = validator.validate(keys, keySchema); if (!validationResults.valid) throw new Error(validationResults); if (typeof keys !== 'object') throw new Error('Expected keys to be an object'); const { privateKey, publicKey } = keys; if (privateKey) this.#privateKeyFile = privateKey; if (publicKey) this.#publicKeyFile = publicKey; // Passphrase Validation if (passphrase && typeof passphrase !== 'string') throw new Error('Passphrase must be a string'); if (passphrase) this.#passphrase = passphrase; } /** * Encrypt single secret using public key * @param {string} secret - UTF8 Secret to encrypt * @returns {string} - BASE64 Encrypted secret */ encrypt(secret) { if (!fs.existsSync(this.#publicKeyFile)) throw new Error('Public key file provided does not exist'); if (typeof secret !== 'string') throw new Error('Secret must be a string'); const key = fs.readFileSync(this.#publicKeyFile, 'utf8'); const buffer = Buffer.from(secret, 'utf8'); const encrypted = crypto.publicEncrypt(key, buffer); return encrypted.toString('base64'); } /** * Secrypt secret using private key * @param {string} secret - BASE64 Encrypted secret * @returns {string} - UTF8 Decrypted secret */ decrypt(secret) { if (!fs.existsSync(this.#privateKeyFile)) throw new Error('Private key file provided does not exists'); if (typeof secret !== 'string') throw new Error('Encrypted secret must be a string'); const key = { key: fs.readFileSync(this.#privateKeyFile, 'utf8').toString() }; if (this.#passphrase) { key.passphrase = this.#passphrase; } const buffer = Buffer.from(secret, 'base64'); const decrypted = crypto.privateDecrypt(key, buffer); return decrypted.toString(); } /** * Generate a public / private key pair * @returns {Object} - Object containing the key file paths */ generateKeys() { const parameters = { modulusLength: 4096, publicKeyEncoding: { type: 'pkcs1', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }; if (this.#passphrase) { parameters.privateKeyEncoding.passphrase = this.#passphrase; parameters.privateKeyEncoding.cipher = 'aes-256-cbc'; } const newKeys = crypto.generateKeyPairSync('rsa', parameters); fs.writeFileSync(this.#publicKeyFile, newKeys.publicKey); fs.writeFileSync(this.#privateKeyFile, newKeys.privateKey); return { privateKeyFile: this.#privateKeyFile, publicKeyFile: this.#publicKeyFile }; } } module.exports = Cryptor;