NodeJS crypto RSA, not compatible with Elixir/Erlang PublicKey? - node.js

I’m having trouble trying to decrypt in Elixir (using ExCrypto - which is a only a wrapper around crypto and public_key) encrypted by NodeJS Crypto.
Here’s what works :
encrypt_public in NodeJS -> decrypt_private NodeJS (Works)
encrypt_public in Elixir -> decrypt_pricate in Elixir (Works)
But :
encrypt_public in NodeJS -> decrypt_private in Elixir (Does Not Work)
It seems that an encrypt_public from Node, can’t be decrypted by a decrypt_private in Elixir.
I even tried the hard way directly with the Erlang module public_key, it gives me the same result.
Here’s what I tried :
var fs = require("fs")
var crypto = require("crypto")
var rsa_key = fs.readFileSync('./priv/public.key').toString()
var rsa_priv = fs.readFileSync('./priv/private.key').toString()
var buffer = Buffer.from("Hello world")
var encrypted_auth = crypto.publicEncrypt({key: rsa_key}, buffer)
console.log("copy/paste this in iex: ", encrypted_auth.toString("Base64"))
var crypted_buffer = Buffer.from(encrypted_auth)
var decrypted_auth = crypto.privateDecrypt({key: rsa_priv, passphrase: "my_pass_phrase"}, crypted_buffer)
console.log("--- Res: ", decrypted_auth.toString())
But as soon as you copy/paste the Base64 generated in Node Here’s what I get :
key = ExPublicKey.load!("./priv/private.key", "my_pass_phrase")
cipher = "PASTED BASE64 FROM NODE"
ExPublicKey.decrypt_private(cipher, key)
returns :error
I have suspected the rsa padding to be different, but it doesn’t seems to be that… Does anyone have a clue ?

There were 2 issues
The padding by default in Node is : RSA_PKCS1_OAEP_PADDING
while the default one is erlang is : rsa_pkcs1_padding
the solution is to modify the padding in Node :
var encrypted_auth = crypto.publicEncrypt({key: rsa_key, padding: crypto.constants.RSA_PKCS1_PADDING}, buffer)
The second issue, was that EXPublicKey was trying to Base.url_decode64 while the Base64 generated by Node is not url safe
To fix that :
ExPublicKey.decrypt_private(cipher, key, url_safe: false)

Related

Node Crypto Encryption and decryption alogorithem

I am looking for standard utility for supporting the encryption & decryption based on the below algorithm in node server side.
algorithm: aes-256-gcm
using the createCipheriv, createDecipheriv from nodejs crypto module.
Please suggest some working references
The utility need to build it from your side, and based on your needs below are the small code may help you to build the utility:
const crypto = require('crypto');
// This two value (ivValueEn , ivValueDe) should be same, to decode the text properly!
// you can generate any strong value and past it at the same place of you can have it from the config value.
const ivValueEn = "c5949f09a7e67318888c5949f09a7e6c09ca51e602867318888c5949f09a7e6c09ca51e602867318888";
const ivValueDe = "c5949f09a7e6c09ca51e602867318888c5949f09a7e6c09ca51e602867319ca51e602867318888";
// insert your key here, better to chose a strong key
const keyValue = "c5949f09a7e6c09a7e6c09ca51e602867318888c5949f09a7e6c09ca51e602867318888";
// slicing are not required you can remove the slice(0,64) part & the console.log as well
const alog = 'aes-256-gcm'
const ivEn = ivValueEn.toString('hex').slice(0,64); console.log(ivEn);
const ivDe = ivValueDe.toString('hex').slice(0,64); console.log(ivDe);
// The one that working with me correctly is to slicing the key to (32) characters.
const key = keyValue.slice(0,32);
const cipher = crypto.createCipheriv(alog,key,ivEn);
const decipher = crypto.createDecipheriv (alog,key,ivDe);

How to rewrite NodeJS CryptoJS functions so they work in ReactJS web?

