Encryption - Decryption between Java and React Js - javascript

I am unable to create a successful encryption in React Js that can be decrypted in Java.
Below is the code --
import React, { Component } from "react";
import { Row, Col, Typography, Spin } from "antd";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as Crypto from 'crypto-js';
import * as utf8 from 'utf8';
import { Calendar } from "antd";
class Test extends Component {
state = {};
componentDidMount = () => {
debugger;
var keySize = 0;
var ivSize = 16;
var saltSize = 16;
var iterations = 4096;
var pass = 'mysuperstrongpassword';
var passMac = 'mysuperstrongpasswordMac';
let message = 'Gurpreet';
const CryptoJS = require("crypto-js")
var Crypto = new AES()
function AES() {}
AES.prototype.generateKey = function(salt, passPhrase) {
var key = CryptoJS.PBKDF2(passPhrase, CryptoJS.enc.Hex.parse(salt), { keySize: 256/16, iterations: 4096 });
return key;
}
AES.prototype.encrypt = function(password, message) {
var salt = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex)
var iv = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex)
var encrypted = CryptoJS.AES.encrypt(message, this.generateKey(salt, password), { iv: CryptoJS.enc.Hex.parse(iv) })
var base64 = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
return salt + base64.substring(0, base64.length-2) + iv
}
AES.prototype.decrypt = function(password, message) {
var salt = message.substring(0, 32)
var iv = message.substring(message.length-32, message.length)
var cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Base64.parse(message.substring(32, message.length-32) + "==")
});
var decrypted = CryptoJS.AES.decrypt(cipherParams, this.generateKey(salt, password), { iv: CryptoJS.enc.Hex.parse(iv) })
return decrypted.toString(CryptoJS.enc.Utf8)
}
//var rest = btoa(hmac + result);
var salt = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex);
var saltMac = CryptoJS.lib.WordArray.random(20).toString(CryptoJS.enc.Hex)
var iv = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex)
var macKey = AES.prototype.generateKey(saltMac, passMac)
var rest = AES.prototype.encrypt(pass, message)
var hmac = CryptoJS.HmacSHA1(passMac, macKey);
let result = hmac + salt + saltMac + rest;
let result2 = CryptoJS.enc.Utf8.parse(result).toString();
var rest2 = btoa(result2);
console.log(rest2);
console.log(result2);
};
render() {
return (
<div className="test">
href={this.rest}
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({}, dispatch);
}
export default withRouter(
connect(null, mapDispatchToProps, null, { forwardRef: true })(Test)
);
Java
public class EncryptDecryptUtil {
// private static final String CONTENT = "thisneedstobestoredverysecurely";
private static final String PASSPHRASE = "mysuperstrongpassword";
private static final String PASSPHRASEMAC = "mysuperstrongpasswordMac";
private static final int IV_LENGTH = 16;
private static final int AES_KEY_LENGTH = 16;
private static final int MAC_KEY_LENGTH = 16;
private static final int MAC_LENGTH = 20;
private static final int SALT_LENGTH = 16;
private static final int SALT_MAC_LENGTH = 16;
private static final int ITERATION_COUNT = 4096;
private static final String AES = "AES";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String SECRET_KEY_ALGORITHM = "PBKDF2WithHmacSHA1";
private static final String MAC_ALGORITHM = "HmacSHA1";
Cipher cipher = null;
byte[] iv = new byte[IV_LENGTH];
byte[] secretBytes = null;
byte[] secretBytesMac = null;
public String encrypt(String plainText) throws Exception {
cipher = Cipher.getInstance(CIPHER_ALGORITHM);
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_ALGORITHM);
SecureRandom sr = new SecureRandom();
byte[] salt = new byte[SALT_LENGTH];
sr.nextBytes(salt);
SecretKey secretKey = factory
.generateSecret(new PBEKeySpec(PASSPHRASE.toCharArray(), salt, ITERATION_COUNT, 256));
secretBytes = secretKey.getEncoded();
byte[] saltMac = new byte[SALT_MAC_LENGTH];
sr.nextBytes(saltMac);
SecretKey secretKeyMac = factory
.generateSecret(new PBEKeySpec(PASSPHRASEMAC.toCharArray(), saltMac, ITERATION_COUNT, 256));
secretBytesMac = secretKeyMac.getEncoded();
sr = new SecureRandom();
sr.nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secretBytes, 0, AES_KEY_LENGTH, AES),
new IvParameterSpec(iv));
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
byte[] result = concatArrays(iv, concatArrays(saltMac, (concatArrays(salt, encrypted))));
byte[] macResult = getMAC(secretBytesMac, result);
result = concatArrays(macResult, result);
return Base64.getEncoder().encodeToString(result);
}
public String decrypt(String cipherText) throws Exception {
byte[] result = Base64.getDecoder().decode(cipherText);
byte[] macResult = new byte[MAC_LENGTH];
byte[] salt = new byte[SALT_LENGTH];
byte[] saltMac = new byte[SALT_MAC_LENGTH];
cipher = Cipher.getInstance(CIPHER_ALGORITHM);
System.arraycopy(result, 0, macResult, 0, MAC_LENGTH);
System.arraycopy(result, MAC_LENGTH, iv, 0, IV_LENGTH);
System.arraycopy(result, MAC_LENGTH + IV_LENGTH, saltMac, 0, SALT_MAC_LENGTH);
System.arraycopy(result, MAC_LENGTH + IV_LENGTH + SALT_MAC_LENGTH, salt, 0, SALT_LENGTH);
byte[] encrypted = new byte[result.length - (MAC_LENGTH + IV_LENGTH + SALT_MAC_LENGTH + SALT_LENGTH)];
System.arraycopy(result, MAC_LENGTH + IV_LENGTH + SALT_MAC_LENGTH + SALT_LENGTH, encrypted, 0,
encrypted.length);
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_ALGORITHM);
SecretKey secretKeyMac = factory
.generateSecret(new PBEKeySpec(PASSPHRASEMAC.toCharArray(), saltMac, ITERATION_COUNT, 256));
secretBytesMac = secretKeyMac.getEncoded();
if (!MessageDigest.isEqual(
getMAC(secretBytesMac, concatArrays(iv, concatArrays(saltMac, (concatArrays(salt, encrypted))))),
macResult)) {
System.out.println("Invalid MAC");
}
SecretKey secretKey = factory
.generateSecret(new PBEKeySpec(PASSPHRASE.toCharArray(), salt, ITERATION_COUNT, 256));
secretBytes = secretKey.getEncoded();
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretBytes, 0, AES_KEY_LENGTH, AES),
new IvParameterSpec(iv));
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted, "UTF-8");
}
private static byte[] getDigest(byte[] mac) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA1");
return digest.digest(mac);
}
private static byte[] getMAC(byte[] secretBytes, byte[] data) throws Exception {
Mac mac = Mac.getInstance(MAC_ALGORITHM);
mac.init(new SecretKeySpec(secretBytes, 0, MAC_KEY_LENGTH, MAC_ALGORITHM));
return mac.doFinal(data);
}
private static byte[] concatArrays(byte[] first, byte[] second) {
byte[] ret = new byte[first.length + second.length];
System.arraycopy(first, 0, ret, 0, first.length);
System.arraycopy(second, 0, ret, first.length, second.length);
return ret;
}
}
I cant change anything at Java Backend because for the iOS team they are able to encrypt-decrypt using this util and are sending us the correct strings.
But the issue is at Web end #React Js end - code above where we are unable to match the encryption technique same as Java.

