AES encryption in node resulting is different output compared to java - javascript

I'm trying to port the Java code for AES ECB encryption into node.js
The issues is that the output from java and node are not the same.
Here's the java code
public static final String DEFAULT_ENCODING = "UTF-8";
public static final String SEC_PROVIDER = "SunJCE";
public static final String AES_ALGORITHM = "AES";
public static final String RIJNDAEL_CIPHER = **"Rijndael/ECB/NoPadding"**;
public static final int **CIPHER_PAD_SIZE = 32**;
public static final String HEX_KEY = "3b6ce332ca3b6519eac769710f41ca5c";
public static String encryptData(String text, String hexKey) throws
Exception {
byte[] b1 = Hex.decodeHex(HEX_KEY.toCharArray());
SecretKey key = new SecretKeySpec(b1, AES_ALGORITHM);
Cipher cipher = Cipher.getInstance(RIJNDAEL_CIPHER, SEC_PROVIDER);
text = padRightToMod(text, CIPHER_PAD_SIZE);
byte[] buf = text.getBytes(DEFAULT_ENCODING);
cipher.init(Cipher.ENCRYPT_MODE, key);
buf = cipher.doFinal(buf);
String result = new String(Hex.encodeHex(buf));
result = result.toUpperCase();
return result;
}
// ensure block size of 32
public static String padRightToMod(String text, int mod) {
if (text == null || mod <= 0) {
return text;
}
final int len = text.length();
StringBuilder buf = new StringBuilder(512);
buf.append(text);
for (int i = len; i % mod > 0; i++) {
buf.append(" ");
}
String rs = buf.toString();
System.out.println(rs.length());
return rs;
}
// Call to the encrypt function
String encText = encryptData("Hello", HEX_KEY);
The result is CC0AC95B5FFD4758DBFA40F909C285F0F86A8F19ED1A12C1BFC098348A2AC683
And with this javascript code
crypto = require('crypto');
function encrypt(data,key) {
var cipher = crypto.createCipher('**aes-128-ecb**', key); //create aes cipher
var encrypted = cipher.update(data,'utf8', 'hex'); //output as hex
return encrypted;
}
function padRightTo32(str) // ensure block size of 32
{
len=str.length;
for(i=len; i%32>0; i++){
str=str +" ";
}
return str;
}
// call to encryption function
hexkey="3b6ce332ca3b6519eac769710f41ca5c"
encStr=encrypt(padRightTo32("Hello"),hexKey);
console.log(encStr);
The result is
1B928CF3C18D53BA5138DD1484D181939FD2B7BB2A17AE6A79664488B5C12652
==== Update ======
I tried https://github.com/Snack-X/rijndael-js implementaiton form github with this code
const Rijndael = require("./node_modules/node-rijndael-master");
function padRightTo32(str)
{
len=str.length;
for(i=len; i%32>0; i++){
str=str +" ";
}
console.log(str);
console.log(str.length);
return str;
}
let key = "3b6ce332ca3b6519eac769710f41ca5c";
let original = padRightTo32("Hello");
let cipher = new Rijndael(key, "ecb");
let ciphertext = cipher.encrypt(original, 128);
console.log(ciphertext.toString("hex"));
I get this result e97282fb5838a9c78e6df1f1b4aad108aa010418ec573d74b9c991f4e897e752 but not the encrypted text that Iget from in java. Trying the 256 block size doesn't help either.
what I'm missing that is resulting in a different output?

