How to replicate same Android encrypt method in React-native? - javascript

I'm creating a react-native application, where I need to encrypt with TripleDES using ECB mode and Pkcs7 padding.
I have an Android Java piece of code that do the encrypt algorithm i want to replicate.
In react-native I added "CryptoJS" from https://github.com/brix/crypto-js and I called CryptoJS.enc.TripleDES.encrypt() method but it returns a different result than expected.
This is Android code i want to reach:
private static byte[] encrypt(String message, String privateKeyMobile) throws Exception {
final MessageDigest md = MessageDigest.getInstance("MD5");
final byte[] digestOfPassword = md.digest(privateKeyMobile.getBytes("utf-8"));
final byte[] keyBytes = new byte[24];
int q;
for (q = 0; q < 24 && q < digestOfPassword.length; q++) {
keyBytes[q] = digestOfPassword[q];
}
if (q < 24) {
for (int u = q; u < 24; u++)
keyBytes[u] = 0;
}
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
final byte[] plainTextBytes = message.getBytes("utf-8");
return cipher.doFinal(plainTextBytes);
}
This one is my react-native code:
encrypt(message, key) {
let md5key = CryptoJS.enc.MD5(key);
var ciphertext = TripleDES.encrypt(message, md5key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return ciphertext;
}
The two functions returns different outputs.
This php code makes same work as Java code:
https://webtools.workontech.com/triple-des-encryption-online
Other online TripleDES tools return the same react-native result.

Related

How to write the equivalent of this Java encryption function in the Node JS

This is the function used to encrypt in java
public static String encryptionFunction(String fieldValue, String pemFileLocation) {
try {
// Read key from file
String strKeyPEM = "";
BufferedReader br = new BufferedReader(new FileReader(pemFileLocation));
String line;
while ((line = br.readLine()) != null) {
strKeyPEM += line + "\n";
}
br.close();
String publicKeyPEM = strKeyPEM;
System.out.println(publicKeyPEM);
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "").replaceAll("\\s", "");;
byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
// byte[] encoded = Base64.decode(publicKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = (PublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherData = cipher.doFinal(fieldValue.getBytes());
if (cipherData == null) {
return null;
}
int len = cipherData.length;
String str = "";
for (int i = 0; i < len; i++) {
if ((cipherData[i] & 0xFF) < 16) {
str = str + "0" + java.lang.Integer.toHexString(cipherData[i] & 0xFF);
} else {
str = str + java.lang.Integer.toHexString(cipherData[i] & 0xFF);
}
}
return str.trim();
} catch (Exception e) {
System.out.println("oops2");
System.out.println(e);
}
return null;
}
I want the equivalent of this in javascript/Nodejs, i tried this:
import * as NodeRSA from 'node-rsa';
private encryptionFunction(fieldValue: string , pemkey: string) : string {
const rsa = new NodeRSA(pemkey);
const encrypted= rsa.encrypt(fieldValue , 'hex')
return encrypted
}
But the output size of both functions is the same, but apparently the encryption type is wrong.
Node-RSA applies OAEP (here) as padding by default, so the PKCS#1 v1.5 padding used in the Java code must be explicitly specified. This has to be added after key import and before encryption:
rsa.setOptions({ encryptionScheme: 'pkcs1' });
Alternatively, the padding can be specified directly during key import:
const rsa = new NodeRSA(pemkey, { encryptionScheme: 'pkcs1' });
With this change, both codes are functionally identical.
Regarding testing: Keep in mind that RSA encryption is not deterministic, i.e. given the same input (key, plaintext), each encryption provides a different ciphertext. Therefore, the ciphertexts of both (functionally identical) codes will be different even if the input is identical. So this is not a bug, but the expected behavior.
How then can the equivalence of both codes be proved? E.g. by decrypting both ciphertexts with the same code/tool.

Why AEC encryption in .NET yields different result than JavaScript?

