Implementing DES encryption in NodeJS (Crypto) - node.js

I'm trying to convert the below java function for DES encryption into NodeJS.
public static String decrypt (String value) {
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(SALT, COUNT);
PBEKeySpec pbeKeySpec = new PBEKeySpec(KEY);
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(SECRET_KEY);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher dcipher = Cipher.getInstance(SECRET_KEY);
dcipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(value);
byte[] utf8 = dcipher.doFinal(dec);
return new String(utf8, UTF8);
}
After some research, I was able to implement the below function in NodeJS.
function decrypt(input) {
const encrypted = Buffer.from(input, 'base64')
const key = crypto.pbkdf2Sync(SECRET_KEY, salt, ITERATION_COUNT, SECRET_KEY.length, 'md5');
const cipher = crypto.createDecipher('des-cbc', key);
const decrypted = Buffer.concat([cipher.update(encrypted, 'base64'), cipher.final()])
return decrypted.toString('utf-8');
}
When I try to decrypt an already encrypted string, I'm getting some garbage value. Also, I don't understand where should I add the KEY value in NodeJS as in java code.
Please help me to understand what I'm doing wrong here.
UPDATE
As mentioned by #Topaco in the comment, the name SECRET_KEY is misleading in the original java code. I found it as PBEWithMD5AndDES and was able to search with the same name and find a code snippet that solves my issue.
The code snippet is available as a gist: https://gist.github.com/qzaidi/5401800

As mentioned by #Topaco in the comment, the name SECRET_KEY is misleading in the original java code. I found it as PBEWithMD5AndDES and was able to search with the same name and find a code snippet that solves my issue.
The code snippet is available as a gist: https://gist.github.com/qzaidi/5401800
"use strict";
/*
* Emulates Java's PBEWITHMD5ANDDES for node.js
*/
var crypto = require('crypto');
var pbewithmd5anddes = {
KDF: function(password, salt, iterations) {
var pwd = new Buffer(password, 'utf-8');
var key = Buffer.concat([pwd, salt]);
var i;
for (i = 0; i < iterations; i += 1) {
key = crypto.createHash("md5").update(key).digest();
}
return key;
},
getKeyIV: function(password, salt, iterations) {
var key = this.KDF(password, salt, iterations);
var keybuf = new Buffer(key, 'binary').slice(0, 8);
var ivbuf = new Buffer(key, 'binary').slice(8, 16);
return [keybuf, ivbuf];
},
encrypt: function(payload, password, salt, iterations, cb) {
var kiv = this.getKeyIV(password, salt, iterations);
var cipher = crypto.createCipheriv('des', kiv[0], kiv[1]);
var encrypted = [];
encrypted.push(cipher.update(payload, 'utf-8', 'hex'));
encrypted.push(cipher.final('hex'));
return cb(undefined, new Buffer(encrypted.join(''), 'hex').toString('base64'));
},
decrypt: function(payload, password, salt, iterations, cb) {
var encryptedBuffer = new Buffer(payload, 'base64');
var kiv = this.getKeyIV(password, salt, iterations);
var decipher = crypto.createDecipheriv('des', kiv[0], kiv[1]);
var decrypted = [];
decrypted.push(decipher.update(encryptedBuffer));
decrypted.push(decipher.final());
return cb(undefined, decrypted.join(''));
}
};
module.exports = pbewithmd5anddes;
/* ---------------- TEST CODE ---------------- */
(function() {
if (require.main === module) {
var password = 'test';
var iterations = 19;
var salt = new Buffer('d99bce325735e303', 'hex');
pbewithmd5anddes.encrypt('helloworld', password, salt, iterations, function(err, msg) {
console.log('encrypted: ' + msg);
// eat your own dogfood
pbewithmd5anddes.decrypt(msg, password, salt, iterations, function(err, msg) {
console.log('decrypted: ' + msg);
});
});
}
}());

Related

Nodejs AES-256-GCM encryption and decryption in client by browser webcrypto api

I generate a pair public/private key on Client and send the publicKey to the Server and the backend will generate a sharedKey on its side and respond me a publicKey which help me to generate a sharedKey on the client too for encryption/decryption. So I encrypt a message by AES-256-GCM on Nodejs and decrypted the message on the Client.
Backend-Side:
export function encrypt(sharedKey: string, message: string) {
const firstIv = getRandomIV();
const cipher = crypto.createCipheriv(
'aes-256-gcm',
Buffer.from(sharedKey, 'base64'),
firstIv
);
const encrypted = cipher.update(message, 'utf8');
return Buffer.from(encrypted + cipher.final()).toString('base64');
}
function getRandomIV() {
return crypto.randomBytes(12);
}
Client-Side:
async function decrypt(encryptedData: Uint8Array) {
const aesKey = await generateAesKey();
const nonce = encryptedData.subarray(0, SERVER_ENCRYPTION_IV_LENGTH);
const data = encryptedData.subarray(SERVER_ENCRYPTION_IV_LENGTH);
const decrypted = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: nonce,
},
aesKey,
data
);
return {
decrypted: new Uint8Array(decrypted),
decryptedString: new TextDecoder().decode(decrypted),
};
}
async function generateAesKey() {
const publicKey = await getServerPublicKey();
const privateKey = await getPrivateKey();
const sharedSecret = await crypto.subtle.deriveBits(
{
name: 'ECDH',
public: publicKey!,
},
privateKey,
256
);
const aesSecret = await crypto.subtle.digest('SHA-256', sharedSecret);
return crypto.subtle.importKey('raw', aesSecret, 'AES-GCM', true, [
'encrypt',
'decrypt',
]);
}
Now, I can't decrypt the server encrypted response in the client and I encounter to DOMException error and I don't know why?
GCM uses an authentication tag that is handled separately by NodeJS/Crypto, while WebCrypto automatically concatenates it with the ciphertext.
Therefore, in the NodeJS code, the tag must be explicitly determined and appended to the ciphertext. This is missing in the current NodeJS code and can be taken into account as follows. Note the determination of the tag with cipher.getAuthTag() and its concatenation:
var crypto = require('crypto');
function encrypt(key, plaintext) {
var nonce = getRandomIV();
var cipher = crypto.createCipheriv('aes-256-gcm', key, nonce);
var nonceCiphertextTag = Buffer.concat([
nonce,
cipher.update(plaintext),
cipher.final(),
cipher.getAuthTag() // Fix: Get tag with cipher.getAuthTag() and concatenate: nonce|ciphertext|tag
]);
return nonceCiphertextTag.toString('base64');
}
function getRandomIV() {
return crypto.randomBytes(12);
}
var message = Buffer.from('The quick brown fox jumps over the lazy dog', 'utf8');
var sharedKey = Buffer.from('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=', 'base64');
var ciphertext = encrypt(sharedKey, message);
console.log(ciphertext); // wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k=
A possible output is
wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k=
The following code for decryption on the WebCrypto side is essentially based on your code (without the derivation of the key from a shared secret, which is irrelevant to the current problem):
(async () => {
var nonceCiphertextTag = base64ToArrayBuffer('wRE5KM6FG81QSMNvG0xR+iaIeF77cyyeBceGS5NkcYaD17K9nL0/helnqRBOkD9pLVoWM/nRAcaKg/YdvfNJcO1Zn/7ZM0k=');
var nonceCiphertextTag = new Uint8Array(nonceCiphertextTag);
var decrypted = await decrypt(nonceCiphertextTag);
console.log(decrypted); // The quick brown fox jumps over the lazy dog
})();
async function decrypt(nonceCiphertextTag) {
const SERVER_ENCRYPTION_IV_LENGTH = 12; // For GCM a nonce length of 12 bytes is recommended!
var nonce = nonceCiphertextTag.subarray(0, SERVER_ENCRYPTION_IV_LENGTH);
var ciphertextTag = nonceCiphertextTag.subarray(SERVER_ENCRYPTION_IV_LENGTH);
var aesKey = base64ToArrayBuffer('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=');
aesKey = await window.crypto.subtle.importKey('raw', aesKey, 'AES-GCM', true, ['encrypt', 'decrypt']);
var decrypted = await crypto.subtle.decrypt({name: 'AES-GCM', iv: nonce}, aesKey, ciphertextTag);
return new TextDecoder().decode(decrypted);
}
// Helper
// https://stackoverflow.com/a/21797381/9014097
function base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
which successfully decrypts the ciphertext of the NodeJS side:
The quick brown fox jumps over the lazy dog