Concerning your key you have to convert your hex-string into binary data using a buffer (see e.g. Encrypt binary data with aes-ecb on node.js).
Moreover, you have to use the method crypto.createCipheriv to instantiate the cipher (see e.g. https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options).
The currently used (deprecated) method crypto.creataCipher expects a password and generates the key from the password (see e.g. https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options).
The following code
crypto = require('crypto');
function encrypt(data,key) {
var cipher = crypto.createCipheriv('aes-128-ecb', key,''); //create aes-128 cipher
var encrypted = cipher.update(data,'utf8', 'hex'); //output as hex
return encrypted;
}
function padRightTo32(str) { // ensure block size of 32
len=str.length;
for(i=len; i%32>0; i++) {
str=str +" ";
}
return str;
}
// call to encryption function
var hexKey = new Buffer('3b6ce332ca3b6519eac769710f41ca5c', 'hex'); // 16 Byte-key
encStr=encrypt(padRightTo32("Hello"),hexKey);
console.log(encStr);
has the output
cc0ac95b5ffd4758dbfa40f909c285f0f86a8f19ed1a12c1bfc098348a2ac683
which is equal to the output of the Java code.
In Java the length of the key defines the used AES-variant, e.g. if you choose a 16-Byte key AES-128 is used, if a 32-Byte key is chosen, AES-256 is used.
In the nodejs-code you have to explicitly specifiy the AES-variant, i.e. aes-128-ecb for a 16-Byte key, and aes-256-ecb for a 32-Byte key etc.
As already mentioned in the comments ECB in't a secure mode (see e.g. https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption).
I don't know if there is really a difference between Rijndael/ECB/NoPadding and AES/ECB/NoPadding concerning the cipher-instantiation in Java. In my testcases at least the results are identical. Thus, for the nodejs-code the selection of aes-128-ecb (for 16-Byte key) or aes-256-ecb (for 32-Byte keys) should work.

Related

Convert function to javascript, calling from c++ to node.js server - encryption

I'm trying to encrypt a string using the juce framework generatekeypair, this create a RSA keypair and splits it into 2 parts, (and puts it in hex format I think).
This is the working code I'm using in c++:
const juce::String encryptString (const juce::String& str)
{
juce::RSAKey private_key2 ("thisistheprivatekeypart1,thisistheprivatekeypart2");
auto utf8 = str.toUTF8();
auto* utf8Address = utf8.getAddress();
juce::MemoryBlock plainMemoryBlock(utf8Address, utf8.sizeInBytes());
juce::BigInteger sourceInteger;
sourceInteger.loadFromMemoryBlock(plainMemoryBlock);
if (!sourceInteger.isZero())
{
juce::BigInteger encodedInteger(sourceInteger);
private_key2.applyToValue(encodedInteger);
juce::MemoryBlock encodedMemoryBlock = encodedInteger.toMemoryBlock();
return encodedMemoryBlock.toBase64Encoding();
}
return {};
}
Now i'm doing my first steps in node.js, i've successfully setup a server that takes and sends parameters, does anyone know how to write this function in node.js?
On the juce website they have this unconfirmed java snippet but it's not helping me a lot:
public class RSAKey
{
static BigInteger applyToValue (BigInteger value, String key_part1, String key_part2)
{
BigInteger result = BigInteger.ZERO;
BigInteger part1 = new BigInteger (key_part1, 16);
BigInteger part2 = new BigInteger (key_part2, 16);
if (part1.equals (BigInteger.ZERO) || part2.equals (BigInteger.ZERO)
|| value.compareTo (BigInteger.ZERO) <= 0)
return result;
while (! value.equals (BigInteger.ZERO))
{
result = result.multiply (part2);
BigInteger[] div = value.divideAndRemainder (part2);
value = div[0];
result = result.add (div[1].modPow (part1, part2));
}
return result;
}
}

Not able to decrypt the encryptedValue using crypto