Related

Encrypt in Javascript and Decrypt in Java For Zip File

I want to encrypt local zip file in javascript, then decrypt it in java.
I used code from this link https://www.devglan.com/corejava/aes-encryption-javascript-and-decryption-in-java
encrypt in javascript
var AesUtil = function (keySize, iterationCount) {
this.keySize = keySize / 32;
this.iterationCount = iterationCount;
};
AesUtil.prototype.generateKey = function (salt, passPhrase) {
var key = CryptoJS.PBKDF2(
passPhrase,
CryptoJS.enc.Hex.parse(salt),
{keySize: this.keySize, iterations: this.iterationCount});
return key;
};
AesUtil.prototype.encrypt = function (salt, iv, passPhrase, plainText) {
var key = this.generateKey(salt, passPhrase);
var encrypted = CryptoJS.AES.encrypt(
plainText,
key,
{iv: CryptoJS.enc.Hex.parse(iv)});
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
};
AesUtil.prototype.decrypt = function (salt, iv, passPhrase, cipherText) {
var key = this.generateKey(salt, passPhrase);
var cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Base64.parse(cipherText)
});
var decrypted = CryptoJS.AES.decrypt(
cipherParams,
key,
{iv: CryptoJS.enc.Hex.parse(iv)});
return decrypted.toString(CryptoJS.enc.Utf8);
};
var CryptoJS = require('crypto-js'),
fs = require('fs');
var data = fs.readFileSync("C:\\<my_path>\\scripts.zip");
var passPhrase = "123456789123456789";
var iv = "a145525c53eafb0258999612b13d9d3e"; //CryptoJS.lib.WordArray.random(128 / 8).toString(CryptoJS.enc.Hex);
var salt = "ca70e17a698096cfb42047926713dd62";// CryptoJS.lib.WordArray.random(128 / 8).toString(CryptoJS.enc.Hex);
var aesUtil = new AesUtil(128, 1000);
var ciphertext = aesUtil.encrypt(salt, iv, passPhrase, data.toString());
fs.writeFileSync('C:/Output.encrypted', ciphertext);
and decryption in java is like this:
AesUtil.java
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
public class AesUtil {
private final int keySize;
private final int iterationCount;
private final Cipher cipher;
public AesUtil(int keySize, int iterationCount) {
this.keySize = keySize;
this.iterationCount = iterationCount;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw fail(e);
}
}
public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
return new String(decrypted, "UTF-8");
}
catch (UnsupportedEncodingException e) {
return null;
}catch (Exception e){
return null;
}
}
public byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
try {
cipher.init(encryptMode, key, new IvParameterSpec(hex(iv)));
return cipher.doFinal(bytes);
}
catch (InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| BadPaddingException e) {
return null;
}
}
public SecretKey generateKey(String salt, String passphrase) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return key;
}
catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
return null;
}
}
public static byte[] base64(String str) {
return Base64.decodeBase64(str);
}
public static byte[] hex(String str) {
try {
return Hex.decodeHex(str.toCharArray());
}
catch (DecoderException e) {
throw new IllegalStateException(e);
}
}
private IllegalStateException fail(Exception e) {
return null;
}
}
Main method:
private static void mainScan(String[] args) {
try {
String keyString = "123456789123456789";
int keySize = 128;
int iterationCount = 1000;
String iv = "a145525c53eafb0258999612b13d9d3e";
String salt = "ca70e17a698096cfb42047926713dd62";
AesUtil aesUtil = new AesUtil(keySize, iterationCount);
String encryptedPath = "C:/Output.encrypted";
String decryptedPath = "C:/Output.zip";
String fileString = new String(Files.readAllBytes(Paths.get(encryptedPath)));
String decryptedText = aesUtil.decrypt(salt, iv, keyString, fileString);
FileUtils.writeStringToFile(new File(decryptedPath), decryptedText);
} catch (IOException e) {
e.printStackTrace();
}
}
But still, after decryption, I got a zip file that is bigger size from the original zip and isn't a valid zip file.
Note: This code worked when the text was only String and not getting the string from/to file
Treating binary content like Zip files as if they were strings is usually a big mistake in most languages. On the Javascript side, CryptoJS expects arbitrary bytes sequences to be supplied as CryptoJS.lib.WordArray arguments.
So, instead of
var ciphertext = aesUtil.encrypt(salt, iv, passPhrase, data.toString());
you should have
var ciphertext = aesUtil.encrypt(salt, iv, passPhrase, CryptoJS.lib.WordArray.create(data));
On the Java side, change the function decrypt to return a byte[].
public byte[] decrypt(String salt, String iv, String passphrase, String ciphertext) {
SecretKey key = generateKey(salt, passphrase);
byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
return decrypted;
}
And in main, change the code to look something like
String fileString = new String(Files.readAllBytes(Paths.get(encryptedPath)));
byte [] decryptedText = aesUtil.decrypt(salt, iv, keyString, fileString);
Files.write(Paths.get(decryptedPath), decryptedText);