encrypt data using aes-128-ecb in nodejs

I have To encrypt data using aes-128-ecb in nodejs my code is
I am using Crypto to encrypt data
const crypto = require('crypto');
const secret = '1000060000000000';
const cipher = crypto.createCipher('aes-128-ecb', secret);
const ciphertext = cipher.update('9', 'utf8', 'base64')+cipher.final('base64');
console.log("Cipher text is: " + ciphertext);
the output should be EtgITaHs6lEvEHBipj08Kg==
but the output is coming as nNzqejauQBnfiDqznGhZ0Q==
The problem here is the use of crypto.createCipher, it doesn't use the key directly, but rather a digest.
To quote the documentation:
The implementation of crypto.createCipher() derives keys using the OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt.
If we use cipher.createCipheriv on the other hand, we can specify the key directly and it will give us the expected output.
Here's an example:
const crypto = require("crypto");
function encrypt(plainText, key, outputEncoding = "base64") {
const cipher = crypto.createCipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(plainText), cipher.final()]).toString(outputEncoding);
}
function decrypt(cipherText, key, outputEncoding = "utf8") {
const cipher = crypto.createDecipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(cipherText), cipher.final()]).toString(outputEncoding);
}
const key = "1000060000000000";
const plainText = "9";
const encrypted = encrypt(plainText, key, "base64");
console.log("Encrypted string (base64):", encrypted);
const decrypted = decrypt(Buffer.from(encrypted, "base64"), key, "utf8")
console.log("Decrypted string:", decrypted);
The output will be
EtgITaHs6lEvEHBipj08Kg==
It is late but will help others
You can pass any Algorithm i.e. aes-128-cbc , aes-128-ecb
Create a new file and name it as aes-service.js in service folder or anywhere in Node.js application
aes-service.js
const crypto = require('crypto');
const cryptkey = 'C51GH00SE8499727';
const iv = 'BDA30EGDH1578F81';
async function encrypt(text){
try {
var cipher = crypto.createCipheriv('aes-128-cbc',cryptkey,iv);
var crypted = cipher.update(text,'utf8','base64'); //base64 , hex
crypted += cipher.final('base64');
return crypted;
} catch (err) {
console.error('encrypt error',err);
return null;
}
}
async function decrypt(encryptdata){
//Check all Algorithms
console.log(crypto.getCiphers()); // ['aes-128-cbc', 'aes-128-ccm', ...]
try {
let decipher = crypto.createDecipheriv('aes-128-cbc',cryptkey,iv)
decipher.setAutoPadding(false)
let decoded = decipher.update(encryptdata,'base64','utf8') //base64 , hex
decoded += decipher.final('utf8')
return decoded
} catch (err) {
console.error('decrypt error',err)
return null
}
}
const AesService = {
encrypt:encrypt,
decrypt:decrypt,
}
module.exports = AesService
Node.js contorller i.e abc.controller.js
//Get aes encrypted data from node.js request
const AesService = require("./services/aes-service")
exports.getAesEncryptedDatafromReq= async (req, res) => {
try{
let decryptData = ''
try{
const buffers = [];
for await (const chunk of req) {
buffers.push(chunk);
}
const dataBuffer = Buffer.concat(buffers).toString();
const jsonParsedData = JSON.parse(dataBuffer)
decryptData = jsonParsedData.data
}catch(err){}
let decryptedData = await AesService.decrypt(decryptData)
console.log('decrypted data',decryptedData)
let sendbackdata = {
"status": 0,
"anotehr_key":[
{ "dec":"0", "asc":"1"}
]
}
sendbackdata = JSON.stringify(sendbackdata)
let encryptedData = await AesService.encrypt(sendbackdata)
//Check if encrypted performed well
// let decryptedDataAgain = await AesService.decrypt(encryptedData)
//console.log('decryptedDataAgain ',decryptedDataAgain)
return res.status(201).send({"data":encryptedData})
}catch(err){
return res.status(500)
}
}
Add route
router.post("/get/aes/encrypted/data/from/req", controller.getAesEncryptedDatafromReq)

Saving object to JSON file than using it back in function after reading