I'm going crazy about this one. Spend whole day and still can't understand what is going on. I'm using AES256CBC encryption both in .Net and JavaScript. For some reason I got different results, despite that I'm using same key an iv. My codes are:
JavaScript:
function convertStringToArrayBuffer(str) {
var length = str.length;
var bytes = new Uint8Array(length);
for(var i = 0; i < length; i++) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
}
var keyB64 ="sy/d1Ddy/9K3p8x6pWMq2P8Qw2ftUjkkrAA7xFC7aK8=";
var viB64 = "t8eI2F+QmlUBWZJVIlTX6Q==";
var dataToEnc = "Test123!"
let dataInBytes = convertStringToArrayBuffer(dataToEnc);
let key = window.atob(keyB64);
let iv = window.atob(viB64);
console.log(key);
console.log(iv);
window.crypto.subtle.importKey("raw", convertStringToArrayBuffer(key).buffer, {name: "AES-CBC", length: 256}, false, ["encrypt"]).then(function(key){
console.log(key);
window.crypto.subtle.encrypt({name: "AES-CBC", iv: convertStringToArrayBuffer(iv).buffer}, key, dataInBytes.buffer).then(function(encrypted){
console.log(encrypted);
});
});
This one produces
.Net:
public static void Test()
{
var dataToEnc = "Test123!";
var keyB64 = "sy/d1Ddy/9K3p8x6pWMq2P8Qw2ftUjkkrAA7xFC7aK8=";
var viB64 = "t8eI2F+QmlUBWZJVIlTX6Q==";
var key = Convert.FromBase64String(keyB64);
var iv = Convert.FromBase64String(viB64);
var data = Encoding.UTF8.GetBytes(dataToEnc);
byte[] encrypted = null;
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = key;
aesAlg.IV = iv;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(data);
}
encrypted = msEncrypt.ToArray();
}
}
}
}
This one produces
I belive it is something trivial, yet I can't find this. I appreciate any hint here.
If anyone else is having this issue #Topaco's comment is right way to go, I'm pasting it below
The bug is in the C# code. You have to use swEncrypt.Write(dataToEnc) instead of swEncrypt.Write(data). The overload you are currently using implicitly executes data.ToString()

How to implement java MessageDigest with CryptoJS