Encrypted in C#. Need decryption in NodeJS

I barely have any knowledge on c#. I have a scenario where I have encryption and decryption in c# as follows. But, I want to implement same decryption in NodeJS. Can anyone help me in figuring this out.
Here's the salt and secret keys
private static byte[] _salt = Encoding.ASCII.GetBytes("SampleSaltHere");
private string secret = "SecretHere";
Encryption Part
public string Encrypt(string plainText)
{
string outStr = null;
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(secret, _salt);
RijndaelManaged aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
}
outStr = Convert.ToBase64String(msEncrypt.ToArray());
}
return outStr;
}
Decryption Part which I want to implement in NodeJS.
public string Decrypt(string cipherText)
{
string plaintext = null;
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(secret, _salt);
byte[] bytes = Convert.FromBase64String(cipherText);
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
RijndaelManaged aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = ReadByteArray(msDecrypt);
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
plaintext = srDecrypt.ReadToEnd();
}
}
return plaintext;
}
private static byte[] ReadByteArray(Stream s)
{
byte[] rawLength = new byte[sizeof(int)];
byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
return buffer;
}
Any help is highly appreciated. Thank you.

Encoding in JavaScript

I'm very new in programming with Javascript and stuck in encoding my data.
I have done this as per need in my Android App, but could not able to do the same in JavaScript for my web portal.
The code which I'm using in Android App:
public void encryptdata(byte[] data) {
Encrypter encrypter = new Encrypter();
HashGenerator hashGenerator = new HashGenerator();
try {
byte[] e = encrypter.generateSessionKey();
byte[] encryptedData = encrypter.encryptUsingSessionKey(e, data);
byte[] hmac = hashGenerator.generateSha256Hash(data);
byte[] encryptedHmacBytes = encrypter.encryptUsingSessionKey(e, hmac);
this.encodedSessionKey = encodeBase64(e);
this.encodedHmac = encodeBase64(encryptedHmacBytes);
this.encodedData = encodeBase64(encryptedData);
} catch (Exception var6) {
var6.printStackTrace();
throw new RuntimeException(var6);
}
}
Encrypter.java
class Encrypter {
private static final String JCE_PROVIDER = "BC";
private static final int SYMMETRIC_KEY_SIZE = 256;
Encrypter() {
}
public byte[] generateSessionKey() throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator kgen = KeyGenerator.getInstance("AES", "BC");
kgen.init(256);
SecretKey key = kgen.generateKey();
byte[] symmKey = key.getEncoded();
return symmKey;
}
public byte[] encryptUsingSessionKey(byte[] skey, byte[] data) throws InvalidCipherTextException {
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new AESEngine(), new PKCS7Padding());
cipher.init(true, new KeyParameter(skey));
int outputSize = cipher.getOutputSize(data.length);
byte[] tempOP = new byte[outputSize];
int processLen = cipher.processBytes(data, 0, data.length, tempOP, 0);
int outputLen = cipher.doFinal(tempOP, processLen);
byte[] result = new byte[processLen + outputLen];
System.arraycopy(tempOP, 0, result, 0, result.length);
return result;
}
static {
Security.addProvider(new BouncyCastleProvider());
}
}
HashGenerator.java
class HashGenerator {
public HashGenerator() {
}
public byte[] generateSha256Hash(byte[] message) {
String var2 = "SHA-256";
String var3 = "BC";
byte[] var4 = null;
try {
MessageDigest var7 = MessageDigest.getInstance(var2, var3);
var7.reset();
var4 = var7.digest(message);
} catch (Exception var6) {
var6.printStackTrace();
}
return var4;
}
}
These piece of code encoding the data and giving me HMAC as per my need, but I am not able to do same with JavaScript.
Can anyone give any reference or code on for JavaScript client.
Any help will be appreciated.
Thanks for down-voting the question, may be a not great question, but here I done it myself
this.doEncryption = function (data) {
var key = btoa(this.generateRandomString());
var Encryptionkey = CryptoJS.enc.Base64.parse(key);
var encryptedPid = CryptoJS.AES.encrypt(data,Encryptionkey,{ mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7});
var sha256 = CryptoJS.SHA256(data);
var encryptedHmac = CryptoJS.AES.encrypt(sha256,Encryptionkey,{ mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7});
return {
encryptionKey : key,
encryptedPid : encryptedPid.toString(),
encryptedHmac : encryptedHmac.toString()
};

react-native AES Encryption matching Java Decryption algorithm

The Full code of my Java Encryption/Decryption algorithm:
public class AESEncryptUtil {
private static AESEncryptUtil instance = new AESEncryptUtil();
private String password = "123456";
private Key key;
private Cipher cipher;
public AESEncryptUtil(){
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
key = new SecretKeySpec(enCodeFormat, "AES");
cipher = Cipher.getInstance("AES");
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String content) throws Exception {
byte[] byteContent = content.getBytes("utf-8");
instance.cipher.init(Cipher.ENCRYPT_MODE, instance.key);
byte[] result = instance.cipher.doFinal(byteContent);
return result;
}
public static byte[] decrypt(byte[] content) throws Exception {
instance.cipher.init(Cipher.DECRYPT_MODE, instance.key);
byte[] result = instance.cipher.doFinal(content);
return result;
}
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2),
16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
public static String getNonce() {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 16; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
String content = "test";
System.out.println("content: " + content);
byte[] encryptResult = encrypt(content);
String encryptResultStr = parseByte2HexStr(encryptResult);
System.out.println("encryptResultStr: " + encryptResultStr);
byte[] decryptFrom = parseHexStr2Byte(encryptResultStr);
byte[] decryptResult = decrypt(decryptFrom);
System.out.println("decryptResult: " + new String(decryptResult));
}
}
I've tried many times and many ways to match the Java algorithm, but the result are always different. Which module should I use to do this ? Can anyone help me to deal it ? Thanks a lot !
I found the right way to match two algorithm:
Java part:
public static String encrypt() throws Exception {
try {
String data = "123456";
String key = "1234567812345678";
String iv = "1234567812345678";
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return new sun.misc.BASE64Encoder().encode(encrypted);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String desEncrypt() throws Exception {
String encrypted = encrypt() ;
try
{
String data = encrypted ;
String key = "1234567812345678";
String iv = "1234567812345678";
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
return originalString;
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
React native part:
pre coding: npm install crypto-js
import CryptoJS from 'crypto-js' ;
encryptFun() {
var data = "123456";
var key = CryptoJS.enc.Latin1.parse('1234567812345678');
var iv = CryptoJS.enc.Latin1.parse('1234567812345678');
var encrypted = CryptoJS.AES.encrypt(
data,
key,
{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding
});
console.log('encrypted: ' + encrypted) ;
var decrypted = CryptoJS.AES.decrypt(encrypted,key,{iv:iv,padding:CryptoJS.pad.ZeroPadding});
console.log('decrypted: '+decrypted.toString(CryptoJS.enc.Utf8));
}
the result :
encrypted: aK7+UX24ttBgfTnAndz9aQ==
decrypted: 123456
I hope my code would help someone:)

CryptoJs's decrypt method returns an empty string

I am trying to encrypt/decrypt using AES256 using Java for encryption and CryptoJS for decryption. Encryption is tested in Java is working fine but the decryption method in JavaScript is returning an empty string. Please note in order to test JavaScript I printed out in tmp file the values for data, IV and salt and then hardcoded in JS. (Note: format in file is: data (byte[] base64) , Iv(string base64) and salt(string base64) ).
Here is the code in java:
public byte[] encrypt(String plainText) throws Exception {
//get salt
salt = generateSalt();
byte[] saltBytes = salt.getBytes("UTF-8");
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//encrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
return Base64.encode(encryptedTextBytes);
}
what is wrong with the decryption code in JavaScript below?
// the password that user provides
var userPass = document.getElementById("password").value;
console.log("user pass : " + userPass);
// hash contains 5 bytes
var hashedPass = CryptoJS.SHA1(userPass);
console.log("hashed pass : " + hashedPass.toString(CryptoJS.enc.Base64) + " | array length " + hashedPass.words.length + " | " + typeof(hashedPass));
// use only 4 bytes (128 bits) from the hashed pass
// (same as used in java when encrypting)
/////////////////////////var hashed4bytes = CryptoJS.lib.WordArray.create(hashedPass.words.slice(0,4));
//console.log( "hashed4bytes encoded 64 = " + hashed4bytes.toString(CryptoJS.enc.Base64));
// get the encrypted msg
var encMsg64 = document.getElementById("themessage").innerHTML;
encMsg64 = encMsg64.toString( CryptoJS.enc.Base64);
//var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
var salt =CryptoJS.enc.Base64.parse("EAWnOgxUDuvhWqrSUsugq1umMpI=");
var iv =CryptoJS.enc.Base64.parse("xWpmXNbmbFjmWBUajuWYXQ==");
//var salt = "EAWnOgxUDuvhWqrSUsugq1umMpI=";
//var iv = "xWpmXNbmbFjmWBUajuWYXQ==";
console.log('salt '+ salt );
console.log('iv '+ iv );
var key = CryptoJS.PBKDF2(hashedPass, salt, { keySize: 256/32, iterations: 1000 });
console.log( 'key '+ key);
var decText = '';
var ok = true;
try {
debugger;
var decMsg = CryptoJS.AES.decrypt( encMsg64, key, {
iv:iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
} );
console.log( "decryptedData = " + decMsg );
// convert to UTF8 string
decText = decMsg.toString( CryptoJS.enc.Utf8 );
console.log( "decryptedText = " + decText );
if (decText == '') {
ok = false;
}
}
catch (e) {
//console.log("Error when decrypting: " + e.message)
ok = false;
}
after mafe the changed issue still persists
Here is complete code after the change
JAVA
public class AES256EncryptionServiceBean implements EncryptionService {
private static final Logger LOGGER = LoggerFactory
.getLogger(AES256EncryptionServiceBean.class);
private String salt = null; //get bytes out of UTF-8 for decryption
private static final int PSWDITERATIONS = 1000;//65536;
private static final int KEYSIZE = 256;
private static final String AES_ALGO = "AES";
private static final String SHA1_ALGO = "PBKDF2WithHmacSHA1";
private static final String AES_CBC_PKCS5_TRANSFORM = "AES/CBC/PKCS5Padding";
private byte[] Iv;
/**
* Encrypts the data with AES-256 algorithm Encrypted data will be encoded
* with base64 algorithm and the returned. Initial vector is being used
* during encryption along with CBC encryption mode.
*
* output format: [algo indicator(1char)][Initialization vector()][salt()][encoded data(variable size)]
*/
#Override
public byte[] encrypt(String password, byte[] data) throws PibException {
byte[] encodedData = null;
try {
byte[] encryptedData = encryptCBC256Bits(password, data);
encodedData = Base64.encodeBase64(encryptedData);
/*String finalStr=null;
String algo256 = "2";
String datastr = Base64.encodeBase64String(encryptedData);
String ivstr = new String(Iv);
finalStr = algo256 +ivstr+salt+datastr;
encodedData = finalStr.getBytes();
*/
} catch (Exception e) {
throw ExceptionFactory.createPibException(
MessageCodes.PIB_ENCRYPTION_FAILED, e, LOGGER);
}
return encodedData;
}
/**
* Encrypts the input data with AES CBC transformation using 256 bits (32
* bytes) Key is generated based on the provided password and random salt.
* Salt is the extra bits added to the password to ensure every key is
* unique SHA1 hashing is also participate in key generation.
*
* #throws PibException
*
*/
private byte[] encryptCBC256Bits(String password, byte[] data)
throws PibException {
salt = generateSalt();
byte[] saltBytes = salt.getBytes(StandardCharsets.UTF_8);
byte[] encryptedTextBytes = null;
// Derive the key
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance(SHA1_ALGO);
// Password based key specification
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes,
PSWDITERATIONS, KEYSIZE);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(),
AES_ALGO);
// encrypt the data
Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_TRANSFORM);
// SecureRandom random = new SecureRandom();
// byte[] ivTemp = new byte[16];
// random.nextBytes(ivTemp);
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
Iv = params.getParameterSpec(IvParameterSpec.class).getIV();
encryptedTextBytes = cipher.doFinal(data);
} catch (NoSuchAlgorithmException | InvalidKeySpecException
| NoSuchPaddingException | InvalidKeyException
| InvalidParameterSpecException | IllegalBlockSizeException
| BadPaddingException e) {
throw ExceptionFactory.createPibException(
MessageCodes.PIB_ENCRYPTION_FAILED, e, LOGGER);
}
return encryptedTextBytes;
}
private String generateSalt() {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String s = new String(bytes);
return s;
}
public String getSalt() {
return salt;
}
public byte[] getIv() {
return Iv;
}
}
Javascript
function decryptMsg256() {
// the password that user provides
var userPass = document.getElementById("password").value;
console.log("user pass : " + userPass);
// get the encrypted msg
var encMsg64 = document.getElementById("themessage").innerHTML;
var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
var salt =CryptoJS.enc.Utf8.parse("?E€O5?…°®I^y??O:n");
var iv =CryptoJS.enc.Utf8.parse("S;Ui?¨=ENzI—$");
console.log('salt '+ salt );
console.log('iv '+ iv );
var key = CryptoJS.PBKDF2("password", salt, { keySize: 256/32, iterations: 1000 });
console.log( 'key '+ key);
var decText = '';
var ok = true;
try {
debugger;
var decMsg = CryptoJS.AES.decrypt( encMsg, key, {
iv:iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
} );
console.log( "decryptedData = " + decMsg );
// convert to UTF8 string
decText = decMsg.toString( CryptoJS.enc.Utf8 );
console.log( "decryptedText = " + decText );
if (decText == '') {
ok = false;
}
}
catch (e) {
//console.log("Error when decrypting: " + e.message)
ok = false;
}
I can not understands what is wrong please help
CipherText,Salt and Iv is retrieved as follows:
public void testEncryption_WriteToFile() throws Exception {
byte[] data = IOUtils.toByteArray(this.getClass().getClassLoader()
.getResourceAsStream(SOME_FILE_NAME));
byte[] encryptedData = this.encryptionService.encrypt(PASSWORD, data);
byte[] initial_vector = ((AES256EncryptionServiceBean) encryptionService)
.getIv();
String salt = ((AES256EncryptionServiceBean) encryptionService)
.getSalt();
IOUtils.write(encryptedData, new FileOutputStream(
"C:\\Temp\\data.encrypted"));
/*IOUtils.write(new String(encryptedData), new FileOutputStream(
"C:\\Temp\\data[byte32string].encrypted"));
*/
IOUtils.write(Base64.encodeBase64String(salt.getBytes(StandardCharsets.UTF_8)), new FileOutputStream(
"C:\\Temp\\salt.encrypted"));
/*IOUtils.write(salt.getBytes(StandardCharsets.UTF_8), new FileOutputStream(
"C:\\Temp\\salt.encrypted"));
*/
IOUtils.write(Base64.encodeBase64String(initial_vector), new FileOutputStream(
"C:\\Temp\\iv.encrypted"));
/*IOUtils.write(initial_vector, new FileOutputStream(
"C:\\Temp\\iv.encrypted"));*/
}
CryptoJS.PBKDF2 uses SHA1 by default. So as long as the same password, salt, keysize and iteration count is supplied, it will produce the same key. The problem is that in JavaScript you additionally hash the password with SHA1. Don't do that and pass the password directly into PBKDF2 in the same way you do this in Java.
The second problem is that the ciphertext should be in the native format of CryptoJS when trying to decrypt. Since you get the base 64 encoded ciphertext from Java, you have to decode it as such. Uncomment the line:
var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
and don't do encMsg64 = encMsg64.toString( CryptoJS.enc.Base64); since this will encode the already encoded ciphertext again.
For the updated code, you cannot print your key and salt simply as a string and expect it to work in JavaScript. Those are byte[] for a reason. They contain unprintable characters which will be lost when you try to parse it in JavaScript. You have to encode all the byte[] values that you want to transport from Java to JavaScript as Base64 and then decode them in JavaScript.

Categories

Resources