简体   繁体   English

解密使用 openssl 和 aes-cbc-256 加密的文件

[英]Decrypt file encrypted using openssl with aes-cbc-256

I have encrypted a file using below commands我已经使用以下命令加密了一个文件

openssl rand 32 > test.key openssl rand 32 > test.key

openssl enc -aes-256-cbc -iter 10000 -pbkdf2 -salt -in test.txt -out test.txt.enc -pass file:test.key openssl enc -aes-256-cbc -iter 10000 -pbkdf2 -salt -in test.txt -out test.txt.enc -pass file:test.key

Now i am trying to decrypt it using java.现在我正在尝试使用 java 对其进行解密。 tring since last few days but no success.从最近几天开始尝试,但没有成功。

Can anyone help?任何人都可以帮忙吗?

my code我的代码

package test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
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.io.IOUtils;

public class OpenSSlDecryptor {
    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 = 10000;

    private static final int ARG_INDEX_FILENAME = 0;
    private static final int ARG_INDEX_PASSWORD = 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(final int key_len, final int iv_len, final MessageDigest md,
            final byte[] salt, final byte[] data, final int count) {
        final byte[][] both = new byte[2][];
        final byte[] key = new byte[key_len];
        int key_ix = 0;
        final 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 void main(final String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
     final String fileName = "test.txt.enc";

      final File f = new File(fileName );
      try {
            // --- read base 64 encoded file ---

            List<String> lines = new ArrayList<>();

            try (BufferedReader br = new BufferedReader(new FileReader(f))) {
              //br returns as stream and convert it into a List
              lines = br.lines().collect(Collectors.toList());

          } catch (final IOException e) {
              e.printStackTrace();
          }

            final StringBuilder sb = new StringBuilder();
            for (final String line : lines) {
                sb.append(line.trim());
            }


            final String random_bin_key = "test.key";
            final byte[] passwordKey = IOUtils.toByteArray(new FileInputStream(random_bin_key));

            // --- extract salt & encrypted ---
            final byte[] headerSaltAndCipherText = sb.toString().getBytes();
            // header is "Salted__", ASCII encoded, if salt is being used (the default)
            final byte[] salt = Arrays.copyOfRange(
                    headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
            final byte[] encrypted = Arrays.copyOfRange(
                    headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);

            // --- specify cipher and digest for EVP_BytesToKey method ---

            final Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final MessageDigest md5 = MessageDigest.getInstance("MD5");

            // --- create key and IV  ---

            // the IV is useless, OpenSSL might as well have use zero's
            final byte[][] keyAndIV = EVP_BytesToKey(
                    KEY_SIZE_BITS / Byte.SIZE,
                    aesCBC.getBlockSize(),
                    md5,
                    salt,
                    passwordKey,
                    ITERATIONS);
            final SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
            final IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);

            // --- initialize cipher instance and decrypt ---

            aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
            final byte[] decrypted = aesCBC.doFinal(encrypted);
            final String answer = new String(decrypted);
            System.out.println(answer);
        } catch (final BadPaddingException e) {
           System.out.println(e.getMessage());
        } catch (final IllegalBlockSizeException e) {
          System.out.println(e.getMessage());
        } catch (final GeneralSecurityException e) {
          System.out.println(e.getMessage());
        } catch (final IOException e) {
          System.out.println(e.getMessage());
        }
}

Error i am getting我得到的错误

Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

I refereed following link我参考了以下链接

https://raymii.org/s/tutorials/Encrypt_and_decrypt_files_to_public_keys_via_the_OpenSSL_Command_Line.html https://raymii.org/s/tutorials/Encrypt_and_decrypt_files_to_public_keys_via_the_OpenSSL_Command_Line.html

https://community.cloudera.com/t5/Support-Questions/How-do-I-decrypt-AES-256-CBC-data-in-HDF-if-it-was-encrypted/td-p/97961# https://community.cloudera.com/t5/Support-Questions/How-do-I-decrypt-AES-256-CBC-data-in-HDF-if-it-was-encrypted/td-p/97961#

tried with试过

` final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    // strip off the salt and iv
    final ByteBuffer buffer = ByteBuffer.wrap(encryptedText);
    byte[] saltBytes = new byte[16];
    buffer.get(saltBytes, 0, saltBytes.length);
    saltBytes =  Arrays.copyOfRange(saltBytes, 8, 16);
    final byte[] ivBytes1 = new byte[cipher.getBlockSize()];
    buffer.get(ivBytes1, 0, ivBytes1.length);
    final int length = buffer.capacity() - 16 - ivBytes1.length;
    // length = length+ 16 -(length%16);
    final byte[] encryptedTextBytes = new byte[length];

    buffer.get(encryptedTextBytes);
    // Deriving the key
     final SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
     final PBEKeySpec spec = new PBEKeySpec(new String(password).toCharArray(), saltBytes, 10000,
    256);
     final SecretKey secretKey = factory.generateSecret(spec);
     final SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes1));
    byte[] decryptedTextBytes = null;
    try {
      decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
    } catch (final IllegalBlockSizeException e) {
      e.printStackTrace();
    } catch (final BadPaddingException e) {
      e.printStackTrace();
    }

Getting badpadding exception获取 badpadding 异常

tried with PBKDF2WithHmacSHA256 still getting the error尝试使用PBKDF2WithHmacSHA256仍然收到错误

You have several problems.你有几个问题。 The most obvious is that you are trying to read the IV from the file, but openssl enc in its default password-based mode derives both key and IV from password and salt -- even when using PBKDF2.最明显的是您正在尝试从文件中读取 IV,但openssl enc在其默认的基于密码的模式下从密码和盐中派生密钥和 IV - 即使使用 PBKDF2。 However, both the standard (Sun/Oracle/OpenJDK) and BouncyCastle providers in Java implement PBKDF2 to derive only a key -- the way it is used in PBES2 .但是,Java 中的标准(Sun/Oracle/OpenJDK)和 BouncyCastle 提供程序都实现了 PBKDF2 以仅派生一个密钥——它在PBES2中的使用方式。

Even without that, your method of generating the 'password' as random bytes wouldn't work either.即使没有这个,您将“密码”生成为随机字节的方法也不起作用。 The PKCS5 standard actually defines PBKDF2 to take the password as PKCS5 标准实际上定义了 PBKDF2 来取密码为

an octet string of arbitrary length whose interpretation as a text string is unspecified.一个任意长度的八位组字符串,其作为文本字符串的解释是未指定的。 In the interest of interoperability, however, it is recommended that applications follow some common text encoding rules.然而,为了互操作性,建议应用程序遵循一些常见的文本编码规则。 ASCII and UTF-8 [RFC3629] are two possibilities. ASCII 和 UTF-8 [RFC3629] 是两种可能性。 (ASCII is a subset of UTF-8.) (ASCII 是 UTF-8 的子集。)

Many systems take interoperable encoding more seriously, and Java in particular (which was designed from its inception to be worldwide) defines PBEKeySpec to contain characters -- char[] in Java is UTF-16 -- which are encoded as UTF-8 when doing PBKDF2. Many systems take interoperable encoding more seriously, and Java in particular (which was designed from its inception to be worldwide) defines PBEKeySpec to contain characters -- char[] in Java is UTF-16 -- which are encoded as UTF-8 when doing PBKDF2. In contrast openssl is a C program dating from before the turn of the century when C started admitting the existence of countries other than the USA, so it only knows about bytes -- bytes which might be ASCII, or some other single-byte code like EBCDIC, but maybe not characters at all and certainly not any of those weird foreign characters that don't fit in a byte.相比之下openssl是一个 C 程序,可以追溯到世纪之交之前,当时 C 开始承认除了美国以外的国家存在字节,因此它只知道其他字节EBCDIC,但可能根本不是字符,当然也不是那些不适合一个字节的奇怪外来字符。 The probability of a sequence of 32 random bytes being valid UTF-8 is very low; 32 个随机字节序列有效 UTF-8 的概率非常低; it's too much work for me to figure analytically, but I ran a test of 100 million random values and got only one that would work with your scheme.对我来说,分析计算的工作量太大,但我对 1 亿个随机值进行了测试,结果只有一个可以与你的方案配合使用。 (I was going to test a billion but got tired of waiting.) (我打算测试十亿,但厌倦了等待。)

Plus, since a password is supposed to be text, openssl reads -pass file: as a text file and treats it as a string.另外,由于密码应该是文本, openssl读取 -pass -pass file:作为文本文件并将其视为字符串。 That means if any of the random bytes is a null byte or a byte corresponding to the newline character, the remainder of the data in the file is discarded and ignored for the key-and-IV derivation.这意味着如果任何随机字节是 null 字节或对应于换行符的字节,则文件中的其余数据将被丢弃并忽略密钥和 IV 派生。 This will occur on average about 1 in 4 times for random 32-byte values, and about 1 in 20 times it will occur early enough in the file to make the result cryptographically weak and breakable.对于随机的 32 字节值,这将平均大约 4 次出现,大约 20 次中的 1 次会在文件中发生得足够早,以使结果在密码学上很弱且易破解。

Which raises the point: why are you using password-based encryption at all?这就提出了一点:你为什么要使用基于密码的加密? If your 'key' is 32 bytes from a decent secure RNG -- which openssl rand is -- you don't need to strengthen it, it's already valid as a key.如果您的“密钥”是来自一个体面的安全 RNG 的 32 个字节openssl rand是——你不需要加强它,它已经作为一个有效的密钥。 You can use openssl enc to do key-based encryption, not password-based, and it's more efficient, more secure, AND much easier in Java -- a massive win.您可以使用openssl enc进行基于密钥的加密,而不是基于密码的加密,并且在 Java 中更高效、更安全、更容易——这是一个巨大的胜利。 IF you use a new, random key for each encryption you don't even have to use a real IV, you can just use a zero IV as I did below.如果您为每个加密使用一个新的随机密钥,您甚至不必使用真正的 IV,您可以像我在下面所做的那样使用零 IV。 But if you are going to reuse the/any key, you need to use a unique and unpredictable -- normally random -- IV for each encryption, and convey it with the data, perhaps by just putting it at the front.但是,如果您要重用/任何密钥,则需要为每次加密使用唯一且不可预测的(通常是随机的)IV,并将其与数据一起传送,也许只需将其放在前面即可。

So anyway, here's a fairly simple Java program which can handle either case: the openssl form of pdbkf2 with a 'password' that isn't actually a password and isn't UTF-8, or the more sensible key-based form (but for this demo with zero IV):因此,无论如何,这是一个相当简单的ZD52387880EE1EA22817A72D3759213819Z程序,可以处理任何一种情况:Z50955D4B2031271F8FDDA1764C1A6666ACIBIE的exey eimny ins of ze emem pdbkf2 eque ness of'passect and password99666666Acibile Inspect ofers'password99 expassion'passpers和密码。 IV 为零的演示):

//nopackage
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.security.*;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class SO61613286 {
    static public void main (String[] args) throws Exception /* let JVM give error */{
        // arguments: P/K, filename output from openssl enc, filename of text pw or binary key
        byte[] file = Files.readAllBytes(Paths.get(args[1])); 
        byte[] fil2 = Files.readAllBytes(Paths.get(args[2])); 
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        if( args[0].startsWith("P") ){
            // possibly truncate 'password' in fil2
            int n = 0; for( ; n < fil2.length; n++ ) if( fil2[n]==0 || fil2[n]=='\n' ) break;
            if( n < fil2.length ) fil2 = Arrays.copyOf(fil2, n);
            // extract salt and derive ...
            byte[] salt = Arrays.copyOfRange(file, 8, 16);
            byte[] derive = PBKDF2 ("HmacSHA256", fil2, salt, 10000, 48);
            // ... key is first 32, IV is last 16
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(derive,0,32,"AES"), new IvParameterSpec(derive,32,16));
            // remainder of file is ciphertext
            System.out.write( cipher.doFinal(file,16,file.length-16) );
        }else{
            // just use fil2 as key and zeros for IV ...
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(fil2,"AES"), new IvParameterSpec(new byte[16]));
            // ... all of file is ciphertext
            System.out.write( cipher.doFinal(file,0,file.length) );
            // !!!if key will be reused, must use better IVs and transmit with the data!!!
        }
    }
    public static byte[] PBKDF2 (String prf, byte[] pass, byte[] salt, int iter, int len)
            throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException {
        byte[] result = new byte[len];
        Mac mac = Mac.getInstance(prf);
        mac.init (new SecretKeySpec (pass,prf));
        byte[] saltcnt = Arrays.copyOf (salt, salt.length+4);
        while( /*remaining*/len>0 ){
            for( int o = saltcnt.length; --o>=saltcnt.length-4; ) if( ++saltcnt[o] != 0 ) break; 
            byte[] u = saltcnt, x = new byte[mac.getMacLength()];
            for( int i = 1; i <= iter; i++ ){
                u = mac.doFinal (u); 
                for( int o = 0; o < x.length; o++ ) x[o] ^= u[o];
            }
            int len2 = Math.min (len, x.length);
            System.arraycopy (x,0, result,result.length-len, len2);
            len -= len2;
        }
        return result;
    }
    public static void testutf8 (){
        Random r = new Random();
        byte[] t = new byte[32];
        for( int i = 0; i < 1000000000; i++ ){
            r.nextBytes(t); 
            if( Arrays.equals(new String (t, StandardCharsets.UTF_8).getBytes(StandardCharsets.UTF_8), t) ) 
                System.out.println(i+" "+Arrays.toString(t));
            if( i % 1000000 == 999999 ) System.out.println (i);
        }
    }
}

and a demo:和一个演示:

$ openssl rand 32 >SO61613286.rnd   # repeated several times until I got this:
$ xxd SO61613286.rnd   # notice the null byte
0000000: ab1a 1384 9238 0900 c947 6b9a c23d 5ee0  .....8...Gk..=^.
0000010: 32f0 6b2f 91ec 2dd9 a69d eb7d e00e 37ff  2.k/..-....}..7.
$
$ echo the name of the cat >SO61613286.in
$
$ openssl aes-256-cbc -in SO61613286.in -out SO61613286.enc1 -pass file:SO61613286.rnd -pbkdf2 -iter 10000
$ java8 -cp . SO61613286 P SO61613286.enc1 SO61613286.rnd
the name of the cat
$
$ openssl aes-256-cbc -in SO61613286.in -out SO61613286.enc2 -K $(xxd -p -c32 SO61613286.rnd) -iv 00
hex string is too short, padding with zero bytes to length
$ # that's a warning and can be ignored, as long as we don't need real IVs (see above)
$ java8 -cp . SO61613286 K SO61613286.enc2 SO61613286.rnd      
the name of the cat
$

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

相关问题 使用OpenSSL AES 256 CBC对加密文件进行Java解密 - Java decryption of an encrypted file with openssl aes 256 cbc 如何解密用PHP方法openssl_encryp aes-256-cbc加密的Java中的数据? - How to decrypt data in Java that was encrypted by PHP method openssl_encryp aes-256-cbc? AES-256-CBC用PHP加密并用Java解密 - AES-256-CBC encrypted with PHP and decrypt in Java AES-256-CBC 在 java 中用 key / IV 加密,在 OpenSSL 中解密 - AES-256-CBC encrypt with key / IV in java and decrypt in OpenSSL 使用Java使用提供的密钥和iv解密openssl aes-256-cbc - Using Java to decrypt openssl aes-256-cbc using provided key and iv 正在寻找用于解密使用 openssl -aes-256-cbc -a -salt 命令加密的消息的 Java 实现? - Looking for Java implementation for decrypting a message encrypted using openssl -aes-256-cbc -a -salt command? 如何使用AES解密用openssl命令加密的Java文件? - How to decrypt file in Java encrypted with openssl command using AES? 解密AES 256 CBC JavaScript - Decrypt AES 256 CBC javascript 我可以只解密用Java中的AES / CBC加密的文件的一部分吗? - Can I decrypt only part of file encrypted with AES/CBC in Java? 如何使用 MySQL AES_DECRYPT 解密使用 BouncyCastles PBEWITHSHA256AND256BITAES-CBC-BC 算法加密的数据 - How to use MySQL AES_DECRYPT to decrypt data encrypted with BouncyCastles PBEWITHSHA256AND256BITAES-CBC-BC Algorithm
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM