Java decryption is failing to decrypt a CryptoJS encrypted data - encryption

I am using AES/CBC algorithm. I am open to use any padding mechanism.
Javascript encrypt code:
var text = "test#gmail.com";
var key = CryptoJS.enc.Hex.parse("lzi839AEKDDDtQN63+eTZbhJrLq/cn62oqH23IrqoMY=");
//Trying to generate 16 null bytes, Equivalent to java code : "new IvParameterSpec(new byte[16])". Not sure if this is correct.
var iv = CryptoJS.enc.Hex.parse("00000000000000000000000000000000");
var encrypted = CryptoJS.AES.encrypt(text, key, {iv: iv, mode : CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
console.log("Encrypted String:: "+encrypted.toString());
This produces following output:
Encrypted String:: jcmI/BhSDa8yhWNoFLIF6g==
Following is the Java decryption code:
public String decrypt(String encryptedString, String key) throws SecurityException {
try {
Cipher cipher = getCipher();
cipher.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(Base64.decodeBase64(key),"AES"),
new IvParameterSpec(new byte[16]));
byte[] decryptedText = cipher.doFinal(Base64.decodeBase64(encryptedString), 0, 32);
return new String(decryptedText);
} catch (Exception e) {
throw new SecurityException("Fail to decrypt", e);
}
}
When I call the above java function as :
String encryptedString = "jcmI/BhSDa8yhWNoFLIF6g==";
String key = "lzi839AEKDDDtQN63+eTZbhJrLq/cn62oqH23IrqoMY=";
decrypt(encryptedString, key);
I get below exception message:
**Given final block not properly padded. Such issues can arise if a bad key is used during decryption**
I Referred a similar question : Why can't java decrypt CryptoJS encrypted data?
But this is in ECB mode and it did not help me completely to arrive at my solution.
Please help me to fix this error and decrypt properly at Java end. I am open to change Padding methods. However, I must use AES/CBC mode only.
Update:
Full code for Java Decryption:
public String decrypt(String encryptedString, String key) throws SecurityException {
try {
Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(Base64.decodeBase64(key),"AES"),
new IvParameterSpec(new byte[16]));
byte[] decryptedText = cipher.doFinal(Base64.decodeBase64(encryptedString), 0, 32);
return new String(decryptedText);
} catch (Exception e) {
throw new SecurityException("Fail to decrypt", e);
}
}

Related

Decode(Base64) and Decrypt(AES/CBC/PKCS5PADDING) with CryptoJS - React

I am working on the web application using react as front-end and spring mvc as back-end. I need to store some user information in local storage of the browser. I do not want to store that info in local storage as a plain text. So I thought to go for AES encryption at server side and pushing those data back to JS side. For that I need client side decryption framework. I found crypto-js as very useful for all these things. I am not able to understand where I am lacking at client side to decrypt and decode.
I am explaining my Spring Side Encryption Code first which is absolutely fine:
public class EncryptDecrypt {
private static final String SECRET_KEY_1 = "ssdkF$HUy2A#D%kd";
private static final String SECRET_KEY_2 = "weJiSEvR5yAC5ftB";
private IvParameterSpec ivParameterSpec;
private SecretKeySpec secretKeySpec;
private Cipher cipher;
public EncryptDecrypt() throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException {
ivParameterSpec = new IvParameterSpec(SECRET_KEY_1.getBytes("UTF-8"));
secretKeySpec = new SecretKeySpec(SECRET_KEY_2.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
}
public String encrypt(String toBeEncrypt) throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(toBeEncrypt.getBytes());
return Base64.encodeBase64String(encrypted);
}
}
At the client side, I am not able to decode and decrypt the code with simple things. Here is my client side code:
var CryptoJS = require("crypto-js");
var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";
var key = "weJiSEvR5yAC5ftB";
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = "ssdkF$HUy2A#D%kd";
var crypttext = rawData.substring(16);
console.log(rawData);
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Base64.parse(crypttext) },
key,
{ iv: iv }
);
console.log(plaintextArray);
console.log(CryptoJS.enc.Base64.stringify(plaintextArray));
var decryptedData = JSON.parse(CryptoJS.enc.Base64.stringify(plaintextArray).toString(CryptoJS.enc.Utf8));
console.log(decryptedData);
P.S: I have sent JSON to client side and so that I am parsing it in the end. I am newbie for encryption and decryption. I am really stuck with what my client side code should look a like. Please help.
You shouldn't pass string as key in CryptoJS. In this case it considers this string not as key, but as password. And generate key from password by using PBKDF. Working example below:
var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";
var rawData = CryptoJS.enc.Base64.parse(data);
var key = CryptoJS.enc.Latin1.parse("weJiSEvR5yAC5ftB");
var iv = CryptoJS.enc.Latin1.parse("ssdkF$HUy2A#D%kd");
var plaintextData = CryptoJS.AES.decrypt(
{ ciphertext: rawData },
key,
{ iv: iv });
var plaintext = plaintextData.toString(CryptoJS.enc.Latin1);
console.log(plaintext);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>
BTW, you shouldn't use the same IV every time. In this case you miss the base purpose of IV and CBC mode. Your overall security becomes equal to ECB mode.

Encryption interop between Java (Cipher) & JavaScript (crypto-js)

I've been tasked to fix an interoping encryption algorithm that was working perfectly fine from before but suddenly went haywire for reasons unknown, no one has touched any of the code for both of the languages (Java & JS).
I'm not really well verse with cryptography so I don't know what possible solutions to look for or work with. The task was basically to have this encryption code on Java translated to JavaScript that would both have a resulting Base64 string that was to be decrypted through Java.
The following are the code snippets for the encryption being done with Java & JS and decryption process on Java:
Java Encryption
public static String encryptMsg(String message) {
#SuppressLint("GetInstance") Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes(UTF_CHARSET));
return Base64.encodeToString(cipherText, Base64.DEFAULT);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
e.printStackTrace();
} catch (NullPointerException e) {
//Do nothing, nothing to encrypt
}
return null;
}
JavaScript Encryption
function encryptData(data, key) {
const options = {
mode: Crypto.mode.ECB,
padding: Crypto.pad.Pkcs7
}
const secret = Crypto.enc.Utf8.parse(key)
const encrypted = Crypto.AES.encrypt(data, secret, options)
return encrypted.ciphertext.toString(Crypto.enc.Base64)
}
Java Decryption
public static String decryptMsg(String base64cipherText) {
#SuppressLint("GetInstance") Cipher cipher = null;
try {
cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secret);
String decryptString = new String(cipher.doFinal(Base64.decode(base64cipherText, Base64.DEFAULT)), UTF_CHARSET);
return decryptString;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
e.printStackTrace();
} catch (NullPointerException e) {
//Do nothing, nothing to decrypt
}
return null;
}
Currently the results return null on the encrypted string using the JavaScript encryption function when being decrypted so it's probably encrypting correctly(?) I'm not sure what I'm missing or doing wrong here...
Seems you are missing IV (initialization vector).
don't really know what an IV is or if it's needed here, the encryption Java code doesn't state it anywhere
The IV is an initialization vector allowing to reuse a key to encrypt multiple messages (or blocks), please have a look at the CBC block mode as you are using it.
I am not sure for JavaScript API, but at least I can give you example in Java. As well you can have a look at my blog about crypto examples
Java Encryption
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[SYMMETRIC_BLOCK_SIZE / 8];
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
SecretKey symmetricKey = new SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);
Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, symmetricKey, ivParamSpec);
byte[] encrypted = cipher.doFinal(encryptionParams.getPlaintext());
/* and encoded form can contain form of base64( IV + ciphertext ) */
For CBC mode the IV must be random. If you don't specify the IVParameter, it will be generated and you can read it from cipher.getIV();. The IV can be public, it is usually prepended before the ciphertext, as the IV is needed to decrypt the ciphertext itself.
Java Decryption
/* if IV is prepended before the ciphertext, it can be fetched as sub-array
of the decoded message */
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, symmetricKey, ivParamSpec);
byte[] decrypted = cipher.doFinal(encryptionParams.getCiphertext());
In this example there's no Mac (message authentication code included), you can have a look at the linked example.
For JavaScript you should have a look at the used API, but the principle stays the same (you have to generate, use, pass and provide the IV too somehow). This blog seems to contain be more complete code.
var iv = CryptoJS.lib.WordArray.random(128/8);
var encrypted = CryptoJS.AES.encrypt(msg, key, {
iv: iv,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
});

How to read from encrypted CDC in Cassandra