I am completly new to node.js but have to use it in my student project that require this library: https://github.com/bitchan/eccrypto.
The goal is to encrypt file using ethereum public key, save it as JSON and than read it back to decrypt with private key:
var fs = require('fs');
var crypto = require("crypto");
var eccrypto = require("eccrypto");
var content = fs.readFileSync('pdf_test.pdf');
var importPrivateKey = "c337ded6f56c07205fb7b391654d7d463c9e0c726869523ae6024c9bec878878"
var importPublicKey = "04730a151f545f5dcdb1c6d99fb1251f5c70f216f39ba2681bcf10db16bd582e6720bc881d51f25ffbe961df6a0af24a9d39a4db3d86a7f6b3f9bf4eaac0e4006b"
var privateKey = new Buffer(importPrivateKey, "hex");
var publicKey = new Buffer(importPublicKey, "hex");
// Encrypting the file for B.
eccrypto.encrypt(publicKey, Buffer(content)).then(function(encrypted) {
//console.log('Encrypted message ' + JSON.stringify(encrypted));
let data = JSON.stringify(encrypted);
fs.writeFileSync('encrypted.json', data);
console.log('encryption done');
let rawData = fs.readFileSync('encrypted.json')
let encryptedContent = JSON.parse(rawData);
//console.log(encryptedContent);
// B decrypting the file.
eccrypto.decrypt(privateKey, encryptedContent).then(function(plaintext) {
//console.log("Decrypted message: ", plaintext.toString());
fs.writeFile('decrypted.pdf', plaintext, function (err) {
if (err) return console.log(err);
console.log('decryption done');
});
});
});
I get following error from this code: "(node:271) UnhandledPromiseRejectionWarning: Error: Bad input."
Everything is working when i replace "encryptedContent" variable with "encrypted" on eccrypto.decrypt function but i want to let user store encrypted object and decrypt it later with this function. How can i do that?
The problem is the encrypted object is not exactly JSON serializable so you have to encode the buffers in some sort of JSON serializable object. Since you used hex for the private and public keys I used it as well at below. (Also the Buffer() constructor is depracated and not secure so I switched it to Buffer.from()
var fs = require('fs');
var crypto = require("crypto");
var eccrypto = require("eccrypto");
var content = fs.readFileSync('pdf_test.pdf');;
var importPrivateKey = "c337ded6f56c07205fb7b391654d7d463c9e0c726869523ae6024c9bec878878"
var importPublicKey = "04730a151f545f5dcdb1c6d99fb1251f5c70f216f39ba2681bcf10db16bd582e6720bc881d51f25ffbe961df6a0af24a9d39a4db3d86a7f6b3f9bf4eaac0e4006b"
let privateKey = Buffer.from(importPrivateKey, 'hex');
let publicKey = Buffer.from(importPublicKey, 'hex');
// Encrypting the file for B.
eccrypto.encrypt(publicKey, Buffer.from(content)).then(function (encrypted) {
//console.log('Encrypted message ' + JSON.stringify(encrypted));
let data = JSON.stringify({
iv: encrypted.iv.toString('hex'),
ciphertext: encrypted.ciphertext.toString('hex'),
mac: encrypted.mac.toString('hex'),
ephemPublicKey: encrypted.ephemPublicKey.toString('hex')
});
fs.writeFileSync('encrypted.json', data);
console.log('encryption done');
let rawData = fs.readFileSync('encrypted.json')
let encryptedContent = JSON.parse(rawData);
encryptedContent = {
iv: Buffer.from(encryptedContent.iv, 'hex'),
ciphertext: Buffer.from(encryptedContent.ciphertext, 'hex'),
mac: Buffer.from(encryptedContent.mac, 'hex'),
ephemPublicKey: Buffer.from(encryptedContent.ephemPublicKey, 'hex')
}
//console.log(encryptedContent);
// B decrypting the file.
eccrypto.decrypt(privateKey, encryptedContent).then(function (plaintext) {
//console.log("Decrypted message: ", plaintext.toString());
fs.writeFile('decrypted.pdf', plaintext, function (err) {
if (err) return console.log(err);
console.log('decryption done');
});
});
});

NodeJS decryption fails while using Crypto. What is the issue?

var decipher = Crypto.createDecipheriv('aes-256-cfb', 'testtesttesttesttesttest', 'testtesttesttest')
Error: Invalid key length
at new Decipheriv (crypto.js:267:16)
at Object.createDecipheriv (crypto.js:627:10)
This is the error I get. Where am I going wrong?
¿if we try another way ? as in this example:
function encryptdata(key, text) {
const hash = crypto.createHash('sha256');
hash.update(key);
const keyBytes = hash.digest();
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cfb', keyBytes, iv);
let enc = [iv, cipher.update(text, 'utf8')];
enc.push(cipher.final());
return Buffer.concat(enc).toString('base64');
}
The key needs to be exactly 32 bytes in length.
Please try the below code to see if it works.
var crypto = require('crypto');
var key = 'testtesttesttesttesttesttesttest';
try{
var cipher = crypto.createCipheriv('aes-256-cfb', key, 'testtesttesttest');
var encryptedData = cipher.update("hello", 'utf8', 'hex') + cipher.final('hex');
console.log(encryptedData);
var decipher = crypto.createDecipheriv('aes-256-cfb', key, 'testtesttesttest');
var decryptedData = decipher.update(encryptedData, 'hex', 'utf8') + decipher.final('utf8');
console.log(decryptedData);
} catch(exception) {
console.error(exception);
}

How to encrypt and decrypt string/object in nodejs

I would like to encrypt an object then decrypt it. The encryption works very well but the decryption fails. Below my code :
crypto_ext.js
const crypto = require("crypto")
const password = "shared_key"
const algorithm = "aes256"
export const encrypt = (text) => {
if(!text) return ''
const cipher = crypto.createCipher(algorithm, password);
let crypted = cipher.update(text, 'utf-8', 'base64');
crypted += cipher.final('base64');
return crypted;
}
export const decrypt = (text) => {
if(!text) return ''
const decipher = crypto.createDecipher(algorithm, password);
let decrypted = decipher.update(text, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');
return decrypted;
}
and in my test.js, I have :
import {encrypt, decrypt} from './crypto_ext.js'
let test = {key1: val1, key2: val2}
test = encrypt(JSON.stringify(test)) || test
console.log("Encrypt : ", test)
console.log("Decrypt : ", decrypt(test)) // I should have my object as string here
And this is what I'm getting as error :
Uncaught Error: unable to decrypt data
at unpad (decrypter.js:83)
at Decipher.webpackJsonp../node_modules/browserify-aes/decrypter.js.Decipher._final (decrypter.js:38)
at Decipher.webpackJsonp../node_modules/cipher-base/index.js.CipherBase._finalOrDigest (index.js:76)
at decrypt (crypto_ext.js:17)
...
Can you please tell me what I'm doing wrong ?
Method createCipher deprecated. Use createCipheriv instead.
Try to use aes192 algorithm.
Dependencies: crypto package
You can achieve encoding and decoding with below codes:-
const crypto = require('crypto');
var password = 'ojisdasjdsjabdjs';
var iv = 'kiamdksndn';
function sha1(input) {
return crypto.createHash('sha1').update(input).digest();
}
function password_derive_bytes(password, salt, iterations, len) {
var key = Buffer.from(password + salt);
for (var i = 0; i < iterations; i++) {
key = sha1(key);
}
if (key.length < len) {
var hx = password_derive_bytes(password, salt, iterations - 1, 20);
for (var counter = 1; key.length < len; ++counter) {
key = Buffer.concat([key, sha1(Buffer.concat([Buffer.from(counter.toString()), hx]))]);
}
}
return Buffer.alloc(len, key);
}
async function encode(string) {
var key = password_derive_bytes(password, '', 100, 32);
var cipher = crypto.createCipheriv('aes-256-cbc', key, Buffer.from(iv));
var part1 = cipher.update(string, 'utf8');
var part2 = cipher.final();
const encrypted = Buffer.concat([part1, part2]).toString('base64');
return encrypted;
}
async function decode(string) {
var key = password_derive_bytes(password, '', 100, 32);
var decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv));
var decrypted = decipher.update(string, 'base64', 'utf8');
decrypted += decipher.final();
return decrypted;
}
And then call below functions for encode and decode
For encode
await encode(JSON.stringify({'title': 'some text'}));
For decode
await decode('encoded_string_to_decode');
Try to use 'bcrypt' package it will help you in the encryption for passwords.
If you want to encryption for Data. Then use crypto or node-rsa
Link npm bcrypt package
Node-RSA

Resources