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

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)

Related

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"

AES encryption in node resulting is different output compared to java

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.

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

Encrypt string with Blowfish in NodeJS

I need to encrypt a string but I almost get the output I desire, I read online that it has something to do with padding and iv_vector at the end to complete for the remaining 8 bytes to be same length as txtToEncrypt.
I'm using this library https://github.com/agorlov/javascript-blowfish
// function in Java that I need
// javax.crypto.Cipher.getInstance("Blowfish/CBC/NoPadding").doFinal("spamshog")
var iv_vector = "2278dc9wf_178703";
var txtToEncrypt = "spamshog";
var bf = new Blowfish("spamshog", "cbc");
var encrypted = bf.encrypt(txtToEncrypt, iv_vector);
console.log(bf.base64Encode(encrypted));
Actual output: /z9/n0FzBJQ=
What I need: /z9/n0FzBJRGS6nPXso5TQ==
If anyone has any clue please let me know. I searched all over Google all day.
Finally, here is how to encrypt a string in NodeJS with Blowfish
// Module crypto already included in NodeJS
var crypto = require('crypto');
var iv = "spamshog";
var key = "spamshog";
var text = "2278dc9wf_178703";
var decipher = crypto.createCipheriv('bf-cbc', key, iv);
decipher.setAutoPadding(false);
var encrypted = decipher.update(text, 'utf-8', "base64");
encrypted += decipher.final('base64');
console.log(encrypted);
Returns: /z9/n0FzBJRGS6nPXso5TQ==

Node.js Crypto input/output types

I am trying to figure out the Node.js Crypto library and how to use it properly for my situation.
My Goal is:
key in hex string 3132333435363738313233343536373831323334353637383132333435363738
text in hex string 46303030303030303030303030303030
ciphered text in hex string 70ab7387a6a94098510bf0a6d972aabe
I am testing this through a c implementation of AES 256 and through a website at http://www.hanewin.net/encrypt/aes/aes-test.htm
This is what I have to far, it's not working the way I would expect it to work. My best guess is that the input and output types are incorrect for the cipher function. The only one that works is utf8 if I use hex it fails with a v8 error. Any ideas on what I should convert or change to get it to work.
var keytext = "3132333435363738313233343536373831323334353637383132333435363738";
var key = new Buffer(keytext, 'hex');
var crypto = require("crypto")
var cipher = crypto.createCipher('aes-256-cbc',key,'hex');
var decipher = crypto.createDecipher('aes-256-cbc',key,'hex');
var text = "46303030303030303030303030303030";
var buff = new Buffer(text, 'hex');
console.log(buff)
var crypted = cipher.update(buff,'hex','hex')
The output in crypted in this example is 8cfdcda0a4ea07795945541e4d8c7e35 which is not what I would expect.
Your code is using aes-256-cbc when the website you are deriving test vectors from is using ecb mode. Also, you are calling createCipher, but with ECB you should use createCipheriv with no IV (see nodeJS: can't get crypto module to give me the right AES cipher outcome),
Here is some code that demonstrates this:
var crypto = require("crypto");
var testVector = { plaintext : "46303030303030303030303030303030",
iv : "",
key : "3132333435363738313233343536373831323334353637383132333435363738",
ciphertext : "70ab7387a6a94098510bf0a6d972aabe"};
var key = new Buffer(testVector.key, "hex");
var text = new Buffer(testVector.plaintext, "hex");
var cipher = crypto.createCipheriv("aes-256-ecb", key, testVector.iv);
var crypted = cipher.update(text,'hex','hex');
crypted += cipher.final("hex");
console.log("> " + crypted);
console.log("? " + testVector.ciphertext);
The output of running that code is not exactly what I expect, but the first block of the encrypted output matches your expectation. Probably another parameter that needs to be tweaked.:
$ node test-aes-ecb.js
> 70ab7387a6a94098510bf0a6d972aabeeebbdaed7324ec4bc70d1c0343337233
? 70ab7387a6a94098510bf0a6d972aabe

Categories

Resources