AES-256-CBC在java中使用密钥/ IV加密,在OpenSSL中解密

zkure5ic  于 2022-12-02  发布在  Java
关注(0)|答案(1)|浏览(574)

我正在努力使用openssl和aes加密。我需要在java中加密一个文件,同时强制使用密钥和IV值,而不是使用密码。它必须用下面的命令解密:

openssl aes-256-cbc -d -K $KEY_VALUE -iv $IV -in hello.txt.ssl -out hello-clear.txt

遗憾的是,我不能改变这个命令,这是客户的要求。
我设法用java加密了文件(见下面的代码),但是用openssl解密时出现了这个错误

hex string is too short, padding with zero bytes to length
hex string is too short, padding with zero bytes to length
bad decrypt
40874B28DD7F0000:error:1C800064:Provider routines:ossl_cipher_unpadblock:bad decrypt:providers/implementations/ciphers/ciphercommon_block.c:124:

你知道我在加密过程中漏掉了什么吗?或者有什么其他的方法吗?

  • 以下是加密和生成解密命令的完整java示例:*
import org.apache.commons.io.IOUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;

public class OpenSslEncryptor {

    public static void main(String... args) throws Exception {

        int keyLength = 256;
        int keyLengthInBit = keyLength / 8;

        File baseFile = new File("hello.txt");
        IOUtils.write("hello openssl !", new FileOutputStream(baseFile), StandardCharsets.UTF_8);
        byte[] inBytes = new FileInputStream(baseFile).readAllBytes();

        String keyValue = generateRandom(40);
        String iv = generateRandom(16);
        byte[] keyValueB = Arrays.copyOfRange(keyValue.getBytes(), 0, keyLengthInBit);
        byte[] ivB = Arrays.copyOfRange(iv.getBytes(), 0, 16);

        final SecretKeySpec key = new SecretKeySpec(keyValueB, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivB));

        byte[] data = cipher.doFinal(inBytes);

        File outputFile = new File(baseFile.getAbsolutePath() + ".ssl");
        IOUtils.write(data, new FileOutputStream(outputFile));

        String decryptCommand = "openssl aes-256-cbc -d -K " + keyValue
                + " -iv " + iv
                + " -in " + outputFile.getName()
                + " -out hello-clear.txt";

        System.out.println(decryptCommand);
    }

    private static String generateRandom(int length) {
        SecureRandom secureRandom = new SecureRandom();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            builder.append(Integer.toHexString(secureRandom.nextInt(16)));
        }
        return builder.toString();
    }
}

下面是命令行encrypt with openssl,如果有人想要的话

openssl aes-256-cbc -e -K $KEY_VALUE -iv $IV -in hello.txt -out hello.txt.ssl
q8l4jmvw

q8l4jmvw1#

generateRandom()方法返回十六进制编码的数据。当 y 字节的序列是十六进制编码时,它由 2y* 个十六进制数字组成。因此,生成的密钥和IV必须具有所需长度的两倍(64用于AES-256的32字节密钥,32用于AES的16字节IV)。然后,这些可以被十六进制解码以用于加密,例如,用HexFormat.of().parseHex()

String keyValue = generateRandom(64); 
String iv = generateRandom(32); 
byte[] keyValueB = HexFormat.of().parseHex(keyValue);
byte[] ivB = HexFormat.of().parseHex(iv);

然而,总的来说,generateRandom()中的随机数据的生成有些麻烦,并且可以被重构,例如:

byte[] keyValueB = generateRandom(32);
byte[] ivB = generateRandom(16);
String keyValue = HexFormat.of().formatHex(keyValueB);
String iv = HexFormat.of().formatHex(ivB);
...
private static SecureRandom secureRandom = new SecureRandom();
private static byte[] generateRandom(int lengthInBytes) {
    byte[] data = new byte[lengthInBytes];
    secureRandom.nextBytes(data);
    return data;
}

现在generateRandom()直接生成所需的数据byte[],然后可以对这些数据进行十六进制编码,例如HexFormat.of().formatHex()

相关问题