简体   繁体   English

正在寻找用于解密使用 openssl -aes-256-cbc -a -salt 命令加密的消息的 Java 实现?

[英]Looking for Java implementation for decrypting a message encrypted using openssl -aes-256-cbc -a -salt command?

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.我正在寻找任何示例 java 代码,如果密钥已知,它将解密使用“openssl enc -aes-256-cbc) -a -salt”命令加密的消息。

https://pastebin.com/YiwbCAW8 https://pastebin.com/YiwbCAW8

So far i was able to get the following java code that encrypts and also decrypts the message.到目前为止,我能够获得以下用于加密和解密消息的 Java 代码。 But i am not able to decrypt the encrypted message using openssl command.但是我无法使用 openssl 命令解密加密的消息。 Getting "Bad Magic Number" error.出现“错误的幻数”错误。 Any idea ?任何的想法 ?

Encrypt the message using the code >

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

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

Decrypt the message using openssl >

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

enter aes-256-cbc decryption password:输入aes-256-cbc解密密码:

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 + "\"");
    }
}

You may search stackoverflow for many similar questions.您可以在 stackoverflow 中搜索许多类似的问题。

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.在 Java 中,您使用 PBKDF2 从提供的密码生成加密密钥。 Openssl uses its EVP_BytesToKey . Openssl 使用其EVP_BytesToKey Search internet for Java implementation.在 Internet 上搜索 Java 实现。 Please note the hash used in the EVP_BytesToKey changed with some openssl version (from MD5 to请注意 EVP_BytesToKey 中使用的哈希随某些 openssl 版本(从 MD5 到SHA-1 SHA-1 SHA-256), if someone is having more details, please comment SHA-256),如果有人有更多详细信息,请发表评论

  1. And you use random IV.你使用随机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.您不会沿密文传递 IV,因此您可以使用相同的密码实例(kkeping 相同的 iv)解密密文,但是让我们尝试使用 Java 代码在其他时间或使用其他实例解密您的密文,它不会工作。 You need to pass IV along the ciphertext (usually it's prepended)您需要在密文中传递 IV(通常是前置的)

  2. Openssl expect following format: Openssl 期望以下格式:

    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. 8 字节 salt 是一个随机字节数组,用于从提供的密码生成加密密钥和 IV。 Try encrypt with openssl with -p parameter, it will print the salt, IV and Key generated so you can check and compare尝试使用带-p参数的 openssl 加密,它将打印生成的盐、IV 和密钥,以便您可以检查和比较

  1. Using CBC without any integrity check (hmac, ..) may be unsafe in many implementations在许多实现中使用 CBC 而不进行任何完整性检查 (hmac, ..) 可能是不安全的

Suggestions:建议:

  • you can find an openssl java library implementing the same required (EVP_BytesToKey)您可以找到一个实现相同要求的 openssl java 库(EVP_BytesToKey)
  • you can implement EVP_BytesToKey yourself你可以自己实现 EVP_BytesToKey
  • you can use openssl directly with -K/-iv parameters providing the encryption key and IV (in hex format) instead of password, then openssl expects pure ciphertext (no Salted_ or salt inside the input)您可以直接将 openssl 与-K/-iv参数一起使用,提供加密密钥和 IV(十六进制格式)而不是密码,然后 openssl 需要纯密文(输入中没有 Salted_ 或 salt)

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.我在很多地方都看到过类似 EVP_BytesToKeys 的代码,但花了一些时间来弄清楚用法。 I am able to decrypt the msg encrypted by openssl.我能够解密由 openssl 加密的 msg。

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));
    }
}

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 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)));
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM