Rfc2898DeriveBytes to crypto.pbkdf2 - node.js

I am not getting the same results when switching from C# to JS:
Converting from this:
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(
passPhrase,
Encoding.UTF8.GetBytes(saltValue)
);
MemoryStream ms = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = pdb.GetBytes(aes.KeySize / 8);
aes.IV = pdb.GetBytes(aes.BlockSize / 8);
CryptoStream cs = new CryptoStream(ms,
aes.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(input, 0, input.Length);
cs.Close();
ms.Close();
aes.Clear();
return ms.ToArray();
To this:
crypto.pbkdf2(Buffer.from(pass), Buffer.from(salt, 'hex'), 1000, 256 / 8, null, function (err, key) {
console.log("Key:" + key);
crypto.pbkdf2(Buffer.from(pass), Buffer.from(salt, 'hex'), 1000, 128 / 8, null, function (err, key) {
console.log("VID:" + key);
}
}
But it's not working for some reason
I tried Buffer.from(pass, 'utf8') and so one but I am never getting the same result.
I know I have something wrong but I have no idea how pbkdf2 works

For anyone in the same problem as I am this is the equivalent code
var pass = 'password';
var salt = 'salt';
var keyLen = keylen; // aes.KeySize / 8 where KeySize is 256
var IVLen = ivlen; // aes.BlockSize / 8 where BlockSize is 128
var nodeCrypto = crypto.pbkdf2Sync(pass, salt, 1000, keyLen + IVLen, 'sha1');
// Output same values as C# Bytes
var aesKey = [], aesIV = [];
for (var i = 0; i < nodeCrypto.length; i++) {
if (i < keyLen)
aesKey[i] = nodeCrypto[i];
else
aesIV[i - keyLen] = nodeCrypto[i];
}
console.log(aesKey);
console.log(aesIV);
// How to use it to decrypt
var r = fs.createReadStream(path);
var decrypt = crypto.createDecipheriv('aes-256-cbc', nodeCrypto.slice(0, keyLen), nodeCrypto.slice(keyLen, keyLen + IVLen));
var w = fs.createWriteStream(output);
r.pipe(decrypt).pipe(w);

Related

Encrypt des-ede3-cbc nodejs

I am trying to encrypt a string with des-ede3-cbc algorithm
My base64 password is sq7HjrUOBfKmC576ILgskD5srU870gJ7, the message I want to encrypt is 06080232580 the result I should have in hexadecimal is a5334014a4f010c8779cef789886c123
First try
const iv = Buffer.alloc(8);
const cipher = crypto.createCipheriv('des-ede3-cbc', Buffer.from('sq7HjrUOBfKmC576ILgskD5srU870gJ7', 'base64'), iv);
let ciph = cipher.update('06080232580', 'utf8', 'hex');
ciph += cipher.final('hex');
console.log(ciph);
The result is a5334014a4f010c8300101ae242354de
An other test
let shortkey = Buffer.from('06080232580', 'utf8');
let key = Buffer.alloc(24);
key.fill('\0');
for (i = 0; i < shortkey.length; i++) {
key[i] = shortkey[i];
}
let IV = Buffer.alloc(8);
const cipher = crypto.createCipheriv('des-ede3-cbc', key, IV);
password = Buffer.from('sq7HjrUOBfKmC576ILgskD5srU870gJ7', 'base64');
let encryptedArr = [cipher.update(password)];
encryptedArr.push(cipher.final());
encrypted = Buffer.concat(encryptedArr);
console.log(encrypted.toString('hex'));
The result is 6f6b59b6c3ea45592bedbd86db4f31cc5da23d85e2ff773940aaa39e2efdc4ae
Y have my old code working in php
<?php
$message = "06080232580";
$key = base64_decode("sq7HjrUOBfKmC576ILgskD5srU870gJ7");
$l = ceil(strlen($message) / 8) * 8;
$message = $message.str_repeat("\0", $l - strlen($message));
$result = substr(openssl_encrypt($message, 'des-ede3-cbc', $key, OPENSSL_RAW_DATA, "\0\0\0\0\0\0\0\0"), 0, $l);
echo implode(unpack("H*", $result));
The result id a5334014a4f010c8779cef789886c123
Found the solution
let shortkey = Buffer.from('06080232580', 'utf8');
let key = Buffer.alloc(16);
key.fill('\0');
for (i = 0; i < shortkey.length; i++) {
key[i] = shortkey[i];
}
let IV = Buffer.alloc(8);
const password = Buffer.from('sq7HjrUOBfKmC576ILgskD5srU870gJ7', 'base64');
const cipher = crypto.createCipheriv('des-ede3-cbc', password, IV);
cipher.setAutoPadding(false)
let encryptedArr = [cipher.update(key)];
encryptedArr.push(cipher.final());
encrypted = Buffer.concat(encryptedArr);
console.log(encrypted.toString('hex'));

Unable to decrypt a pdf file Using 'aes-256-cbc' algorithm in node js

I am trying to decrypt a PDF file using node js,PDF file encrypted by third party using C# .
I am having a hard time because I keep getting this error:
D:\IMP\DevOps Implementation\New folder (2)> node index1.js
internal/crypto/cipher.js:172
const ret = this[kHandle].final();
^
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
at Decipheriv.final (internal/crypto/cipher.js:172:29)
at Object.AESCrypt.decrypt (D:\IMP\DevOps Implementation\New folder (2)\index1.js:12:18)
at D:\IMP\DevOps Implementation\New folder (2)\index1.js:57:24
at FSReqCallback.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:63:3) {
library: 'digital envelope routines',
function: 'EVP_DecryptFinal_ex',
reason: 'bad decrypt',
code: 'ERR_OSSL_EVP_BAD_DECRYPT'
}
we are using below code for encryption(C#)
private void FileEncrypt(string inputFile, string outputfile, string password)
{
byte[] salt = GenerateSalt();
byte[] passwords = Encoding.UTF8.GetBytes(password);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwords, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
AES.Padding = PaddingMode.Zeros;
using (FileStream fsCrypt = new FileStream(inputFile + ".aes", FileMode.Create))
{
fsCrypt.Write(salt, 0, salt.Length);
using (CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
{
byte[] buffer = new byte[1048576];
int read;
while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
cs.Write(buffer, 0, read);
}
}
}
}
}
we are using below code for decryption(Node js)
AESCrypt.encrypt = function(cryptkey, iv, cleardata) {
var encipher = crypto.createCipheriv('aes-256-cbc', cryptkey, iv);
return Buffer.concat([
encipher.update(cleardata),
encipher.final()
]);
}
function decrypted(){
var enc;
fs.readFile('./resource/test.pdf', function (err,data) {
if (err) {
return console.log(err);
}
var bufferenc = new Buffer.from(data);
var dec = AESCrypt.decrypt(cryptkey,iv, bufferenc);
console.log(dec);
// var buffer = new Buffer.from(dec);
fs.writeFileSync('./resource/decrypted.pdf',dec);
});
}
Unable to decrypt a pdf file Using 'aes-256-cbc' algorithm in node js
You can try this code to decrypt the pdf data, it's working for me with the C# code:
const fs = require('fs');
const crypto = require("crypto");
function FileDecrypt(inputFile, outputfile, password)
{
// Read the entire file into the buffer.
let buffer = fs.readFileSync(inputFile);
// Read the first eight bytes as a salt.
let salt = buffer.slice(0,8);
let cipherText = buffer.slice(8);
// use key derivation function to get key and iv.
let derivedBytes = crypto.pbkdf2Sync(password, salt, 50000, 48, "sha1");
let key = derivedBytes.slice(0, 32);
let iv = derivedBytes.slice(32);
let cipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
// Switch off auto padding in this context
cipher.setAutoPadding(false);
let decryptedData = Buffer.concat([cipher.update(cipherText), cipher.final()]);
fs.writeFileSync(outputfile, decryptedData);
}
FileDecrypt("encrypted.pdf", "node-decrypted.pdf", "password");

Secure token created in node different from .net

I'm trying to implement reCaptcha Secure Tokens in nodejs.
Looked at the examples made in Java and in .NET and created this version for node:
exports.getSecureToken = function() {
var algorithm = 'aes-128-ecb';
var tokenObj = { session_id: 'ab0069ec-3c2c-436c-868b-43c7a10db229'/*uuid.v4()*/, ts_ms: 1446560931992/*(new Date()).getTime()*/ };
var text = JSON.stringify(tokenObj);
var shaHash = new Buffer(crypto.createHash('sha1').update('6LeyNOTTVALIDH2RLNaivqrrpm2zh56Y3uHqOjFO'/*config.reCAPTCHASecret*/).digest('hex'), 'hex');
var key = shaHash.slice(0, 16);
var cipher = crypto.createCipher(algorithm, key, key);
var encryptedToken = cipher.update(text, 'utf8', 'base64') + cipher.final('base64');
var result = encryptedToken.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
return result;
};
Problem is that in .NET I get a token that works (using the right key not included in the post), but in node I get a different token for the same input, and it doesn't works:
.NET - LhPTUELia5vc0X6aDGDtqpsbmB7oqm6vUnzk5BL2auactYXRU5TEUzML8gZ_JubXG07rvJxk1Sb5_a-wqVUGEf_UuO1gGi-WO83yJHOxnjI
node - EGr7drd1JEylwzLGakZ6dpPRSf2nFdpzHOrJlLZlyHYmVRj5obAw7WjPt4W5l0vsywNEqCQ-2_d7qIZOMiOedianfBrQPOBaOmmq44IOB8Q
I got to see that key and input are the same (in .NET and node) right at the moment before encryption, so the problem must(?) be the cipher, any clues?
.NET code for reference:
public static void Main(string[] args)
{
//Your code goes here
Console.WriteLine(EncryptJsonToken(GetJsonToken()));
}
public static string GetJsonToken()
{
//Example: {"session_id": e6e9c56e-a7da-43b8-89fa-8e668cc0b86f,"ts_ms":1421774317718}
string jsonRequest = "{" + string.Format("\"session_id\": {0},\"ts_ms\":{1}", "ab0069ec-3c2c-436c-868b-43c7a10db229", 1446560931992) + "}";
return jsonRequest;
}
public static byte[] getKey()
{
string secretKey = "6LeyNOTTVALIDH2RLNaivqrrpm2zh56Y3uHqOjFO";
SHA1 sha = SHA1.Create();
byte[] dataToHash = Encoding.UTF8.GetBytes(secretKey);
byte[] shaHash = sha.ComputeHash(dataToHash);
byte[] first16OfHash = new byte[16];
Array.Copy(shaHash, first16OfHash, 16);
return first16OfHash;
}
public static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Mode = CipherMode.ECB;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
public static string EncryptJsonToken(string jsonToken)
{
byte[] encrypted = EncryptStringToBytes_Aes(jsonToken, getKey(), getKey());
//Base64 encode the encrypted data
//Also applys the URL variant of base64 encoding, unfortunately the HttpServerUtility.UrlTokenEncode(encrypted) seems to truncate the last value from the string so we can't use it?
return Convert.ToBase64String(encrypted, Base64FormattingOptions.None).Replace("=", String.Empty).Replace('+', '-').Replace('/', '_');
}
To debug in .NET: DEMO
You have two problems:
You're using JSON.stringify() to produce a valid JSON string, but the GetJsonToken() method in the C# code doesn't produce a valid JSON string. There are " missing for the UUID and there is a space between the session_id key and its value for some reason. You have to reflect those differences in JavaScript:
var uuidToken = "ab0069ec-3c2c-436c-868b-43c7a10db229";
var time = 1446560931992;
var text = "{\"session_id\": "+uuidToken+",\"ts_ms\":"+time+"}";
There is no such function crypto.createCipher(algorithm, key, key). There is however crypto.createCipheriv(algorithm, key, iv). createCipher(algorithm, password) can be used if one has a password instead of a key which you don't have. Since there is no IV for ECB mode, you can pass in an empty (binary) string as the IV.
Full code:
var crypto = require("crypto");
var algorithm = 'aes-128-ecb';
var uuidToken = "ab0069ec-3c2c-436c-868b-43c7a10db229";
var time = 1446560931992;
var text = "{\"session_id\": "+uuidToken+",\"ts_ms\":"+time+"}";
console.log("Token: " + text);
var shaHash = crypto.createHash('sha1').update('6LeyNOTTVALIDH2RLNaivqrrpm2zh56Y3uHqOjFO').digest();
var key = shaHash.slice(0, 16);
var cipher = crypto.createCipheriv(algorithm, key, "");
var encryptedToken = cipher.update(text, 'utf8', 'base64') + cipher.final('base64');
var result = encryptedToken.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
console.log("result: " + result);
console.log("expected: LhPTUELia5vc0X6aDGDtqpsbmB7oqm6vUnzk5BL2auactYXRU5TEUzML8gZ_JubXG07rvJxk1Sb5_a-wqVUGEf_UuO1gGi-WO83yJHOxnjI");
Output:
Token: {"session_id": ab0069ec-3c2c-436c-868b-43c7a10db229,"ts_ms":1446560931992}
result: LhPTUELia5vc0X6aDGDtqpsbmB7oqm6vUnzk5BL2auactYXRU5TEUzML8gZ_JubXG07rvJxk1Sb5_a-wqVUGEf_UuO1gGi-WO83yJHOxnjI
expected: LhPTUELia5vc0X6aDGDtqpsbmB7oqm6vUnzk5BL2auactYXRU5TEUzML8gZ_JubXG07rvJxk1Sb5_a-wqVUGEf_UuO1gGi-WO83yJHOxnjI

NodeJS encryption: re-use cipher object to improve performance

I would like to nodejs and an encrypted MongoDB database. I am concerned about performance. Consider the following use case:
I have an encrypted DB from which I retrieve a list of encrypted strings (names for example)
[_encrypted_name_1, _encrypted_name_2, ...]
I would like to decrypt all elements from that list
Since I am concerned about performance I did some tests to figure it out.
I observed that encrypting/decrypting lots of small strings is very slow compared to encrypting/decrypting a very large string.
Consider the following example:
var crypto = require('crypto'),
_ = require('lodash'),
encryptedStringArray = [],
decryptedStringArray = [],
encryptedLongString,
NB_ITERATION = 100000,
stringArray = [],
longString = '',
myString = 'Your Name';
function encrypt(text){
var cipher = crypto.createCipher('aes-256-cbc', 'd6F3Efeq');
var crypted = cipher.update(text, 'utf8', 'hex');
crypted += cipher.final('hex');
return crypted;
}
function decrypt(text){
var decipher = crypto.createDecipher('aes-256-cbc', 'd6F3Efeq');
var dec = decipher.update(text, 'hex', 'utf8');
dec += decipher.final('utf8');
return dec;
}
// SLOW: ARRAY OF STRINGS
console.time("slow");
for (var i = 0; i < NB_ITERATION; i += 1) {
stringArray.push(myString);
}
_.forEach(stringArray, function (item) {
encryptedStringArray.push(encrypt(item));
});
_.forEach(encryptedStringArray, function (item) {
decryptedStringArray.push(decrypt(item)); //.toString());
});
console.timeEnd("slow");
// FAST: SUPER LONG STRING
console.time("fast");
for (var i = 0; i < NB_ITERATION; i += 1) {
longString += myString;
}
encryptedLongString = encrypt(longString);
decrypt(encryptedLongString);
console.timeEnd("fast");
// **********************************************************************
// FOR LOOP
// **********************************************************************
//
console.time("for_loop");
stringArray = [];
encryptedStringArray = [];
decryptedStringArray = [];
for (var i = 0; i < NB_ITERATION; i += 1) {
stringArray.push(myString);
}
_.forEach(stringArray, function (item) {
encryptedStringArray.push(myString);
});
_.forEach(encryptedStringArray, function (item) {
decryptedStringArray.push(myString);
});
console.timeEnd("for_loop");
// **********************************************************************
// CREATION OF CIPHER ONLY - NO ENCRYPTION
// **********************************************************************
function noencrypt(text){
var cipher = crypto.createCipher('aes-256-cbc', 'd6F3Efeq');
// var crypted = cipher.update(text, 'utf8', 'hex');
// crypted += cipher.final('hex');
// return crypted;
return text;
}
function nodecrypt(text){
var decipher = crypto.createDecipher('aes-256-cbc', 'd6F3Efeq');
// var dec = decipher.update(text, 'hex', 'utf8');
// dec += decipher.final('utf8');
// return dec;
return text;
}
// SLOW
console.time("slow_nocrypt");
for (var i = 0; i < NB_ITERATION; i += 1) {
stringArray.push(myString);
}
_.forEach(stringArray, function (item) {
encryptedStringArray.push(noencrypt(item));
});
_.forEach(encryptedStringArray, function (item) {
decryptedStringArray.push(nodecrypt(item)); //.toString());
});
console.timeEnd("slow_nocrypt");
// FAST
console.time("fast_nocrypt");
for (var i = 0; i < NB_ITERATION; i += 1) {
longString += myString;
}
encryptedLongString = noencrypt(longString);
nodecrypt(encryptedLongString);
console.timeEnd("fast_nocrypt");
Here are the results:
slow: 2078ms
fast: 20ms
for_loop: 14ms
slow_nocrypt: 1898ms
fast_nocrypt: 1ms
Most of the time is spent creating Cipher objects. Therefore, I would like to use the same cipher object to encrypt/decrypt a list of of strings. In this case one needs to properly deal with the initialisation vector:
How to deal with the initialisation vector?
Once a cipher object is created, is it possible to change its initialisation vector?
The ideal scenario would probably be to use stream objects illustrated by the following pseudo-code:
var myArray = [
{to_encrypt: 'Your Name 1', iv: INIT_VECTOR_1},
{to_encrypt: 'Your Name 2', iv: INIT_VECTOR_2}];
var encrypted_array = [];
streamify(myArray)
.pipe(CIPHER_WITH_IV_UPDATE)
.write(streamify(encrypted_array));
Your code is actually slow because symmetric algorithms work in discrete blocks.
When you encrypt the single string Your Name, the cipher will pad it with random bytes to reach a multiple of the block size (128 bits).
Therefore, your slow version is actually encrypting more data per string.
To speed it up, either use a smaller block size or encrypt more data per block.

Silverlight Speex playing at fast rate

I'm using Speex to encode the raw data but after I decode the data the audio plays at a faster rate because it makes you sound like a chipmunk. I'm using NSpeex and Silverlight 4.
8kHz Sampling
Encoding Function:
JSpeexEnc encoder = new JSpeexEnc();
int rawDataSize = 0;
public byte[] EncodeAudio(byte[] rawData)
{
var encoder = new SpeexEncoder(BandMode.Narrow);
var inDataSize = rawData.Length / 2;
var inData = new short[inDataSize];
for (var index = 0; index < rawData.Length; index += 2)
{
inData[index / 2] = BitConverter.ToInt16(rawData, index);
}
inDataSize = inDataSize - inDataSize % encoder.FrameSize;
var encodedData = new byte[rawData.Length];
var encodedBytes = encoder.Encode(inData, 0, inDataSize, encodedData, 0, encodedData.Length);
byte[] encodedAudioData = null;
if (encodedBytes != 0)
{
encodedAudioData = new byte[encodedBytes];
Array.Copy(encodedData, 0, encodedAudioData, 0, encodedBytes);
}
rawDataSize = inDataSize; // Count of encoded shorts, for debugging
return encodedAudioData;
}
Decoding Function:
SpeexDecoder decoder = new SpeexDecoder(BandMode.Narrow);
public byte[] Decode(byte[] encodedData)
{
try
{
short[] decodedFrame = new short[8000]; // should be the same number of samples as on the capturing side
int decoderBytes = decoder.Decode(encodedData, 0, encodedData.Length, decodedFrame, 0, false);
byte[] decodedData = new byte[encodedData.Length];
byte[] decodedAudioData = null;
decodedAudioData = new byte[decoderBytes * 2];
for (int shortIndex = 0, byteIndex = 0; byteIndex < decoderBytes; shortIndex++)
{
BitConverter.GetBytes(decodedFrame[shortIndex + byteIndex]).CopyTo(decodedAudioData, byteIndex * 2);
byteIndex++;
}
// todo: do something with the decoded data
return decodedAudioData;
}
catch (Exception ex)
{
ShowMessageBox(ex.Message.ToString());
return null;
}
}
Playing the audio:
void PlayWave(byte[] PCMBytes)
{
byte[] decodedBuffer = Decode(PCMBytes);
MemoryStream ms_PCM = new MemoryStream(decodedBuffer);
MemoryStream ms_Wave = new MemoryStream();
_pcm.SavePcmToWav(ms_PCM, ms_Wave, 16, 8000, 1);
WaveMediaStreamSource WaveStream = new WaveMediaStreamSource(ms_Wave);
mediaElement1.SetSource(WaveStream);
mediaElement1.Play();
}
Sorry guys for the late response but I figured out what the problem was.
Inside my decode function I loop through the decoded short array but I'm only copying half of the bytes into my new byte array.
It needs to look something like this:
decodedAudioData = new byte[decoderBytes * 2];
for (int shortIndex = 0, byteIndex = 0; shortIndex < decodedFrame.Length; shortIndex++, byteIndex += 2)
{
byte[] temp = BitConverter.GetBytes(decodedFrame[shortIndex]);
decodedAudioData[byteIndex] = temp[0];
decodedAudioData[byteIndex + 1] = temp[1];
}

Resources