java - 尝试解密基于 AES 的加密文本时出现 BadPadding 异常
问题描述
问题场景: 我在 Ubuntu 中使用 OpenSSL AES-256-CBC 模式创建加密文件。
openssl aes-256-cbc -a -in avengers.txt -out avengers.enc
test
test
File Content: avengersAssemble
avengers.enc file content: U2FsdGVkX194TyUFrb4gOn86XYaKjKP98YdOlQDJz+t/76mvVmNKl+NyKKUnYwYH
To Decrypt: openssl aes-256-cbc -a -d -in avengers.enc
现在我想avengers.enc
使用java代码解密这个加密文件,即我只存储这个文件内容和密码(在这种情况下是测试)来解密它。
我的目标:我想使用从上述命令(128 或 192 或 256 cbc)加密的密码(对称加密)解密。
请帮忙。
我找到了这段代码,但它显示了 BadPadding 异常。请参考@Maarten Bodewes
链接中的答案。
我从链接中找到的代码:
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.util.encoders.Base64;
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 = 1;
private static final int ARG_INDEX_FILENAME = 0;
private static final int ARG_INDEX_PASSWORD = 1;
private static final String file =
"D:\\Eclipse_Workspace\\Cryptography\\avengers.enc";
private static final String password = "test";
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 void main(String[] args) {
try {
// --- read base 64 encoded file ---
//File f = new File(args[ARG_INDEX_FILENAME]);
File f = new File(file);
List<String> lines = Files.readAllLines(f.toPath(), ASCII);
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line.trim());
}
String dataBase64 = sb.toString();
byte[] headerSaltAndCipherText = Base64.decode(dataBase64);
// --- extract salt & encrypted ---
// header is "Salted__", ASCII encoded, if salt is being used (the default)
byte[] salt = Arrays.copyOfRange(
headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
byte[] encrypted = Arrays.copyOfRange(
headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
// --- specify cipher and digest for EVP_BytesToKey method ---
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
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,
password.getBytes(ASCII),
ITERATIONS); //args[ARG_INDEX_PASSWORD]
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
// --- initialize cipher instance and decrypt ---
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
String answer = new String(decrypted, ASCII);
System.out.println(answer);
} catch (BadPaddingException e) {
// AKA "something went wrong"
throw new IllegalStateException(
"Bad password, algorithm, mode or padding;" +
" no salt, wrong number of iterations or corrupted ciphertext.");
} catch (IllegalBlockSizeException e) {
throw new IllegalStateException(
"Bad algorithm, mode or corrupted (resized) ciphertext.");
} catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
例外:
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:991)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at symmetric.main(symmetric.java:146)
解决方案
OpenSSL
使用EVP_BytesToKey
with 使用 Message Digest 算法,如md5
,sha256
等。在上面的代码中,函数EVP_ByteToKey
有一个 MessageDigest 参数,MD5
但在加密时未在 OpenSSL 命令中添加 MD5。下面是与消息摘要算法(即 MD5)一起使用的实际命令。
openssl aes-256-cbc -a -in avengers.txt -out avengers.enc -md md5
openssl aes-256-cbc -a -d -in avengers.enc -md md5
MessageDigest md5 = MessageDigest.getInstance("SHA-256");
要使用任何其他消息摘要算法,只需在消息摘要算法下的引用链接中传递该 算法。此外,必须传递-md sha256
命令。请参阅 openSSL 的手册页
推荐阅读
- elasticsearch - Flink with Elasticsearch6.0.0 Sink NoSuchMethodError BulkProcessor.builder
- weblogic - 我们可以限制访问 weblogic 应用程序的用户数量吗?
- angular - x 轴上的长文本被切割成条形图 ng2-charts
- innertext - 如何使用Javascript获取表格中的单元格值
- javascript - 如何在 ionic 3 中检查设备中的每个目录
- c# - VS2008 Net 3.5 C# 不能包含 NAudio.csproj:“$”和“=>”的含义
- python - 从另一个文件中删除停用词
- c# - 在 WEB API C# 应用层中存储和访问 HTML 模板的最佳方式
- reactjs - React DnD 用于调整 HTML 表格列的大小
- angularjs - 如何在 angularjs 中创建导航栏/左导航/sidenav,其中每个导航显示来自 json 文件的数据