Home > Blockchain >  CryptoJs decrypt function equivalent in Java
CryptoJs decrypt function equivalent in Java

Time:11-29

I'm trying to decrypt a token that the server I'm using brings in order to load correctly users access to the page.

I already achieved the goal using CryptoJs using JavaScript. However now I need to transfer that function to my java backend.

Look my code using CryptoJs which works correctly

const token = "U2FsdGVkX1 6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4 67n/I7P fg=="
const key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a"

// Decrypt
const iv = CryptoJS.enc.Utf8.parse(key);
const decryptedToken = CryptoJS.AES.decrypt(token, key, {
  keySize: 16,
  iv,
  mode: CryptoJS.mode.CBC,
  padding: CryptoJS.pad.Pkcs7,
});

const stringToken = decryptedToken.toString(CryptoJS.enc.Utf8);
const dataToUse = JSON.parse(stringToken)

console.log("decryptedToken => ", stringToken);
console.log("data => ",dataToUse);

And this is my code in Java using javax.crypto

public String decrypt() {
  String finalToken = null;
  Cipher cipher;
  try {
    String token = "U2FsdGVkX1 6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4 67n/I7P fg==";
    String key = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a";
    Key skeySpec = new SecretKeySpec(Arrays.copyOf(key.getBytes(), 16), "AES");
    byte[] iv = Arrays.copyOf(key.getBytes(StandardCharsets.UTF_8), 16);
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
    byte[] bytesToDecrypt = Base64.decodeBase64(token.getBytes());
    cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
    byte[] decrypted = cipher.doFinal(bytesToDecrypt);
    finalToken = new String(decrypted);
    log.info("Decrypt token succesfully {}", finalToken);
  } catch (NoSuchAlgorithmException
      | InvalidAlgorithmParameterException
      | IllegalBlockSizeException
      | NoSuchPaddingException
      | BadPaddingException
      | InvalidKeyException e) {
    e.printStackTrace();
    log.error("Error decrypting");
  }

  return finalToken;
}

I'm not sure I'm setting correctly the key and the iv. With the piece of code above I get this error:

javax.crypto.BadPaddingException: pad block corrupted

If I don't "cut" the key and the iv in order to have 16 bytes I get wrong length error.

Can someone help me to figure out what's wrong please!

The expected result is to get this info in java so then I can manipulate the object:

{name: "Burak", surName: "Bayraktaroglu"}

CodePudding user response:

Decryption fails because CryptoJS interprets the key material as password when passed as string (as in the posted code).
In this case, the password and a salt are used to derive the key and IV applying the OpenSSL proprietary key derivation function EVP_BytesToKey().
The random 8 bytes salt is generated during encryption, which returns as result the Base64 encoding of the concatenation of a prefix, the salt and the actual ciphertext. The prefix consists of the ASCII encoding of Salted__.

Therefore, the following steps are necessary in the decryption process:

  • Determining salt and ciphertext from the CryptoJS return value
  • Deriving key and IV using password and salt
  • Decryption of the ciphertext with the derived key and IV

There are several ways to implement the last two steps in Java.
You can find various ports of EVP_BytesToKey() on the web which can be used in conjunction with the native JCA/JCE for decryption. These EVP_BytesToKey() ports are not always reliable, a solid port is this one.
A convenient (and reliable) alternative is BouncyCastle (with its own EVP_BytesToKey() port), described below:

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;

...

// Get salt and ciphertext
String saltCiphertextB64 = "U2FsdGVkX1 6YueVRKp6h0dZfk/a8AC9vyFfAjxD4nb7mXsKrM7rI7xZ0OgrF1sShHYNLMJglz4 67n/I7P fg==";
byte[] saltCiphertext = Base64.getDecoder().decode(saltCiphertextB64);
ByteBuffer byteBuffer = ByteBuffer.wrap(saltCiphertext);
byte[] prefix = new byte[8];
byte[] salt = new byte[8];
byteBuffer.get(prefix);
byteBuffer.get(salt);
byte[] ciphertext = new byte[byteBuffer.remaining()];
byteBuffer.get(ciphertext);

// Key derivation via EVP_BytesToKey() (using BouncyCastle)
String passwordStr = "p80a0811-47db-2c39-bcdd-4t3g5h2d5d1a";
byte[] password = passwordStr.getBytes(StandardCharsets.UTF_8);
OpenSSLPBEParametersGenerator pbeGenerator = new OpenSSLPBEParametersGenerator(new MD5Digest()); 
pbeGenerator.init(password, salt);
ParametersWithIV parameters = (ParametersWithIV) pbeGenerator.generateDerivedParameters(256, 128); // keySize, ivSize in bits
// FYI: key: ((KeyParameter)parameters.getParameters()).getKey()
//      IV : parameters.getIV()

// Decryption (using BouncyCastle)
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
cipher.init(false, parameters);
byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length)];
int length = cipher.processBytes(ciphertext, 0, ciphertext.length, plaintext, 0);
length  = cipher.doFinal(plaintext, length);        
String plaintextSr = new String(plaintext, 0, length, StandardCharsets.UTF_8);
System.out.println(plaintextSr); // {"name":"Burak","surName":"Bayraktaroglu"}
  • Related