DEV Community

LS
LS

Posted on

How to use crypto library in Nodejs

#!/usr/bin/env node /** * secret-encode.js * * Usage: * node secret-encode.js encrypt "my secret text" myPassword * node secret-encode.js encrypt-file ./input.txt myPassword * node secret-encode.js decrypt <base64-string> myPassword * node secret-encode.js decrypt-file <base64-string> myPassword ./out.txt * * Output format (JSON string): * { * "v": 1, // version * "salt": "<base64>", // PBKDF2 salt * "iv": "<base64>", // AES-GCM IV * "tag": "<base64>", // AES-GCM auth tag * "ct": "<base64>" // ciphertext * } * * This is reversible: keep the password secret and share the encoded JSON string safely. */ const crypto = require('crypto'); const fs = require('fs').promises; const path = require('path'); const PBKDF2_ITER = 120_000; // iterations (adjust upward if you want more CPU cost) const KEYLEN = 32; // 256-bit key const SALT_LEN = 16; const IV_LEN = 12; // recommended for GCM const AUTH_TAG_LEN = 16; function deriveKey(password, salt) { return crypto.pbkdf2Sync(password, salt, PBKDF2_ITER, KEYLEN, 'sha256'); } function encryptBuffer(plainBuf, password) { const salt = crypto.randomBytes(SALT_LEN); const key = deriveKey(password, salt); const iv = crypto.randomBytes(IV_LEN); const cipher = crypto.createCipheriv('aes-256-gcm', key, iv, { authTagLength: AUTH_TAG_LEN }); const ct = Buffer.concat([cipher.update(plainBuf), cipher.final()]); const tag = cipher.getAuthTag(); const out = { v: 1, salt: salt.toString('base64'), iv: iv.toString('base64'), tag: tag.toString('base64'), ct: ct.toString('base64'), }; return JSON.stringify(out); } function decryptString(encryptedJsonStr, password) { let obj; try { obj = JSON.parse(encryptedJsonStr); } catch (e) { throw new Error('Input is not valid JSON. Make sure you pass the encoded JSON string.'); } if (obj.v !== 1) throw new Error('Unsupported version'); const salt = Buffer.from(obj.salt, 'base64'); const iv = Buffer.from(obj.iv, 'base64'); const tag = Buffer.from(obj.tag, 'base64'); const ct = Buffer.from(obj.ct, 'base64'); const key = deriveKey(password, salt); const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv, { authTagLength: AUTH_TAG_LEN }); decipher.setAuthTag(tag); const plain = Buffer.concat([decipher.update(ct), decipher.final()]); return plain; } async function run() { const args = process.argv.slice(2); if (args.length < 1) return printHelp(); const cmd = args[0]; try { if (cmd === 'encrypt') { if (args.length < 3) return printHelp(); const text = args[1]; const password = args[2]; const out = encryptBuffer(Buffer.from(text, 'utf8'), password); console.log(out); return; } if (cmd === 'encrypt-file') { if (args.length < 3) return printHelp(); const file = args[1]; const password = args[2]; const b = await fs.readFile(file); const out = encryptBuffer(b, password); console.log(out); return; } if (cmd === 'decrypt') { if (args.length < 3) return printHelp(); const encoded = args[1]; const password = args[2]; const plainBuf = decryptString(encoded, password); console.log(plainBuf.toString('utf8')); return; } if (cmd === 'decrypt-file') { if (args.length < 4) return printHelp(); const encoded = args[1]; const password = args[2]; const outFile = args[3]; const plainBuf = decryptString(encoded, password); await fs.writeFile(outFile, plainBuf); console.log(`Wrote decrypted content to ${outFile}`); return; } printHelp(); } catch (err) { console.error('Error:', err.message || err); process.exitCode = 2; } } function printHelp() { const me = path.basename(process.argv[1]); console.log(` Usage: node ${me} encrypt "my secret text" myPassword node ${me} encrypt-file ./input.txt myPassword node ${me} decrypt '<encoded-json-string>' myPassword node ${me} decrypt-file '<encoded-json-string>' myPassword ./out.txt Notes: - The output is a JSON string (salt, iv, tag, ciphertext in base64). Share that string publicly if you like. - NEVER share the password publicly. Send the password via a separate secure channel (phone, Signal, etc). - For short secrets you can use "encrypt", for files use "encrypt-file". `); } if (require.main === module) run(); 
Enter fullscreen mode Exit fullscreen mode

and how to decode

node decrypt-encrypt.js decrypt '{"v":1,"salt":"YkgTYa8bQeuudUVyc6JzTg==","iv":"gYGLAUZFttrwNOHB","tag":"OhQSFgCQuR5iKMQnIh7Jgg==","ct":"43rxWQfb40QNZEEw01EXvDwAH/aRPz7ln6ZgBqah1vIYZGAxkKW4B/xzH/h3u7DEsGMg3kGwZZXTbNp3A/FEiyQhH9gWstmAwy7klJ+hIttCSBVR40WJR6zIKNjVbZDYeDSxsO0HqNGaY7E2gC/ampLwoefl3hHDAeyddsaSk9/4lwji0SYMjrlvFOcHZtMl9CDQiUzkpY7F8zH3TEsu2e8nZuLJojrz3+UEXTdMguGP3hU1YZEAiNfkdW27UiTfgDkHljxm7ZxLKgoe1DBvmnWKIFShixZ2ps1wBYdp6vOyrVv1mXPX3svYSXK5P+f/7WwiuH5QaaVrj1bGSgRmTuT9tfECmToABo9PCi9QoB0vKoRSA/zztTTcG9jSOrkk5GgCmS84r3lBNcZcNyJZ/Iy1IVv8dGJt0r9QjUjPdpGDf1NUTpbrEkUHXdnPpTw8EQNj1mrI88OldqX/xwjayV/2C0rLlS0tRKQCZ0sXSJPBomKL1uPbLGDW8sM6F0iNuMJHoXL6Hd+DvJnmaexDhnW4uHdlwkfM93B9Wqtlzg3wCB80ze1XsQLJml6uP63kbt6rlNG0il1HtuMDOn58oZYAmlciCZ+a/do5dbs7TuxLuOZjVEvh2D8vVA8DCAKT47r0QIaDcrBY8s15+bWJSTnTSVhIXCRs3L3UjINsbNGnvjQjFLpFLjl6El/gGr+9CNyNkmQ3mVEDtZtU8q+0ub6dPawthWzYwsdU7VFC8D04xsfmUBub7QZmWyWPM5BGSTfHITKCtNRwaQvgTyztDQ0fJY8DsK3+ShtdlxBrZY3i2O6PuKL6rEPBxIyqPx4faMwSmBKrvqAYJBgUTdMq6zQVJqugeqVJ7MCEwPDbFW2yUps4IjAqPQhcOQmQ7euSyW/MN3IRzjuqR6iVcv7axCmROK4NAuCNkDpbHHo3rAiJ8WDg1ByMqcM0yKuHUi3E="}' "your secret key" 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)