简体   繁体   English

使用 BouncyCastle SSL 使用 keyFile 进行 Java AES 加密

[英]Java AES encryption with keyFile using BouncyCastle SSL

I am trying to convert the below Command in SSL to Java我正在尝试将 SSL 中的以下命令转换为 Java

openssl enc -in <inputfilename> -out <file_to_encrypt> -e -aes256 -k s_key

s_key is the file provided which contains the key that will be used to encrypt and decrypt s_key 是提供的文件,其中包含将用于加密和解密的密钥

Steps to be done: 1 - Read the key file 2 - Use it to AES encryption to encrypt file inputfilename 3 - Use the key to decrypt the same.要完成的步骤: 1 - 读取密钥文件 2 - 使用它进行 AES 加密以加密文件 inputfilename 3 - 使用密钥解密相同。

I am new to encryption and below is the code i have written so far to encrypt but I am getting issue.我是加密新手,下面是我迄今为止编写的加密代码,但我遇到了问题。

Path path = Paths.get("/home/debashishd/Downloads/s_key");
String content = new String(Files.readAllBytes(Paths.get("/home/debashishd/Downloads/s_key")));
    
String Test_message = "Hello this is Roxane";
    
byte[] keyValue = Files.readAllBytes(path);
ByteArrayInputStream byteIS = new ByteArrayInputStream(keyValue);
    
OpenSSLPBEParametersGenerator gen = new OpenSSLPBEParametersGenerator();
OpenSSLPBEParametersGenerator gen1 = gen;
byte[] saltBytes = Hex.decode(salt.getBytes());
gen1.init(keyValue);
CipherParameters cp = gen1.generateDerivedParameters(256);

byte[] keyBytes = ((KeyParameter)cp);           
SecretKeySpec secretKey = new SecretKeySpec(keyBytes,"AES");
System.out.println(secretKey);
    
Cipher cipher;
Cipher decryptCipher;
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey,new IvParameterSpec(new byte[16]));

String encrypt_value = getEncoder().encodeToString(cipher.doFinal(Test_message.getBytes(StandardCharsets.UTF_8)));
    
System.out.println("Encrypted value: " + encrypt_value);
    
decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey,new IvParameterSpec(new byte[16]));
String Decrypt_result = new String(decryptCipher.doFinal(getDecoder().decode(encrypt_value)));
System.out.println("Decrypted value: " + Decrypt_result);

Is there any changes need to be done to achieve the above encrypt and decrypt是否需要进行任何更改才能实现上述加密和解密

Expected output:预期输出:

Encrypted value: jvggHDPa58+/zQ+HyGUEk/ypndXbatE+b+hBBqiinABOIwxJ7FXqnDb5j813fPwwm/D6d2Y2uh+k4qD77QMqOg==
Decrypted value: Hello this is Roxane

For compatibility with the OpenSSL statement:为了与 OpenSSL 语句兼容:

  • a random 8 bytes of salt must be generated必须生成随机的 8 字节盐
  • a 32 bytes key and 16 bytes IV must be derived using EVP_BytesToKey() and the salt必须使用EVP_BytesToKey()和 salt 导出 32 字节密钥和 16 字节 IV
  • the result must be given in OpenSSL format:结果必须以 OpenSSL 格式给出:
    <ASCII Encoding of Salted__>|<salt>|<ciphertext>

For EVP_BytesToKey() you can apply the OpenSSLPBEParametersGenerator class you already suggested.对于EVP_BytesToKey()您可以应用您已经建议的OpenSSLPBEParametersGenerator类。

EVP_BytesToKey() uses a digest. EVP_BytesToKey()使用摘要。 In earlier versions of OpenSSL MD5 was applied by default, from v1.1.0 SHA256.从 v1.1.0 SHA256 开始,在早期版本的 OpenSSL 中默认应用 MD5。 The digest can be set with the -md5 option.可以使用-md5选项设置摘要。 Code and OpenSSL statement must both use the same digest to be compatible.代码和 OpenSSL 语句必须都使用相同的摘要才能兼容。 OpenSSLPBEParametersGenerator allows the specification of the digest in the constructor, default is MD5. OpenSSLPBEParametersGenerator允许在构造函数中指定摘要,默认为 MD5。

The following code, is based on your code, ie uses OpenSSLPBEParametersGenerator for EVP_BytesToKey() but additionally considers above points.以下代码基于您的代码,即对EVP_BytesToKey()使用OpenSSLPBEParametersGenerator但还考虑了以上几点。 Instead of encrypting the entire data, streams are applied and the data is encrypted chunk by chunk, so that even large files can be processed:不是加密整个数据,而是应用流并且数据被逐块加密,因此即使是大文件也可以处理:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.io.CipherOutputStream;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;

