1

I am trying to encrypt a value server side and then decrypt from the browser side. This is the Java code I'm using to encrypt, which is working correctly:

package aes;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
 
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class AesEncryption {

    private static SecretKeySpec secretKey;
    private static byte[] key;
 
    public static void setKey(String myKey) {
        MessageDigest sha = null;
        try {
            key = myKey.getBytes("UTF-8");
            sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16); 
            secretKey = new SecretKeySpec(key, "AES");
        } 
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } 
        catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public static String encrypt(String strToEncrypt, String secret) {
        try {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
        } 
        catch (Exception e) 
        {
            System.out.println("Error while encrypting: " + e.toString());
        }
        return null;
    }

I then have a JavaScript method I call to decrypt the encrypted value:

  aesDecrypt(encryptedValue) {
    console.log("TESTING ENCRYPTED VALUE : " , encryptedValue)
    var bytes = CryptoJS.AES.decrypt(encryptedValue, secretAesKey, {
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7
    })
    console.log("TESTING BYTES : " , bytes)
    var originalValue = bytes.toString(CryptoJS.enc.Utf8);
    console.log("TESTING ORIGINAL VALUE : " , originalValue)
    return originalValue
  }

When logging each step of the decryption, when trying to print the original value, it's just blank. I know that the encryptedValue has to be converted back to bytes in the same way that it was originally encrypted and then has to be encoded with UTF-8 to get back to the original string. What else could I be missing that is causing this?

3
  • 4
    myKey.getBytes("UTF-8"); is in 99% of all occurrences a bug and a security issue because a String is never directly used as a key in cryptography. If you have a password use a key derivation function that has password as input and a 128bit/256bit key key as output. Commented Jul 13, 2021 at 13:35
  • 3
    In the CryptoJS code the key derivation via SHA1 is missing. The first 16 bytes are used as key. Note, a key derivation via digest is insecure (s. 1st comment), as well as the ECB mode. Commented Jul 13, 2021 at 13:47
  • Just searched around online but can't seem to find an example of CryptoJS using this key derivation via SHA1 during the decryption. Did you know of an example somewhere you could post? @user9014097 And I will definitely look into that myKey.getBytes("UTF-8"); issue. I have never really worked with encryption so am just trying to get a basic baseline code working first. Commented Jul 13, 2021 at 14:54

1 Answer 1

1

Key derivation via SHA-1 is possible with CryptoJS e.g. as follows:

var passphrase = '<your passphrase>';
var sha1Hash = CryptoJS.SHA1(passphrase);                                           // Create SHA1 hash
var secretAesKey = CryptoJS.lib.WordArray.create(sha1Hash.words.slice(0, 16 / 4));  // Use the first 4 words (= 16 bytes) of the hash

Test with a ciphertext generated by the Java code:

function aesDecrypt(encryptedValue) {
    console.log("TESTING ENCRYPTED VALUE : " , encryptedValue);
    var bytes = CryptoJS.AES.decrypt(
        encryptedValue, 
        secretAesKey, 
        {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        }
    );
    console.log("TESTING BYTES : " , bytes);
    var originalValue = bytes.toString(CryptoJS.enc.Utf8);
    console.log("TESTING ORIGINAL VALUE : " , originalValue);
    return originalValue;
  }

var passphrase = 'my secret passphrase';
var sha1Hash = CryptoJS.SHA1(passphrase)
var secretAesKey = CryptoJS.lib.WordArray.create(sha1Hash.words.slice(0, 16 / 4));
var ciphertext = 'hjC0d3sEGJ/3SBTPyV5RrbiIi5jCfhj9T2ZbeWNyBHqmCrVaKLYN94ouiMOz8d4x';
aesDecrypt(ciphertext);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

which correctly decrypts the ciphertext into the original plaintext.


Note that, as mentioned in the comments, the ECB mode is insecure and also the key derivation from a password via a digest (even if it would not be broken). Instead, a reliable key derivation function like Argon2 or PBKDF2 should be used.

Sign up to request clarification or add additional context in comments.

4 Comments

This is very helpful! So should my passphrase used in the sha1Hash match my "secret" that is used in the encryption on the Java side? Or are they completely different?
@knxwledge - Yes, that's right. secret in the Java code, i.e. the 2nd parameter of the encrypt() method, corresponds to passphrase in the CryptoJS code. I created the ciphertext hjC0... 8d4x used in my example with your Java code and the passphrase my secret passphrase and the plaintext The quick brown fox jumps over the lazy dog.
How would salting fit into this? And how would I be able to decrypt from ui after salt has been added? I know that salts are usually unpredictable, random values.
This cannot be answered in the context of a comment. Please ask a new question with all the necessary information.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.