1

I am looking for any sample java code that will decrypt the messages encrypted using "openssl enc -aes-256-cbc) -a -salt" command provided the key is known.

https://pastebin.com/YiwbCAW8

So far i was able to get the following java code that encrypts and also decrypts the message. But i am not able to decrypt the encrypted message using openssl command. Getting "Bad Magic Number" error. Any idea ?

Encrypt the message using the code >

Encrypt("sample text", "test$password") = "i+5zkPPgnDdV7fr/w8uHkw=="

Decrypt("i+5zkPPgnDdV7fr/w8uHkw==", "test$password") = "sample text"

Decrypt the message using openssl >

F:\cipher>echo i+5zkPPgnDdV7fr/w8uHkw== | openssl aes-256-cbc -a -salt -d

enter aes-256-cbc decryption password:

bad magic number

import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;


public class AES {

    private static final byte[] SALT = {
        (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
        (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
    };
    private static final int ITERATION_COUNT = 65536;
    private static final int KEY_LENGTH = 256;
    private Cipher ecipher;
    private Cipher dcipher;

    AES(String passPhrase) throws Exception {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

        ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ecipher.init(Cipher.ENCRYPT_MODE, secret);

        dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
        dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
    }

    public String encrypt(String encrypt) throws Exception {
        byte[] bytes = encrypt.getBytes("UTF8");
        byte[] encrypted = encrypt(bytes);
        return Base64.getEncoder().encodeToString(encrypted);
    }

    public byte[] encrypt(byte[] plain) throws Exception {
        return ecipher.doFinal(plain);
    }

    public String decrypt(String encrypt) throws Exception {
        byte[] bytes = Base64.getDecoder().decode(encrypt);
        byte[] decrypted = decrypt(bytes);
        return new String(decrypted, "UTF8");
    }

    public byte[] decrypt(byte[] encrypt) throws Exception {
        return dcipher.doFinal(encrypt);
    }

    public static void main(String[] args) throws Exception {

        String message = "sample text";
        String password = "test$password";

        AES encrypter = new AES(password);
        String encrypted = encrypter.encrypt(message);        
        String decrypted = encrypter.decrypt(encrypted);

        System.out.println("Encrypt(\"" + message + "\", \"" + password + "\") = \"" + encrypted + "\"");
        System.out.println("Decrypt(\"" + encrypted + "\", \"" + password + "\") = \"" + decrypted + "\"");
    }
}
bStk83
  • 19
  • 5
  • (Near)dupe https://stackoverflow.com/questions/50868055/unable-to-decrypt-csv-file-using-symmetric-key-java https://stackoverflow.com/questions/29151211/how-to-decrypt-an-encrypted-aes-256-string-from-cryptojs-using-java https://stackoverflow.com/questions/31947256/how-to-decode-a-string-encoded-with-openssl https://stackoverflow.com/questions/11783062/how-to-decrypt-an-encrypted-file-in-java-with-openssl-with-aes and more at https://crypto.stackexchange.com/questions/36981/what-are-kdf-parameters-in-openssl-command-line-utility-for-enc . Also salt should never be hardcoded. – dave_thompson_085 Mar 15 '19 at 10:47

3 Answers3

2

You may search stackoverflow for many similar questions.

you have multiple issues in your code:

  1. You use different keys:

In Java you use PBKDF2 to generate an encryption key from the provided password. Openssl uses its EVP_BytesToKey. Search internet for Java implementation. Please note the hash used in the EVP_BytesToKey changed with some openssl version (from MD5 to SHA-1 SHA-256), if someone is having more details, please comment

  1. And you use random IV. you don't pass the IV along the ciphertext, so you may be able to decrypt the ciphertext with the same cipher instance (kkeping the same iv), but lets try your Java code to decrypt your ciphertext other time or with other instance, it won't work. You need to pass IV along the ciphertext (usually it's prepended)

  2. Openssl expect following format:

    Salted_<8 byte salt>ciphertext
    Salted__<8 byte salt>ciphertext

8 byte salt is a random byte array used to generate the encryption key and IV from the provided password. Try encrypt with openssl with -pparameter, it will print the salt, IV and Key generated so you can check and compare

  1. Using CBC without any integrity check (hmac, ..) may be unsafe in many implementations

Suggestions:

  • you can find an openssl java library implementing the same required (EVP_BytesToKey)
  • you can implement EVP_BytesToKey yourself
  • you can use openssl directly with -K/-ivparameters providing the encryption key and IV (in hex format) instead of password, then openssl expects pure ciphertext (no Salted_ or salt inside the input)
gusto2
  • 11,210
  • 2
  • 17
  • 36
  • OpenSSL 1.1.0 (2016) changed default hash for `enc` from MD5 to SHA256 (not SHA1) but you have been able to _specify_ a different hash since before 1.0.0 (2010). `enc` default _derives_ the IV (as well as the key) from pw+salt, as you say in 3; it is not random or explicit. (It is for OpenSSL 'legacy' _privatekey_ files, and it can be for `-K -iv` as you say.) The `enc` file 'magic' is `Salted__` with _two_ underscores. – dave_thompson_085 Mar 15 '19 at 10:45
1

Also got an improved solution from the following site. Got the code for both encryption and decryption for now...

http://qaru.site/questions/19874/java-equivalent-of-an-openssl-aes-cbc-encryption

import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import static java.nio.charset.StandardCharsets.*;

/**
* Mimics the OpenSSL AES Cipher options for encrypting and decrypting messages using a 
* shared key (aka password) with symetric ciphers.
*/

public class OpenSslAesQu {

    /** OpenSSL magic initial bytes. */
    private static final String SALTED_STR = "Salted__";
    private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);

    public static String encryptAndURLEncode(String password, String clearText) {

        String encrypted = null;
        try {
            encrypted = URLEncoder.encode(encrypt(password, clearText),UTF_8.name());
        } catch (Exception e) {e.printStackTrace();}
        return encrypted;

    }

    /**
     * 
     * @param password  The password / key to encrypt with.
     * @param data      The data to encrypt
     * @return  A base64 encoded string containing the encrypted data.
     */

    public static String encrypt(String password, String clearText) {

        String encryptedMsg = null;
        final byte[] pass = password.getBytes(US_ASCII);
        final byte[] salt = (new SecureRandom()).generateSeed(8);
        final byte[] inBytes = clearText.getBytes(UTF_8);

        final byte[] passAndSalt = array_concat(pass, salt);
        byte[] hash = new byte[0];
        byte[] keyAndIv = new byte[0];

        try {
            for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
                final byte[] hashData = array_concat(hash, passAndSalt);
                final MessageDigest md = MessageDigest.getInstance("MD5");
                hash = md.digest(hashData);
                keyAndIv = array_concat(keyAndIv, hash);
            }

            final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
            final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
            final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");

            final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
            byte[] data = cipher.doFinal(inBytes);
            data =  array_concat(array_concat(SALTED_MAGIC, salt), data);
            //return Base64.getEncoder().encodeToString( data );        
            encryptedMsg = org.apache.commons.codec.binary.Base64.encodeBase64String(data);
        } catch(Exception e) {e.printStackTrace();}

        return encryptedMsg;
    }

    /**
     * @see http://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption  for what looks like a useful answer.  The not-yet-commons-ssl also has an implementation
     * @param password
     * @param source The encrypted data
     */

    public static String decrypt(String password, String source) {

        String decryptedMsg = null;

        final byte[] pass = password.getBytes(US_ASCII);

        //final byte[] inBytes = Base64.getDecoder().decode(source);
        final byte[] inBytes = Base64.decodeBase64(source);

        final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
        if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
            throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
        }

        final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);