I am trying to decrypt a value (encrypted in des) coming from VB.
When I try to decrypt the encryptedValue using crypto in Javascript the output gives me an empty value.
I have attached how the encryption was done in VB.
HOW I AM TRYING TO DECRYPT IN JAVASCRIPT
var CryptoJS = require("crypto-js");
var key = "peekaboo";
var encryptedValue = "50AznWWn4fJI19T392wIv/ZysP/Ke3mB";
encryptedValue = CryptoJS.enc.Base64.parse(encryptedValue);
var data = CryptoJS.DES.decrypt(encryptedValue, key, { iv: "cbauthiv" });
const email = data.toString(CryptoJS.enc.Utf8);
console.log(email, "ORIGINAL TEXT");
THE WAY IT IS ENCRYPTED IN VB
Imports System.Security.Cryptography
Imports System.Text
Imports System.IO
Module Module1
Private Const ENCRYPTIONKEY As String = "peekaboo"
Sub Main()
Dim s As String = Encrypt("ditzymoose#outlook.com")
Dim r As String = Decrypt(s)
Console.ReadLine()
End Sub
Private Function Encrypt(stringToEncrypt As String) As String
Dim rng As New RNGCryptoServiceProvider
Dim byteArray() As Byte = New Byte(8) {}
Dim iv_value As String = "cbauthiv"
Dim key() As Byte = {}
Dim IV() As Byte = System.Text.Encoding.UTF8.GetBytes(Left(iv_value, 8))
key = System.Text.Encoding.UTF8.GetBytes(Left(ENCRYPTIONKEY, 8))
Dim des As New DESCryptoServiceProvider
rng.GetBytes(byteArray)
Dim Salt As String = BitConverter.ToString(byteArray)
Dim SaltedInput As String = Salt & "~" & stringToEncrypt
Dim inputByteArray() As Byte = Encoding.UTF8.GetBytes(stringToEncrypt)
Dim ms As New MemoryStream
Dim cs As New CryptoStream(ms, des.CreateEncryptor(key, IV), CryptoStreamMode.Write)
cs.Write(inputByteArray, 0, inputByteArray.Length)
cs.FlushFinalBlock()
Return Convert.ToBase64String(ms.ToArray())
End Function
End Module
The key and IV must be passed as WordArray. For the conversion the Utf8-Encoder has to be used, here.
Also, the ciphertext must be passed as a CipherParams object or alternatively Base64 encoded (which is then implicitly converted to a CipherParams object), here.
With these changes the ciphertext of the VB code can be successfully decrypted using the CryptoJS code:
var key = CryptoJS.enc.Utf8.parse("peekaboo");
var iv = CryptoJS.enc.Utf8.parse("cbauthiv");
var encryptedValue = "50AznWWn4fJI19T392wIv/ZysP/Ke3mB";
var data = CryptoJS.DES.decrypt(encryptedValue, key, {iv: iv});
var email = data.toString(CryptoJS.enc.Utf8);
console.log(email, "ORIGINAL TEXT");
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Please note that DES is insecure (here) and was replaced by AES almost 20 years ago. Also insecure is a static IV. Instead, a random IV should be generated for each encryption.
Furthermore a password should not be used as key. If a password is to be used, the key should be derived from the password using a reliable key derivation function such as PBKDF2.

TripleDES Java Encryprion to Javascript Decryption

