[英]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.