I am trying to convert the below Command in SSL to 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
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.
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
CodePudding user response:
For compatibility with the OpenSSL statement:
- a random 8 bytes of salt must be generated
- a 32 bytes key and 16 bytes IV must be derived using
EVP_BytesToKey()
and the salt - the result must be given in OpenSSL format:
<ASCII Encoding of Salted__>|<salt>|<ciphertext>
For EVP_BytesToKey()
you can apply the OpenSSLPBEParametersGenerator
class you already suggested.
EVP_BytesToKey()
uses a digest. In earlier versions of OpenSSL MD5 was applied by default, from v1.1.0 SHA256. The digest can be set with the -md5 option. Code and OpenSSL statement must both use the same digest to be compatible. OpenSSLPBEParametersGenerator
allows the specification of the digest in the constructor, default is MD5.
The following code, is based on your code, i.e. uses OpenSSLPBEParametersGenerator
for EVP_BytesToKey()
but additionally considers above points. 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 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, i.e. for an OpenSSL version from v1.1.0 SHA256 has to be used instead of 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.
CodePudding user response:
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);
}
}