I am using Java to encrypt a text payload with Triple DES. First I create an ephemeral key that I will use for encrypting the payload:
private byte[] createEphemeralKey() throws Exception {
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
keygen.init(168);
return keygen.generateKey().getEncoded();
}
Then I encrypt my payload with said key:
private String encryptTripleDES(byte[] ephemeralKey, String payload) throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(ephemeralKey, "DESede"));
byte[] plainTextBytes = payload.getBytes();
byte[] cipherText = cipher.doFinal(plainTextBytes);
return Base64.getEncoder().encodeToString(cipherText);
}
Also need a padding function to ensure the data length is divisable by 8:
private String adjustPadding(String input, int blockSize) {
int len = input.length() % blockSize;
int paddingLength = (len == 0) ? 0 : (blockSize - len);
while (paddingLength > 0) {
input += "F";
paddingLength--;
}
return input;
}
And here is my process end to end:
String data = "Marnus"
byte[] = ephemeralKey = createEphemeralKey();
String adjustedData = adjustPadding (data,8);
String encryptedPayload = encryptTripleDES(ephemeralKey, adjustedData);
String encodedKey = Base64.getEncoder().encodeToString(ephemeralKey)
So I take the 2 variables encryptedPayload and encodedKey, that are both Base64 encoded string, and send it off via HTTP to node express app.
In the Javascript side of things, I use node-forge - Here is the part of my express app that does the decryption:
let nodeBuffer = Buffer.from(data, 'base64')
let input = forge.util.createBuffer(nodeBuffer.toString('binary'))
// 3DES key and IV sizes
let keySize = 24;
let ivSize = 8;
let derivedBytes = forge.pbe.opensslDeriveBytes(ephemeralKey, null, keySize + ivSize);
let buffer = forge.util.createBuffer(derivedBytes);
let key = buffer.getBytes(keySize)
let iv = buffer.getBytes(ivSize)
let decipher = forge.cipher.createDecipher('3DES-ECB', key)
decipher.start({iv: iv})
decipher.update(input)
console.log('decipher result', decipher.finish())
let decryptedResult = decipher.output.data;
Here is an Triples DES example in the node-forge docs:
A few notes:
I create a node-forge buffer from a regular buffer since I don't have a input file like the examples gives. Here is how the docs states one should create one buffer from the other:
*I use base64 as that is what I used in the java side to encode the data that was sent.
Then, I dont have a salt so I left the 2'nd param null in opensslDeriveBytes as specified in the docs I should do.
Thirdly, I am also not sure if my keysize of 24 is correct?
My results
So doing an end to end test yields the following:
In my Java app, the test data was "Marnus", the encryptedPayload was ez+RweSAd+4= and the encodedKey was vCD9mBnWHPEBiQ0BGv7gc6GUCOoBgLCu.
Then in my javascript code data was obviously ez+RweSAd+4=(encryptedPayload) and the ephemeralKey was vCD9mBnWHPEBiQ0BGv7gc6GUCOoBgLCu(encodedKey).
After the decryption ran, the value of decryptedResult was ©ýÕ?µ{', which is obviously just garbage since it was not encoded yet, but I cant figure out which encoding to use?
I tried using forge.util.encode64(decipher.output.data), but that just gave me qf3VP7UYeyc=, which is not right.
For what it's worth, here is the type that decipher.output
With a lot more tweaking and testing different options, I got it working - and the good news is I managed to get it all working with the built in crypto library in nodejs (v12.18.4).
First things first, the JAVA side just needs a change to the key size (from 168 to 112), the rest remains the same - see below example as one single method (should be split up in final implementation of course for testability and usability):
//Some data:
String payload = "{\"data\":\"somedata\"}";
// Create Key
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
keygen.init(112);
byte[] ephemeralKey = keygen.generateKey().getEncoded();
// Adjust the data, see adjustPadding method in the question for details.
String data = adjustPadding (payload,8);
// Wil now be "{"data":"somedata"}FFFFF", can just chop off extra in JS if need be. When sending JSON one knows the end of the object will always be "}"
// Do Encrypt
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(ephemeralKey, "DESede"));
byte[] plainTextBytes = data.getBytes();
byte[] cipherText = cipher.doFinal(plainTextBytes);
String encryptedPayload = Base64.getEncoder().encodeToString(cipherText);
//Lastly, Base64 the key so you can transport it too
String encodedKey = Base64.getEncoder().encodeToString(ephemeralKey)
on the Javascript side of things we keep it simple:
// I'm using TS, so change the import if you do plain JS
import crypto = require('crypto')
//need bytes from the base64 payload
let buff = Buffer.from(ephemeralKey, 'base64')
const decipher = crypto.createDecipheriv('des-ede3', buff, null)
decipher.setAutoPadding(false)
let decrypted = decipher.update(data, 'base64', 'utf8')
decrypted += decipher.final('utf8')
console.log(decrypted)
//{"data":"somedata"}FFFFF"

How can I decrypt text in kotlin, encrypted in nodejs?