        final byte[] passAndSalt = array_concat(pass, salt);

        byte[] hash = new byte[0];
        byte[] keyAndIv = new byte[0];
        try {
        for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
            final byte[] hashData = array_concat(hash, passAndSalt);
            final MessageDigest md = MessageDigest.getInstance("MD5");
            hash = md.digest(hashData);
            keyAndIv = array_concat(keyAndIv, hash);
        }

        final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
        final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");

        final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);

        final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
        final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
        decryptedMsg = new String(clear, UTF_8);
        } catch (Exception e) {e.printStackTrace();}

        return decryptedMsg;
    }


    private static byte[] array_concat(final byte[] a, final byte[] b) {
        final byte[] c = new byte[a.length + b.length];
        System.arraycopy(a, 0, c, 0, a.length);
        System.arraycopy(b, 0, c, a.length, b.length);
        return c;
    }

    public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {

        String msg = "the decrypted message is this";       
        String password = "pass";

        System.out.println(">> "+encrypt(password,msg));
        //System.out.println("<< "+decrypt(encrypt(msg, password), password));

        String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
        String encryptedMsg2 = "U2FsdGVkX1/B6oOznz5+nd7W/qXwXI7G7rhj5o9pjx8MS0TXp9SNxO3AhM9HBJ/z";

        System.out.println(decrypt(password,encryptedMsg));
        System.out.println(decrypt(password,encryptedMsg2));
        System.out.println(decrypt(password,encrypt(password,msg)));
    }

}
bStk83
  • 19
  • 5
