I'm trying to convert a Node.js authentication using passport-local to a Java based one
Node.js use this code:
crypto.pbkdf2Sync(password, new Buffer(this.salt, 'base64'), 10000, 64).toString('base64');
I'm trying to replicate this in java using this code:
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 100000, 64 * 8);
SecretKeyFactory key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hashedPassword = key.generateSecret(spec).getEncoded();
hashPassword = new String(Base64.encodeBase64(hashedPassword));
But my generated key is always different than node.js one. i've also tried with different encodings in getBytes() using UTF-16LE as someone pointed here in other threads but with no luck.
I'm pretty sure that pbkdf2Sync in node.js use SHA1 when no digest is used. Can anyone confirm also this?
Thank you
UPDATE
I've resolved using Java8 Base64 decoding.
This is the right function to use (if someone needs it):
final char[] cPassword = password.toCharArray();
final byte[] bSalt = Base64.getDecoder().decode(utente.getSalt().getBytes());
KeySpec spec = new PBEKeySpec(cPassword, bSalt, 10000, 64 * 8);
SecretKeyFactory key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hashedPassword = key.generateSecret(spec).getEncoded();
String hashPassword = new String(Base64.getEncoder().encode(hashedPassword));
hashPassword will have the same Node.js password
Related
I'm working on a project that uses an external API that is written in C#. In this API, I need to send some encrypted data while using their key that was provided when I started a session.
My project is built using NodeJS, so to do this encryption I am using the CryptoJS module. While talking with the API developer, he sent me a code showing how the encryption is made in C#. Here is the code with an example key and value to be encrypted.
TripleDESCryptoServiceProvider mDes = new TripleDESCryptoServiceProvider();
mDes.Key = Convert.FromBase64String("bv8czu/UPuZg6xNxnJAD/vRtbng9mQZX");
mDes.Mode = CipherMode.ECB;
mDes.Padding = PaddingMode.Zeros;
ICryptoTransform mDesEnc = mDes.CreateEncryptor();
byte[] data = Encoding.UTF8.GetBytes("1"); //value to encrypt
var crypto = Convert.ToBase64String(mDesEnc.TransformFinalBlock(data, 0, data.Length));
Console.WriteLine(crypto);
This results in the following encryption: 3EAaQjY2dgA=
As you can see in the code or running it, the encryption uses 3DES, mode ECB, Zero Padding and the key while in a byte array format has 24 bytes.
So I started to recreate this code in JavaScript and ended up with this:
var CryptoJS = require("crypto-js");
var encryptStringWith3DES = function(toEncrypt, key){
toEncrypt = "1";
key = "bv8czu/UPuZg6xNxnJAD/vRtbng9mQZX";
key = CryptoJS.enc.Utf8.parse(key);
console.log(toEncrypt + " " + key);
var encrypted = CryptoJS.TripleDES.encrypt(toEncrypt, key,{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
}).toString();
console.log(encrypted);
}
And it results in the following encryption: dj1byXBV6ug=
I have searched for many answers and none worked. My suspicion is with how CryptoJS receives the key as a parameter and how the key and data are parsed.
Does any of you know how to make my JS code result in the same encrypted data as the C# one?
I am trying to replicate the Java code for AES Encryption and Decryption in Node JS.
Java Code
SecretKeySpec skeySpec;
String key = "a4e1112f45e84f785358bb86ba750f48";
public void encryptString(String key) throws Exception {
try {
skeySpec = new SecretKeySpec(key.getBytes(), "AES");
cipher = Cipher.getInstance("AES");
cipher.init(1, skeySpec);
byte encstr[] = cipher.doFinal(message.getBytes());
String encData = new String(encstr, "UTF-8");
System.out.println(encData);
} catch (NoSuchAlgorithmException nsae) {
throw new Exception("Invalid Java Version");
} catch (NoSuchPaddingException nse) {
throw new Exception("Invalid Key");
}
}
Node JS
var encryptKey = function (text) {
var cipher = crypto.createCipher('aes256', 'a4e1112f45e84f785358bb86ba750f48');
var crypted = cipher.update(text,'utf8', 'hex')
crypted += cipher.final('hex');
console.log(crypted);
return crypted;
}
I am unable to get the exact cipher-text in Node JS, which i am getting in Java.
Your code actually uses different encryption parameters in the 2 cases. AES, being a block cipher, takes: the plain text to encrypt, the initialization vector, also called IV (which is used in conjunction with the plaintext), and the encryption key.
In Java, the IV is, apparently, generated automatically on init() - from the Java SE platform docs for Cipher.init:
The generated parameters can be retrieved using getParameters or getIV
(if the parameter is an IV).
In Node.js, if using the deprecated createCipher function, the IV is generated automatically based on the provided key, probably in a different way than in Java, so you will get a different cipher text. However, you should be using the non-deprecated variant crypto.createCipheriv: https://nodejs.org/docs/latest-v12.x/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options
In order to exactly reproduce the cipher text, you should:
Use the same encryption algorithm on both sides - it's best to specify this exactly, for example aes-256-cbc, or an authenticated encryption scheme such as aes-256-gcm, which is harder to use but provides message authentication.
Use the same IV on both sides, by providing it in the initialization params in Java, and by using createCipheriv in Node; though beware, you should always randomize it in production! See https://stackoverflow.com/a/20888967/6098312
As a closing remark, when using block encryption, you'll usually be generating securely-random IVs, which means the ciphertexts will always differ from one another, even for the same plaintext. This is a good thing! It protects your payload from an attacker who observes the encrypted data and makes conclusions based on message repetitions.
Finally after reviewing Java Docs and Node JS Crypto Docs managed to get the result.
We have to use crypto.createCipheriv() instead of crypto.createCipher with a iv.
Here iv will be null.
Code :
let crypto = require('crypto');
var iv = new Buffer.from(''); //(null) iv
var algorithm = 'aes-256-ecb';
var password = 'a4e1112f45e84f785358bb86ba750f48'; //key password for cryptography
function encrypt(buffer){
var cipher = crypto.createCipheriv(algorithm,new Buffer(password),iv)
var crypted = Buffer.concat([cipher.update(buffer),cipher.final()]);
return crypted;
}
console.log(encrypt(new Buffer('TextToEncrypt')).toString())
I am trying to decrypt some text encrypted client-side and having a difficult time making this happen.
I am using the Standford Javascript Crypto Library (sjcl) on client side which generates a json string that I can send to the server, something like this...
sjcl.encrypt("my_key", "something, something");
and produces this json string...
"{"iv":"tfyF7oMXjauOkRnwaffU3A==","v":1,"iter":10000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"bq5G63+hk0U=","ct":"KahF3hQ3FZZ2l7APxLB4nRvw03Xt/qD04AChoA=="}"
Now I receive this json string server-side, and tried to do this...
dynamic data = JObject.Parse(json);
var key = Encoding.ASCII.GetBytes("my_key");
var iv = Convert.FromBase64String(data.iv.Value);
byte[] cypher = Convert.FromBase64String(data.ct.Value);
string plainText = null;
using (AesCryptoServiceProvider provider = new AesCryptoServiceProvider())
{
provider.Key = key;
provider.IV = iv;
ICryptoTransform decryptor = provider.CreateDecryptor(provider.Key, provider.IV);
using (MemoryStream ms = new MemoryStream(cypher))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
plainText = sr.ReadToEnd();
}
}
}
}
return plainText;
The server side code above crashes on plainText = sr.ReadToEnd; and throws a "The input data is not a complete block". I know that AES requires the cypherText to have a block size of 128 and since the text varies in size, how do I guarantee that it has a block size of 128 and not screw up the intended value? Does setting the padding necessary here?
Also, what's the corresponding mode for "ccm" in the AESCryptoServiceProvider? Do the "iter" & "salt" need to be in play in order to decrypt correctly?
Thanks in advance!
Jaime
I'm getting a 'error:0D07209B:asn1 encoding routines:ASN1_get_object:too long' when trying to sign a object with a PrivateKey I generated, in Node.js.
The buf is a simple object encoded with node-cbor
var ecdh = crypto.createECDH('secp256k1')
ecdh.generateKeys()
var sign = crypto.createSign('RSA-SHA256')
sign.update(buf)
var buf_signed = sign.sign('-----BEGIN PRIVATE KEY-----\n' +
ecdh.getPrivateKey('base64') +
'\n-----END PRIVATE KEY-----' +
'\n-----BEGIN CERTIFICATE-----' +
'\n-----END CERTIFICATE-----', 'binary')
Would the Certificate be strictly necessary? Am I missing any information in the PEM string?
Any help is appreciated, thank you :)
It turns out I was missing that for EC Digital Signing, the right way to do it is using ECDSA.
Node.js doesn't implement it natively, but this module makes a good job of doing so:
https://www.npmjs.com/package/ecdsa
Im Storing SHA256 hashes of user passwords in my database generated by .NET and I need to be able to check them with Node.js. The only problem is that .NET and Node.js create different hashes for the same password.
Password: ThisPassword
.NET:
var ue = new UnicodeEncoding();
var byteSourceText = ue.GetBytes("ThisPassword");
var byteHash = new System.Security.Cryptography.SHA256Managed().ComputeHash(byteSourceText);
return Convert.ToBase64String(byteHash);
//Tlwxyd7HIQhXkN6DrWJtmB9Ag2fz84P/QgMtbi9XS6Q=
Node.js (Using Crypto):
var crypto = require('crypto');
return crypto.createHash('sha256').update('ThisPassword').digest('base64')
//d7I986+YD1zS6Wz2XAcDv2K8yw3xIVUp7u/OZiDzhSY=
I found this, but was unable to figure out how to implement his solution.
Edit: You are using UTF-16 in C#, you must use same encoding in both languages:
Node.js:
var crypto = require("crypto");
var sha256 = crypto.createHash("sha256");
sha256.update("ThisPassword", "utf8");//utf8 here
var result = sha256.digest("base64");
console.log(result); //d7I986+YD1zS6Wz2XAcDv2K8yw3xIVUp7u/OZiDzhSY=
C#:
SHA256 sha256 = SHA256Managed.Create(); //utf8 here as well
byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes("ThisPassword"));
string result = Convert.ToBase64String(bytes);
Console.WriteLine(result); //d7I986+YD1zS6Wz2XAcDv2K8yw3xIVUp7u/OZiDzhSY=
If you're using the .NET Framework's built-in SqlMembershipProvider Class from System.Web.Security, the hash incorporates a salt value as well as the password material when it is generated. Simply hashing the password alone in your node.js will never produce the same result as the hash in the database.
See Microsoft ASP.NET 2.0 Providers: Introduction for a link to .NET source code for the provider that will let you see how the salt value is applied.
You need to include your code if you need more help than this.
I created the node pbkdf2 module(source https://github.com/fundon/pbkdf2)
Required node >= 0.11.11
var pbkdf2 = require('pbkdf2');
var p = 'password';
var s = pbkdf2.generateSaltSync(32);
var pwd = pbkdf2.hashSync(p, s, 1, 20, 'sha256');
var bool = pbkdf2.compareSync(pwd, p, s, 1, 20, 'sha256');