Java AES加密与keyFile使用BouncyCastle SSL



我正在尝试将以下命令在SSL转换为Java

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

s_key是提供的文件,其中包含将用于加密和解密的密钥

需要完成的步骤:1 -读取密钥文件2 -使用AES加密方式加密文件inputfilename3 -使用相同的关键解密。

我是加密新手,下面是我迄今为止编写的加密代码,但我遇到了问题。

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

是否需要做任何更改来实现上述加密和解密

预期输出:

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

为了与OpenSSL语句兼容:

  • 必须随机生成8字节的盐
  • 一个32字节的key和16字节的IV必须使用EVP_BytesToKey()和salt导出

  • <ASCII Encoding of Salted__>|<salt>|<ciphertext>

对于EVP_BytesToKey(),您可以应用您已经建议的OpenSSLPBEParametersGenerator类。

EVP_BytesToKey()使用摘要。在早期版本的OpenSSL中,从v1.1.0 SHA256开始默认应用MD5。可以使用-md5选项设置摘要。代码和OpenSSL语句必须使用相同的摘要才能兼容。OpenSSLPBEParametersGenerator允许在构造函数中指定摘要,默认是MD5。

下面的代码,是基于你的代码,即使用OpenSSLPBEParametersGeneratorEVP_BytesToKey(),但额外考虑以上几点。不是加密整个数据,而是应用流并逐块加密数据,因此即使是大文件也可以处理:

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

使用此代码加密的文件可以使用OpenSSL进行解密,如下所示:

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

因此,代码是在你的问题开始时发布的OpenSSL语句的编程模拟(因此,关于摘要的模糊性仍然必须考虑在内,即对于v1.1.0的OpenSSL版本必须使用SHA256而不是MD5)。

请注意,由于随机盐,每次加密都会生成不同的密钥/IV对,因此没有重用,这也消除了评论中提到的漏洞。

我能够用下面的代码实现我的结果。代码确实可以用更好的方式编写,并删除不必要的初始化。我将在我的实际实现中做同样的事情

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

最新更新