I am trying to decrypt a text which is encrypted using nodejs. Simply Saying I am trying to encrypt data from the server-side and decrypt it to the client-side whenever an API gets called.
Here is my code on the server-side.
function encrpt(text) {
const seckey = '12345678123456781234567812345678';
var aesDec = crypto.createCipheriv("aes-256-ecb", seckey , '');
var output = aesDec.update(text, 'utf8', 'hex');
output += aesDec.final('hex');
return output;
}
This is the code in am using to decrypt on client-side it is in Kotlin.
fun String.test() {
val skc = SecretKeySpec("12345678123456781234567812345678".toByteArray(Charsets.UTF_8), "AES")
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, skc)
val result = cipher.doFinal(this.toByteArray())
println(result.toString())
}
But doing so I am getting javax.crypto.BadPaddingException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT in android studio.
You can use like that:
init{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
secretKeySpec = SecretKeySpec(secret.toByteArray(Charsets.UTF_8),"AES")
}
fun decrypt(cipheredText: String?): String? {
val array: ByteArray = Base64.decode(cipheredText, Base64.DEFAULT)
val encrypted: ByteArray = array.copyOfRange(0, array.size)
cipher!!.init(
Cipher.DECRYPT_MODE,
secretKeySpec,
IvParameterSpec(iv.toByteArray())
)
return String(cipher!!.doFinal(encrypted)).trim()
}
Note: iv must be 16 byte (128 bit)

Using CryptoJS to encrypt and Aes Manager to decrypt

I am using CryptoJS to encrypt a message and send it to the server, and decrypting it on the other end in C# using Aes Manager. I get a response back when I send it to the server, but it isn't correct.
Javascript:
this.CryptoJS=require("crypto-js");
var temp=this.CryptoJS.AES.encrypt("hello","yyyyyyyyyyyyyyyyyyyyyyyyyyyyykey",{
keySize:128/8,
iv:this.CryptoJS.enc.Utf8.parse("helllooohelllooo"),
mode:this.CryptoJS.mode.CBC,
padding:this.CryptoJS.pad.ZeroPadding
});
data.text=temp.toString(); // This is how I send it to the server
C#:
byte[] Key = UTF8Encoding.UTF8.GetBytes("yyyyyyyyyyyyyyyyyyyyyyyyyyyyykey");
byte[] toBytes = UTF8Encoding.UTF8.GetBytes("helllooohelllooo");
AesManaged aes = new AesManaged();
aes.Key = Key;
aes.IV = toBytes;
aes.Padding = PaddingMode.Zeros;
aes.Mode = CipherMode.CBC;
aes.KeySize = 128;
aes.BlockSize = 128;
byte[] bytes = Convert.FromBase64String(data.text);
UTF8Encoding utf8 = new UTF8Encoding();
using (ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
{
MemoryStream MS = new MemoryStream(bytes);
CryptoStream CS = new CryptoStream(MS, decryptor, CryptoStreamMode.Write);
CS.Write(bytes, 0, bytes.Length);
CS.FlushFinalBlock();
MS.Position = 0;
bytes = new byte[MS.Length];
MS.Read(bytes, 0, bytes.Length);
Plaintext = utf8.GetString(bytes);
var temp = 5;
}
This is what I get as a result from the Plaintext variable: t�k�\a``\u007f������\f^,F~\u0017�\u001fp��#5�\u007f\\
You should explicitly pass the key, plaintext and IV as binary data rather than strings:
let iv = CryptoJS.enc.Utf8.parse("helllooohelllooo");
let pt = CryptoJS.enc.Utf8.parse("hello");
let key = CryptoJS.enc.Utf8.parse("yyyyyyyyyyyyyyyyyyyyyyyyyyyyykey");
Then use in the code like so:
CryptoJS.AES.encrypt(pt, key, ...);
Note that your use of zero padding, fixed IV, and no HMAC or AEAD mode makes the code you have completely insecure. You definitely should not use it. Consult this GitHub repository for examples of secure encryption between JavaScript and C#.
I was able to fix my problem i was not converting the original key to utf8 and once i did that it fixed itself
Resource

Categories

Resources