I have some code that was created for me by a past contractor that provided a working implementation of CryptoJS between a Unity3D client and NodeJS server app (as in encryption of client-server messages).
I am working on an implementation that needs to call the same server endpoints from a React web app. Basically, I need to be able to ensure that the React app provides the same results when encrypting and decrypting messages as the server (and hence the Unity3D implementation) does.
The existing encrypt/decrypt functions in the NodeJS app are shown below. They are somewhat more advanced than the standard CryptoJS examples and when I attempt to use them as-is in React, I get this error:
TypeError: cryptoJS.createHash is not a function. (In 'cryptoJS.createHash('md5')', 'cryptoJS.createHash' is undefined)
After some researching, I understand that potentially the 'use as-is' approach will fail because it's using a server side library on the client side, but I have been able to find anything that would explain how I would recreate the same code to work client-side.
encrypt(text, key, iv) {
const keyBuffer = Buffer.from(cryptoJS.createHash('md5').update(key).digest('hex'), "hex")
const ivBuffer = Buffer.from(cryptoJS.createHash('md5').update(iv).digest('hex'), "hex")
const textBuffer = Buffer.from(text, 'utf8')
let cipher = cryptoJS.createCipheriv(algorithm, keyBuffer, ivBuffer)
let encryptedText = Buffer.concat([cipher.update(textBuffer), cipher.final()])
return encryptedText.toString("base64")
}
decrypt(text, key, iv) {
const keyBuffer = Buffer.from(cryptoJS.createHash('md5').update(key).digest('hex'), "hex")
const ivBuffer = Buffer.from(cryptoJS.createHash('md5').update(iv).digest('hex'), "hex")
let decipher = cryptoJS.createDecipheriv(algorithm, keyBuffer, ivBuffer)
const textBuffer = Buffer.from(text, 'base64')
var decipheredContent = Buffer.concat([decipher.update(textBuffer), decipher.final()])
return decipheredContent.toString("utf8")
}
So, I need to figure out how to replace these encrypt/decrypt functions with ones that compile and function in client side in React web. Any help in steering me in the right direction or assisting with the code would be greatly appreciated. Thanks for taking the time to read my question.

HMAC Binary Digest Node vs Ruby

I am trying to implement a Node API using Ruby documentation (ugh). The issue specifically is around verifying a secret, which is put through an HMAC digest and then base64 encoded.
I can't seem to get the two to equate. Here are the same snippets in Node & Ruby:
Note: The below can also be viewed online via repl.it:
Ruby (https://repl.it/repls/SarcasticSpottedSymbol)
Node (https://repl.it/repls/AncientQuarrelsomeWearable)
Node
const crypto = require('crypto');
let text = 'example';
let key = '123';
let h = crypto.createHmac('sha256', key).update(text).digest('binary');
Buffer.from(h).toString('base64');
# => 'acKNVMOwSUUowqdZw7HCnMKOwofCqcO5wp51wqXCiBvCkmfDrjkmwrzDtizCmS3ChMK6'
Ruby
require 'openssl'
require 'base64'
text = 'example'
key = '123'
h = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, text)
Base64.strict_encode64(h)
# => 'aY1U8ElFKKdZ8ZyOh6n5nnWliBuSZ+45Jrz2LJkthLo='
Switching both over to hex works, e.g.
Node
crypto.createHmac('sha256', key).update(text).digest('hex')
Ruby
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, text)
Unfortunately it isn't up to me to switch to hex - the web service uses the ruby code to sign.
Looking up the ruby docs for OpenSSL::HMAC.digest states:
Returns the authentication code as a binary string.
Just outputting the result from the HMAC, I can't tell whether this is a difference or just a rendering issue:
Node
crypto.createHmac('sha256', key).update(text).digest('binary');
# => 'iTðIE(§Yñ©ùu¥\u001bgî9&¼ö,-º'
Ruby
OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, text)
# => "i\x8DT\xF0IE(\xA7Y\xF1\x9C\x8E\x87\xA9\xF9\x9Eu\xA5\x88\e\x92g\xEE9&\xBC\xF6,\x99-\x84\xBA"
How can I get these two to equate?
Thank you!
By not inputting any specific encoding to Node's digest method, the raw unicode buffer is output - matching Ruby's.
This is the end result:
crypto = require('crypto');
text = 'example';
key = '123';
h = crypto.createHmac('sha256', key).update(text).digest();
Buffer.from(h).toString('base64');
Who would've thought - you could just pass nothing into the method...
To get Ruby to output Base64:
require 'openssl'
require 'base64'
text = 'example'
key = '123'
Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, text))
# "aY1U8ElFKKdZ8ZyOh6n5nnWliBuSZ+45Jrz2LJkthLo=\n"
You can delete the \n newlines and trailing padding if you want.

Encrypt data on angular decrypt on Nodejs

