如何使用AES解密用openssl命令加密的Java文件?

我需要使用以下命令在JAVA中解密在UNIX中加密的文件:

openssl aes-256-cbc -a -salt -in password.txt -out password.txt.enc

mypass

mypass

我必须像在UNIX中一样在Java中解密

openssl aes-256-cbc -d -a -in password.txt.enc -out password.txt.new

mypass

有人可以给我一个Java代码来执行此操作吗?

回答:

OpenSSL通常使用自己的基于密码的密钥派生方法,该方法在中指定EVP_BytesToKey,请参见下面的代码。此外,它会在多行中隐式地将密文编码为base 64,以便在邮件正文中发送密文。

因此,结果是伪代码:

salt = random(8)

keyAndIV = BytesToKey(password, salt, 48)

key = keyAndIV[0..31]

iv = keyAndIV[32..47]

ct = AES-256-CBC-encrypt(key, iv, plaintext)

res = base64MimeEncode("Salted__" | salt | ct))

因此解密为:

(salt, ct) = base64MimeDecode(res)

key = keyAndIV[0..31]

iv = keyAndIV[32..47]

pt = AES-256-CBC-decrypt(key, iv, plaintext)

可以像这样在Java中实现:

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

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,

args[ARG_INDEX_PASSWORD].getBytes(ASCII),

ITERATIONS);

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

}

}

}

请注意,该代码将ASCII指定为字符集。对于你的应用程序/终端/操作系统,所使用的字符集可能有所不同。

通常,你应该强制OpenSSL使用NIST批准的PBKDF2算法,因为使用OpenSSL密钥派生方法(迭代计数为1)是不安全的。这可能会迫使你使用与OpenSSL不同的解决方案。请注意,基于密码的加密从本质上讲是不安全的-密码比随机生成的对称密钥安全性低得多。

OpenSSL 1.1.0c更改了某些内部组件中使用的摘要算法。以前使用的是MD5,而1.1.0切换到SHA256。小心的变化不影响你在这两个EVP_BytesToKey和命令状openssl enc

最好在命令行界面中显式指定摘要(例如,-md md5为了向后兼容或sha-256向前兼容),并确保Java代码使用相同的摘要算法("MD5""SHA-256"包括破折号)。另请参阅此答案中的信息。

以上是 如何使用AES解密用openssl命令加密的Java文件? 的全部内容, 来源链接: utcz.com/qa/429100.html

回到顶部