...

String inputPath = "...";
String outputPath = "...";
String passwordStr = "...";

// Generate random 8 bytes salt
SecureRandom random = new SecureRandom();
byte salt[] = new byte[8];
random.nextBytes(salt);

// Derive 32 bytes key (AES_256) and 16 bytes IV
byte[] password = passwordStr.getBytes(StandardCharsets.UTF_8);
OpenSSLPBEParametersGenerator pbeGenerator = new OpenSSLPBEParametersGenerator(new MD5Digest()); // SHA256 as of v1.1.0 (if in OpenSSL the default digest is applied)
pbeGenerator.init(password, salt);
ParametersWithIV parameters = (ParametersWithIV) pbeGenerator.generateDerivedParameters(256, 128); // keySize, ivSize in bits

// Encrypt with AES-256, CBC using streams
try (FileOutputStream fos = new FileOutputStream(outputPath)) {

    // Apply OpenSSL format
    fos.write("Salted__".getBytes(StandardCharsets.UTF_8));
    fos.write(salt);

    // Encrypt chunkwise (for large data)
    PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
    cipher.init(true, parameters);
    try (FileInputStream fis = new FileInputStream(inputPath);
         CipherOutputStream cos = new CipherOutputStream(fos, cipher)) {
        int bytesRead = -1;
        byte[] buffer = new byte[64 * 1024 * 1024]; // chunksize, e.g. 64 MiB
        while ((bytesRead = fis.read(buffer)) != -1) {
            cos.write(buffer, 0, bytesRead);
        }    
    }
}

A file encrypted with this code can be decrypted with OpenSSL as follows:使用此代码加密的文件可以使用 OpenSSL 解密,如下所示:

openssl enc -d -aes256 -k <passpharse> -in <enc file> -out <dec file>

Therefore the code is the programmatic analogue of the OpenSSL statement posted at the beginning of your question (whereby the ambiguity regarding the digest still has to be taken into account, ie for an OpenSSL version from v1.1.0 SHA256 has to be used instead of MD5).因此,该代码是您问题开头发布的 OpenSSL 语句的编程模拟(因此仍然必须考虑关于摘要的歧义,即对于 v1.1.0 的 OpenSSL 版本,必须使用 SHA256 而不是 MD5 )。

Note that because of the random salt, different key/IV pairs are generated for each encryption, so there is no reuse, which also removes the vulnerability mentioned in the comment.注意因为随机盐,每次加密都会生成不同的key/IV对,所以没有复用,这也去掉了评论中提到的漏洞。

I was able to achive my result with the below code.我能够用下面的代码实现我的结果。 The code can indeed be written in a better way and remove the unnecessary initialisation.确实可以用更好的方式编写代码并删除不必要的初始化。 I will be doing the same in my practical Implementation我将在我的实际实施中做同样的事情

public class test2 {
    public static void main(String[] args) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {

        Path path = Paths.get("/home/debashishd/Downloads/key");
        String Test_message = FileUtils.readFileToString(new File("/home/debashishd/Downloads/sample.txt"), StandardCharsets.UTF_8);

        /**
         * Read the keyfile provided to encrypt
         */
        byte[] keyValue = Files.readAllBytes(path);
        OpenSSLPBEParametersGenerator gen = new OpenSSLPBEParametersGenerator();
        OpenSSLPBEParametersGenerator gen1 = gen;
        final String salt = "";
        byte[] saltBytes = Hex.decode(salt.getBytes());
        gen1.init(keyValue,saltBytes);
        CipherParameters cp = gen1.generateDerivedParameters(256);
        byte[] keyBytes = ((KeyParameter)cp).getKey();
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes,"AES");

        /**
         * Initialize the Encrypt Ciphers and encrypt the data from input file
         */
        Cipher encryptcipher;
        Cipher decryptCipher;
        encryptcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        encryptcipher.init(Cipher.ENCRYPT_MODE, secretKey,new IvParameterSpec(new byte[16]));
        String encrypt_value = getEncoder().encodeToString(encryptcipher.doFinal(Test_message.getBytes(StandardCharsets.UTF_8)));
        System.out.println("Encrypted value: " + encrypt_value);

        /**
         * Initialize the Decrypt Ciphers and decrypt the encrypted data
         */
        decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        decryptCipher.init(Cipher.DECRYPT_MODE, secretKey,new IvParameterSpec(new byte[16]));
        String Decrypt_result = new String(decryptCipher.doFinal(getDecoder().decode(encrypt_value)));
        System.out.println("Decrypted value: " + Decrypt_result);

    }
}

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM