In order to write an simple nodejs app talking to an server written in java I have to implement the following functionality for nodejs.
public class Crypto {
Cipher decipher;
byte[] salt = {
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
(byte) 0x0A, (byte) 0x0B, (byte) 0x0C, (byte) 0x0D
};
int iterationCount = 10;
public Crypto(String pass) {
try {
KeySpec keySpec = new PBEKeySpec(pass.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance(
"PBEWithMD5AndTripleDES").generateSecret(keySpec);
ecipher = Cipher.getInstance("PBEWithMD5AndTripleDES/CBC/PKCS5Padding");
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
decipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception ex) {
}
}
}
I use the crypto module of nodejs
var crypto = require('crypto'),
pass = new Buffer(wek),
salt = new Buffer([0x01, 0x02, 0x03, 0x04, 0x0A, 0x0B, 0x0C, 0x0D])
password = 'mySecretPassword'
key = crypto.pbkdf2(pass, salt, 10, 256)
cipher,
encrypted;
cipher = crypto.createCipher('des-ede-cbc', key);
encrypted = cipher.update(new Buffer('the very secred information'));
After sending the encrypted information to the server, I can't decrypt the message with the decipher Object as listed in the java code sample above. I think the main problem is the md5 part. I can't figure out how to implement that with the crypto nodejs module. Has anyone an idea how to solve this problem? Or is ther any other module or library to achieve that?
EDIT: I tried another module for nodejs: node-forge
forge = require('node-forge')
var numIterations = 10,
keyLength = 24,
password = forge.util.createBuffer('mySecretPassword'),
salt = new forge.util.ByteBuffer(new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x0A, 0x0B, 0x0C, 0x0D])),
derivedKey = forge.pkcs5.pbkdf2(password, salt.getBytes(), numIterations, keyLength, forge.md.md5.create())
iv = {}; // TODO... ???
var cipher = forge.des.createEncryptionCipher(derivedKey);
cipher.start(iv);
cipher.update('the very secred information');
cipher.finish();
var encrypted = cipher.output;
But I have several problems/questions:
salt calculation match with the java implementation?keyLength is used in the java implementation? initialization vector generated in the java implementation? In the last code sample with node-forgeI have to provide the iv on cipher.start(iv). In the java code I can't see how this is done. In my opinion the iv must be the same on client and server or is this incorrect?I reverse engineered the DESede part of the key derivation function found at com.sun.crypto.provider.PBES1Core#deriveCipherKey();
We use Jasypt as encryption library in a Java server and our node.js server is able to encrypt and decrypt with this. I hope it helps (Written in ES2015, runs in node v4.0.0 and up):
'use strict';
var crypto = require('crypto');
class Encryption {
constructor() {
this.privateKey = new Buffer('<your password>', 'utf-8');
}
encrypt(message) {
var salt = crypto.randomBytes(8);
var key = this._generateKey(this.privateKey, salt);
var cipher = crypto.createCipheriv('des-ede3-cbc', this._subBuf(key, 0, 24), this._subBuf(key, 24));
var result = cipher.update(message, 'utf-8', 'hex');
return salt.toString('hex') + result + cipher.final('hex');
}
decrypt(message) {
var salt = new Buffer(message.substr(0, 16), 'hex');
var key = this._generateKey(this.privateKey, salt);
message = message.substr(16);
var decipher = crypto.createDecipheriv('des-ede3-cbc', this._subBuf(key, 0, 24), this._subBuf(key, 24));
var result = decipher.update(message, 'hex', 'utf-8');
return result + decipher.final('utf-8');
}
_generateKey(password, salt) {
if (!(password instanceof Buffer)) {
throw new Error('Password needs to be a buffer');
}
if (!(salt instanceof Buffer) || salt.length != 8) {
throw new Error('Salt needs to be an 8 byte buffer');
}
var iterations;
for(iterations = 0; iterations < 4 && salt[iterations] == salt[iterations + 4]; ++iterations) {}
if(iterations == 4) {
for(iterations = 0; iterations < 2; ++iterations) {
var tmp = salt[iterations];
salt[iterations] = salt[3 - iterations];
salt[2] = tmp; // Seems like an error that we have to live with now
}
}
var result = new Buffer(32);
for(iterations = 0; iterations < 2; ++iterations) {
var intermediate = new Buffer(salt.length / 2);
for (let i = 0; i < salt.length / 2; i++) {
intermediate[i] = salt[iterations * (salt.length / 2) + i];
}
for(let i = 0; i < 1000; ++i) {
var hash = crypto.createHash('md5');
hash.update(intermediate);
hash.update(password);
intermediate = hash.digest();
}
for (let i = 0; i<intermediate.length; i++) {
result[i + (iterations * 16)] = intermediate[i];
}
}
return result;
}
_subBuf(buffer, start, length) {
if (!length) {
length = buffer.length - start;
}
var result = new Buffer(length, 'hex');
for (let i = 0; i < length; i++) {
result[i] = buffer[i + start]
}
return result;
}
}
To explain a little what's going on:
Some code clean up might be necessary here, but it should at least get you started in the right direction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With