0

Thanks a lot for the clues. As mentioned, did some search and modified the code from one of the post. I have seen similar code with EVP_BytesToKeys in many places, but took some time to figure out the usage. I am able to decrypt the msg encrypted by openssl.

Trying to search the code for encryption as well. Meanwhile any help of encryption is appreciated as well.

import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

public class AES5 {

    private static final Charset ASCII = Charset.forName("ASCII");
    private static final int INDEX_KEY = 0;
    private static final int INDEX_IV = 1;
    private static final int ITERATIONS = 1;
    private static final int SALT_OFFSET = 8;
    private static final int SALT_SIZE = 8;
    private static final int CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE;
    private static final int KEY_SIZE_BITS = 256;

    /**
     * Thanks go to Ola Bini for releasing this source on his blog. The source was
     * obtained from <a href="http://olabini.com/blog/tag/evp_bytestokey/">here</a>
     * 
     */

    public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data,
            int count) {

        byte[][] both = new byte[2][];
        byte[] key = new byte[key_len];
        int key_ix = 0;
        byte[] iv = new byte[iv_len];
        int iv_ix = 0;
        both[0] = key;
        both[1] = iv;
        byte[] md_buf = null;
        int nkey = key_len;
        int niv = iv_len;
        int i = 0;
        if (data == null) {
            return both;
        }
        int addmd = 0;
        for (;;) {
            md.reset();
            if (addmd++ > 0) {
                md.update(md_buf);
            }
            md.update(data);
            if (null != salt) {
                md.update(salt, 0, 8);
            }
            md_buf = md.digest();
            for (i = 1; i < count; i++) {
                md.reset();
                md.update(md_buf);
                md_buf = md.digest();
            }
            i = 0;
            if (nkey > 0) {
                for (;;) {
                    if (nkey == 0)
                        break;
                    if (i == md_buf.length)
                        break;
                    key[key_ix++] = md_buf[i];
                    nkey--;
                    i++;
                }
            }
            if (niv > 0 && i != md_buf.length) {
                for (;;) {
                    if (niv == 0)
                        break;
                    if (i == md_buf.length)
                        break;
                    iv[iv_ix++] = md_buf[i];
                    niv--;
                    i++;
                }
            }
            if (nkey == 0 && niv == 0) {
                break;
            }
        }
        for (i = 0; i < md_buf.length; i++) {
            md_buf[i] = 0;
        }
        return both;
    }


    public static byte[][] getKeyIV(byte[] headerSaltAndCipherText, Cipher aesCBC, String password) {       
        byte[] salt = Arrays.copyOfRange(headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
        byte[][] keyAndIV=null;
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            keyAndIV = EVP_BytesToKey(KEY_SIZE_BITS / Byte.SIZE, aesCBC.getBlockSize(), md5, salt,
                    password.getBytes(ASCII), ITERATIONS);
        } catch (Exception e) {e.printStackTrace();}

        return keyAndIV;
    }

    // https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes
    public static String decrypt(String encryptedMsg, String password) {

        String decryptedMsg =null;      
        byte[] headerSaltAndCipherText = Base64.decodeBase64(encryptedMsg);
        byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
        try {
            Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final byte[][] keyAndIV = getKeyIV(headerSaltAndCipherText, aesCBC, password);
            SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
            IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
            aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
            byte[] decrypted = aesCBC.doFinal(encrypted);
            decryptedMsg = new String(decrypted, ASCII);
        } catch (Exception e) {e.printStackTrace();}

        return decryptedMsg;
    }

    //TODO - Encrypt the msg in same manner as "openssl enc -aes-256-cbc -a -salt"
    public static String encrypt(String msg, String password) {

        String decryptedMsg =null;      
        byte[] headerSaltAndCipherText = Base64.decodeBase64(msg);
        byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
        try {
            Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final byte[][] keyAndIV = getKeyIV(headerSaltAndCipherText, aesCBC, password);
            SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
            IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
            aesCBC.init(Cipher.ENCRYPT_MODE, key, iv);
            byte[] decrypted = aesCBC.doFinal(encrypted);
            decryptedMsg = new String(decrypted, ASCII);
        } catch (Exception e) {e.printStackTrace();}

        return decryptedMsg;
    }

    public static void main(String[] args) {

        String msg = "the decrypted message is this";
        String password = "pass";

        System.out.println(encrypt(msg, password));

        String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
        System.out.println(decrypt(encryptedMsg, password));
    }
}
bStk83
  • 19
  • 5