What's the way of doing this? I tried using CryptoJS on angular and Crypto Module on node, without success I keep getting description error
Angular encrypt method :
_rsaEnc(p) {
var e = new JSEncrypt();
const key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/kaskaskKAS';
e.setPublicKey(key);
return e.encrypt(p);
}
Node decrypt method
privK = {
key: fs.readFileSync('./app/services/private.pem').toString(),
passphrase: 'xxxxxx'
};
var buf = Buffer.from(base64Data, 'base64');
origData = crypto.privateDecrypt(privK, buf);
return origData.toString('utf-8');
error:
Error: error:040A1079:rsa
routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error
Ended up changing the angular lib to jsencrypt, CryptoJS doesnt support RSA, and changed node lib to node-rsa to set the encryption scheme to pkcs1 with
myDecrypter.setOptions({encryptionScheme: 'pkcs1'});

NodeJS | Crypto Encryption is not producing correct results

I have a tricky problem to resolve. Not sure how to explain it correctly but will try my best. So here is what I am trying to do: I am trying to use a 3rd Party API, which wants me to encrypt a value and submits it. I successfully achieved it through C# code using the following block:
public string Encrypt(byte[] dataToEncrypt, byte[] keyBytes)
{
AesManaged tdes = new AesManaged();
tdes.KeySize = 256;
tdes.BlockSize = 128;
tdes.Key = keyBytes;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
ICryptoTransform crypt = tdes.CreateEncryptor();
byte[] cipher = crypt.TransformFinalBlock(dataToEncrypt, 0, dataToEncrypt.Length);
tdes.Clear();
return Convert.ToBase64String(cipher, 0, cipher.Length);
}
Now, I am trying to achieve the same in Node. I wrote the following function.
encrypt(buffer){
var buffbytes = new Buffer('my app key goes here to be used as password','utf8'); //converts the app key into buffer stream
return this.encrypt_key(new Buffer(buffer,'utf8'), buffbytes);
},
encrypt_key(buffer, keybytes){
var cipher = crypto.createCipher('aes-128-ecb',keybytes);
var crypted = cipher.update(buffer,'utf8','base64');
crypted = crypted+ cipher.final('base64');
return crypted;
},
This encryption code works fine. It encrypts it fine, but it doesn't encrypt it similar to what c# code does. When I take the encrypted text from C# code, and inject the encrypted result into the API call, it passes through fine, but when I use my encrypted result into the API call, it fails mentioning that the format of my key is incorrect.
I would like to know if these code blocks are same or not. I assume it is same, because both code using 128 bit AES, ECB Cipher and default padding for Crypto Node module is PKCS5 which is same as PKCS7 for 128 bit encryption. Please Help!
Edit: 9/19/2017
Fixed as per #smarx solution:
encrypt(buffer){
var buffbytes = new Buffer(helper.Constants.AppKey,'utf8'); //converts the app key into buffer stream
return this.encrypt_key(new Buffer(buffer,'utf8'), helper.Constants.AppKey);
},
encrypt_key(buffer, key){
var cipher = crypto.createCipheriv('aes-256-ecb',key,new Buffer(0));
var crypted = cipher.update(buffer,'utf8','base64');
crypted = crypted+ cipher.final('base64');
console.log('printed: ', crypted);
return crypted;
},
In your Node.js code, you're using the wrong cipher algorithm. Use aes-256-ecb, since you're using a 256-bit key. Also, be sure to use createCipheriv, since createCipher expects a password from which it derives an encryption key.
One-line fix:
const cipher = crypto.createCipheriv('aes-256-ecb', key, new Buffer(0));
The below two programs produce identical output (Q9VZ73VKhW8ZvdcBzm05mw==).
C#:
var key = System.Text.Encoding.UTF8.GetBytes("abcdefghijklmnopqrstuvwxyz123456");
var data = System.Text.Encoding.UTF8.GetBytes("Hello, World!");
var aes = new AesManaged {
Key = key,
Mode = CipherMode.ECB,
};
Console.WriteLine(Convert.ToBase64String(
aes.CreateEncryptor().TransformFinalBlock(data, 0, data.Length)));
Node.js:
const crypto = require('crypto');
const key = 'abcdefghijklmnopqrstuvwxyz123456';
const data = 'Hello, World!';
const cipher = crypto.createCipheriv('aes-256-ecb', key, new Buffer(0));
console.log(cipher.update(data, 'utf-8', 'base64') + cipher.final('base64'));

Resources