We have implemented TDE for all our tables in Cassandra DSE. We generated a system key using AES/ECB/PKCS5Padding / 128 as cipher algorithm.
We have also enabled cdc for few tables that require cdc capture. Since TDE is enabled for the tables, cdc logs are also encrypted.
We need to push the cdc captures to kafka topics. We tried to decrypt the file using the system_key auto generated in the system_key file.
AES/ECB/PKCS5Padding:128:(key)
But we are getting java.security.InvalidKeyException: Illegal key size or default parameters
Can please advise if this is key can be used for decrypting the cdc logs or suggest any solution.
Below is the snippet we used for decrypting.
public class EncryptDecrypt {
public static String encrypt(String input, String key) {
byte[] crypted = null;
try {
SecretKeySpec skey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey);
crypted = cipher.doFinal(input.getBytes());
} catch (Exception e) {
System.out.println(e.toString());
}
java.util.Base64.Encoder encoder = java.util.Base64.getEncoder();
return new String(encoder.encodeToString(crypted));
}
public static String decrypt(String input, String key) {
byte[] output = null;
try {
java.util.Base64.Decoder decoder = java.util.Base64.getDecoder();
SecretKeySpec skey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skey);
output = cipher.doFinal(decoder.decode(input));
} catch (Exception e) {
System.out.println(e.toString());
}
return new String(output);
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String key = "qhk9gDtvTUlLW9dnh/UMaw==";
String data = "ABC";
System.out.println(EncryptDecrypt.encrypt(data, key));
System.out.println(EncryptDecrypt.decrypt(EncryptDecrypt.encrypt(data, key), key));
}
}
The system_key file isn't used for direct encryption of the data, but for encryption of the actual encryption key that is stored in the dse_system.encrypted_keys. These keys are generated for every combination of algorithm/strength. See documentation for more details.

How can I read an encrypted text from a file and decrypt it?

I have some account info that is being encrypted and written to a file like this:
//imports here
public class Main
public static void main(String[] args) {
try {
String text = "this text will be encrypted";
String key = "Bar12345Bar12345";
//Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
//encrypt text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
write(new String(encrypted));
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
}
public static void write(String message) {
BufferedWriter bw = null;
FileWriter fw = null;
try {
String data = message;
File file = new File(FILENAME);
if (!file.exists()) {
file.createNewFile();
}
fw = new FileWriter(file.getAbsoluteFile(), true);
bw = new BufferedWriter(fw);
bw.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null)
bw.close();
if (fw != null)
fw.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
So the contents of the file is one string without any breaks in between. If I wanted to decrypt the string, I would do this:
String key = "Bar12345Bar12345";
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] encrypted = text.getBytes();
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(encrypted));
System.err.println(decrypted);
This works fine as long as byte[] encrypted is the same as used as in the encrypting process, but when I try to read the encrypted text from the file using a FileReader and a BufferedReader and change it into a byte using lines.getByte() it throws the exception
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
Compare the cipher text from the encrypting process with the cipher text from lines.getByte() before you try to do any decryption. They are most likely different. Try reading the entire file into a byte[] first before decrypting it. Symmetric ciphers need to do their work on blocks of the same size, in this case 16 bytes.
I would also be remiss if I didn't comment on some of the poor protocol choices.
Hard coded key - Your encryption key should never be hard-coded in your application. If you ever need to change your encryption key, you are not able to do so. If your application is distributed to end users, it's easy for them to recover the key using an application like strings.
You are using ECB as your mode of operation. ECB has several ways it can be attacked, such as block shuffling, which can allow an attacker to decrypt data without having the encryption key.
Encryption is hard to get right on your own. Consider solving your problem a different way.
You are trying to treat your encrypted array contents as (platform encoded) text, while it is not - it consists of random bytes.
You either need to create a binary file by writing the contents of encrypted to it directly. That or you can create a text file by first encoding encrypted to base64.
Currently you are trying to read lines, but there aren't any. And if there are some line endings in there they will be stripped from the ciphertext before those bytes can be decrypted.
If you perform new String(encrypted) then it is also possible that you lose part of your data, as unsupported encodings are removed from the string without warning.
Note that the word "ciphertext" is a bit misleading; modern ciphers such as AES handle binary data, not text.

JSEncrypt encrypt on client and decrypt on server

I am using JSEncrypt to encrypt user's password on the client and send to server to decrypt.
I get the public key from the server which I build the RSA Key on the client with Javascript:
var leafCert = new X509();
leafCert.readCertPEM(publicCertificate);
In my encrypt method
var encryptKey = new JSEncrypt();
var encryptedValue = serverPublicKey.encrypt(inputStr)
return String(inputStr)
On server when I decrypt I see junk message. My server-side code:
public String decryptString(String encryptedVal, String sessionId)
{
String sResult="";
try
{
PrivateKey pvtKey = getPrivateKeyFromSession(sessionId);
Cipher pkCipher=null;
pkCipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
pkCipher.init(Cipher.DECRYPT_MODE, pvtKey);
Base64 encoder = Base64.getInstance();
byte[] decodedValue = encoder.decode(encryptedVal);
byte[] deCryptedBytes =pkCipher.doFinal( decodedValue );
sResult = new String(deCryptedBytes);
}
catch(Exception ex){log.error("Error decryptString" + ex.getMessage()); }
return sResult.trim();
}
Can you let me know where am I going wrong. I do not see the actual decrypted message on the server.

Resources