JAVA CODE:
byte[] n;
byte[] m;
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
digest.update(n);
byte[] hashed = digest.digest(m);
StringBuilder hexValue = new StringBuilder();
for (byte b : hashed) {
int val = ((int) b) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
System.out.println(hexValue.toString());
How to implement the above code with CryptoJS ?
Thanks!
The following codeļ¼š
var n; // n="abc"
var m; // m="123"
var sha1 = CryptoJS.algo.SHA1.create();
sha1.update(n);
var hashed = sha1.finalize(m);
console.log(hashed.toString());

AES Encryption with C# Decryption with crypto-js

I'm triying to Encrypt string with C# and decrypt it using Angular crypto-js library but it's giving me different output.
I tried different c# aes encryption implementations but crypto-js library can't decrypt the encrypted data in c#. Thank you for any help.
Here is my code
Program.cs
static void Main()
{
var r = EncryptString("exampleString", "examplePassword");
Console.Write(r);
}
public static string EncryptString(string plainText, string passPhrase)
{
if (string.IsNullOrEmpty(plainText))
{
return "";
}
// generate salt
byte[] key, iv;
var salt = new byte[8];
var rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(salt);
DeriveKeyAndIv(passPhrase, salt, out key, out iv);
// encrypt bytes
var encryptedBytes = EncryptStringToBytesAes(plainText, key, iv);
// add salt as first 8 bytes
var encryptedBytesWithSalt = new byte[salt.Length + encryptedBytes.Length + 8];
Buffer.BlockCopy(Encoding.ASCII.GetBytes("Salted__"), 0, encryptedBytesWithSalt, 0, 8);
Buffer.BlockCopy(salt, 0, encryptedBytesWithSalt, 8, salt.Length);
Buffer.BlockCopy(encryptedBytes, 0, encryptedBytesWithSalt, salt.Length + 8, encryptedBytes.Length);
// base64 encode
return Convert.ToBase64String(encryptedBytesWithSalt);
}
private static void DeriveKeyAndIv(string passPhrase, byte[] salt, out byte[] key, out byte[] iv)
{
// generate key and iv
var concatenatedHashes = new List<byte>(48);
var password = Encoding.UTF8.GetBytes(passPhrase);
var currentHash = new byte[0];
var md5 = MD5.Create();
bool enoughBytesForKey = false;
// See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM
while (!enoughBytesForKey)
{
var preHashLength = currentHash.Length + password.Length + salt.Length;
var preHash = new byte[preHashLength];
Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length);
Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length);
Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length);
currentHash = md5.ComputeHash(preHash);
concatenatedHashes.AddRange(currentHash);
if (concatenatedHashes.Count >= 48)
enoughBytesForKey = true;
}
key = new byte[32];
iv = new byte[16];
concatenatedHashes.CopyTo(0, key, 0, 32);
concatenatedHashes.CopyTo(32, iv, 0, 16);
md5.Clear();
}
static byte[] EncryptStringToBytesAes(string plainText, byte[] key, byte[] iv)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException("iv");
// Declare the stream used to encrypt to an in memory
// array of bytes.
MemoryStream msEncrypt;
// Declare the RijndaelManaged object
// used to encrypt the data.
RijndaelManaged aesAlg = null;
try
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, KeySize = 256, BlockSize = 128, Key = key, IV = iv };
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
msEncrypt = new MemoryStream();
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
swEncrypt.Flush();
swEncrypt.Close();
}
}
}
finally
{
// Clear the RijndaelManaged object.
aesAlg?.Clear();
}
// Return the encrypted bytes from the memory stream.
return msEncrypt.ToArray();
}
Simply decrypting it using crypto-js
let CryptoJS = require('crypto-js');
let r = CryptoJS.AES.decrypt('exampleString', 'examplePassword').toString();
The example code is attempting to decrypt the original unencrypted string, which looks to be a mistake perhaps created when trying to simplify the example code for posting the question? Either way the steps required are not too difficult, but the toString() call needs to be replaced.
var data = "U2FsdGVkX1/Zvh/5BnLfUgfbg5ROSD7Aohumr9asPM8="; // Output from C#
let r2 = CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(data, 'examplePassword'));
console.log(r2);

AES ECB No padding

I am trying to implement AES/ECB/NoPadding with cryptojs.
On the Java side I have this:
public static String encrypt(String input, String key) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return DatatypeConverter.printHexBinary(cipher.doFinal(padToMultipleOf32(input.getBytes())));
}
public static byte[] padToMultipleOf32(final byte[] bytes) {
int n16 = ((bytes.length + 31) / 32);
int paddedLen = n16 * 32;
byte[] result = new byte[paddedLen];
for (int i = 0; i < bytes.length; i++) {
result[i] = bytes[i];
}
for (int i = bytes.length; i < paddedLen; i++) {
result[i] = 0x00;
}
System.out.println(new String(result).length());
return result;
}
Running Test.encrypt("test", "4g2ef21zmmmhe678")
Gives me: C24F53DDEAD357510A27AA283C74BBF4638B3F81F8EB44652D424D7C32897525
How would I do the same in cryptojs, what I have currently doesnt work:
var pwd = CryptoJS.AES.encrypt("test", "4g2ef21zmmmhe678", {
mode : CryptoJS.mode.ECB,
padding : CryptoJS.pad.NoPadding
});
expect(pwd.ciphertext.toString(CryptoJS.enc.Base64)).toEqual("C24F53DDEAD357510A27AA283C74BBF4638B3F81F8EB44652D424D7C32897525");
Please help
The documentation of CryptoJS says that when passing a plain passphrase (i.e. a String) to encrypt () , it will automatically generate a 256 bit key (Java implements the 128 bit version) using that password as the seed.
To prevent that you can use this function to directly convert your passphrase to a key:
var key = CryptoJS.enc.Utf8.parse("password");
Possibly you will also have to synchronize the encodings of the ciphertexts. See here for a detailed example on how to perform interoperable encryption with Java and CryptoJS.

Categories

Resources