I referred this and trying to do file decryption with base64 decoding
My requirement is to encode data with base64 during encryption and decode data with base64 during decryption.
But im facing below error:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at java.base/com.sun.crypto.provider.CipherCore.doFinal(Unknown Source)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(Unknown Source)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(Unknown Source)
at java.base/javax.crypto.Cipher.doFinal(Unknown Source)
at aes.DecryptNew.decryptNew(DecryptNew.java:124)
at aes.DecryptNew.main(DecryptNew.java:32)
Also im confused on how to perform decryption in chunks.
Please suggest me issues in this code.
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
public class DecryptNew {
public static void main(String[] args) {
String plaintextFilename = "D:\\\\plaintext.txt";
String ciphertextFilename = "D:\\\\plaintext.txt.crypt";
String decryptedtextFilename = "D:\\\\plaintextDecrypted.txt";
String password = "testpass";
writeToFile();
String ciphertext = encryptfile(plaintextFilename, password);
System.out.println("ciphertext: " + ciphertext);
decryptNew(ciphertextFilename, password, decryptedtextFilename);
}
static void writeToFile() {
BufferedWriter writer = null;
try
{
writer = new BufferedWriter( new FileWriter("D:\\\\plaintext.txt"));
byte[] data = Base64.getEncoder().encode("hello\r\nhello".getBytes(StandardCharsets.UTF_8));
writer.write(new String(data));
}
catch ( IOException e)
{
}
finally
{
try
{
if ( writer != null)
writer.close( );
}
catch ( IOException e)
{
}
}
}
public static String encryptfile(String path, String password) {
try {
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));
final byte[] pass = Base64.getEncoder().encode(password.getBytes());
final byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(Base64.getEncoder().encode("Salted__".getBytes()));
fos.write(salt);
final byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = concatenateByteArrays(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("SHA-1");
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
cos.flush();
cos.close();
fis.close();
System.out.println("encrypt done " + path);
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return path;
}
static void decryptNew(String path,String password, String outPath) {
byte[] SALTED_MAGIC = Base64.getEncoder().encode("Salted__".getBytes());
try{
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(outPath);
final byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
final byte[] inBytes = Files.readAllBytes(Paths.get(path));
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1");
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
String contentDecoded = new String(Base64.getDecoder().decode(clear));
fos.write(contentDecoded.getBytes());
fos.close();
System.out.println("Decrypt is completed");
}catch(Exception e){
e.printStackTrace();
}
}
public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
return ByteBuffer
.allocate(a.length + b.length)
.put(a).put(b)
.array();
}
}
As mentioned in my first comment: Encryption and decryption use different encodings for the password (Base64 in encryption, ASCII in decryption).
In addition the prefix is Base64 encoded in both encryption and decryption, so prefix plus salt are larger than one block (16 bytes), and therefore the subsequent length determination of the ciphertext during decryption fails, as it is assumed that the ciphertext starts with the 2nd block.
Both issues can be solved if the same encoding is used for the password in encryption and decryption (e.g. ASCII) and the prefix is ASCII encoded, e.g. for encryption:
...
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(SALTED_MAGIC);
fos.write(salt);
...
and for decryption:
...
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] prefix = fis.readNBytes(8);
byte[] salt = fis.readNBytes(8);
...
However the current encryption method does not Base64 encode (only the prefix is Base64 encoded which is rather counterproductive, s. above).
Even a Base64 encoded plaintext does not change this, as the ciphertext itself is not Base64 encoded.
Considering your statement My requirement is to encode data with base64 during encryption and decode data with base64 during decryption and the OpenSSL format used, I assume that you want to decrypt a ciphertext that has been encrypted with the -base64 option, analogous to OpenSSL, i.e. with
openssl enc -aes-256-cbc -base64 -pass pass:testpass -p -in sample.txt -out sample.crypt
Here, prefix, salt and ciphertext are concatenated on byte level and the result is then Base64 encoded. To achieve this, your implementation posted in the question could be changed as follows:
static void decrypt(String path, String password, String outPath) {
try (FileInputStream fis = new FileInputStream(path);
Base64InputStream bis = new Base64InputStream(fis, false, 64, "\r\n".getBytes(StandardCharsets.US_ASCII))) { // from Apache Commons Codec
// Read prefix and salt
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] prefix = bis.readNBytes(8);
if (!Arrays.equals(prefix, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
byte[] salt = bis.readNBytes(8);
// Derive key and IV
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1"); // Use digest from encryption
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
// Decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
try (CipherInputStream cis = new CipherInputStream(bis, cipher);
FileOutputStream fos = new FileOutputStream(outPath)) {
int length;
byte[] buffer = new byte[1024];
while ((length = cis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
System.out.println("Decrypt is completed");
} catch (Exception e) {
e.printStackTrace();
}
}
As I already mentioned in the comments to the linked question, the processing in chunks can easily be implemented with the class CipherInputStream. The Base64 decoding can be achieved with the class Base64InputStream of the Apache Commons Codec, as also addressed by Michael Fehr. This is a convenient way to perform Base64 decoding and decryption together. If the data does not need to be Base64 decoded (e.g. if the -base64 option was not used during encryption) the Base64InputStream class can simply be omitted.
As previously stated in the comments, small files do not need to be processed in chunks. This is only necessary when the files become large relative to the memory. However, since your encryption method processes the data in chunks, it is consistent that the decryption does the same.
Note that the encryption posted in the question is not compatible with the result of the above OpenSSL statement, i.e. the encryption would have to be adapted if necessary (analogous to the decryption posted above).
Edit: The encryptfile() method posted in the question creates a file containing the Base64 encoded prefix, the raw salt and the raw ciphertext. For key derivation the Base64 encoded password is applied. The method is used to encrypt a Base64 encoded plaintext. The following method is the counterpart of encryptfile() and allows the decryption and Base64 decoding of the plaintext:
static void decryptfile(String path, String password, String outPath) {
try (FileInputStream fis = new FileInputStream(path)) {
// Read prefix and salt
byte[] SALTED_MAGIC = Base64.getEncoder().encode("Salted__".getBytes());
byte[] prefix = new byte[SALTED_MAGIC.length];
fis.readNBytes(prefix, 0, prefix.length);
if (!Arrays.equals(prefix, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
byte[] salt = new byte[8];
fis.readNBytes(salt, 0, salt.length);
// Derive key and IV
final byte[] pass = Base64.getEncoder().encode(password.getBytes());
byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1"); // Use digest from encryption
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
// Decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
try (CipherInputStream cis = new CipherInputStream(fis, cipher);
Base64InputStream bis = new Base64InputStream(cis, false, -1, null); // from Apache Commons Codec
FileOutputStream fos = new FileOutputStream(outPath)) {
int length;
byte[] buffer = new byte[1024];
while ((length = bis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
System.out.println("Decrypt is completed");
} catch (Exception e) {
e.printStackTrace();
}
}
The decryptfile()-method writes the Base64 decoded plaintext to the file in outPath. To get the Base64 encoded plaintext simply remove the Base64InputStream instance from the stream hierarchy.
As already explained in the comment, this method is incompatible to OpenSSL, i.e. it can't decrypt ciphertexts generated with the OpenSSL statement posted above (with or without -base64 option). To decrypt a ciphertext generated with OpenSSL with -base64 option use the decrypt() method.
Related
I want print specific index of an Array of split text but it gives error, some one can help please
TEXT.txt contains A,B,3,D,5,1
FileReader Alphabets_and_Numbers = new FileReader("TEXT.txt");
Scanner read = new Scanner(Alphabets_and_Numbers);
read.useDelimiter(",\\s*");
while(read.hasNext()) {
String data = read.next();
String[] Alphabets = data.split("\\d");
/*
A
B
D
*/
String[] Numbers = data.split("[^0-6]");
/*
3
5
1
*/
when printed with for loop is Ok it prints ABD
for(int c = 0; c<Alphabets.length; c++)
{
System.out.println(Alphabets[c]);
}
while to print single Element D, it gives
ArrayIndexOutOfBoundsException: 2
System.out.println(Alphabets[2]);
}
Your code is using a random salt for the password deriving but the salt is never stored so when later decrypting (in a second run) you don't have the salt again.
Second - there is a similar issue with the initialization vector that is used for AES encryption. You don't define one on encryption side so Java is (internally)
generating one and does the encryption. Now you have to grab this iv from the cipher and save it as well for later decryption. As you don't provide the iv you run
into the exception "Missing parameter type: IV expected".
As I'm too lazy to check your code you find below the code (provided by #Topaco, all credits go to him ! link: https://stackoverflow.com/a/63719246/8166854) for
a complete file encryption / decryption that is suitable even for larger files as it uses buffered CipherInput-/OutputStreams.
Security warning: the codehas no exception handling and is for educational purpose only!.
code:
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.SecureRandom;
public class MainFileEncryption {
public static void main(String[] args) throws Exception {
System.out.println("File encryption with PBEWithHmacSHA256AndAES_256");
String uncryptedFilename = "plaintext.txt";
String encryptedFilename = "plaintext.enc";
String decryptedFilename = "decrypted.txt";
String password = "mySecretPassword";
byte[] iv = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(iv);
encrypt(password, iv, uncryptedFilename, encryptedFilename);
decrypt(password, encryptedFilename, decryptedFilename);
}
// by Topaco https://stackoverflow.com/a/63719246/8166854
private static void encrypt(String key, byte[] initVector, String inputFile, String outputFile) throws Exception {
// Key
PBEKeySpec pbeKeySpec = new PBEKeySpec(key.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
// IV
IvParameterSpec iv = new IvParameterSpec(initVector);
// Salt
SecureRandom rand = new SecureRandom();
byte[] salt = new byte[16];
rand.nextBytes(salt);
// ParameterSpec
int count = 10000;
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count, iv);
// Cipher
Cipher cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
try (FileInputStream fin = new FileInputStream(inputFile);
FileOutputStream fout = new FileOutputStream(outputFile);
CipherOutputStream cipherOut = new CipherOutputStream(fout, cipher)) {
// Write IV, Salt
fout.write(initVector);
fout.write(salt);
// Encrypt
final byte[] bytes = new byte[1024];
for (int length = fin.read(bytes); length != -1; length = fin.read(bytes)) {
cipherOut.write(bytes, 0, length);
}
}
}
private static void decrypt(String key, String inputFile, String outputFile) throws Exception {
try (FileInputStream encryptedData = new FileInputStream(inputFile);
FileOutputStream decryptedOut = new FileOutputStream(outputFile)) {
// Key
PBEKeySpec pbeKeySpec = new PBEKeySpec(key.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
// Read IV
byte[] initVect = new byte[16];
encryptedData.read(initVect);
IvParameterSpec iv = new IvParameterSpec(initVect);
// Read salt
byte[] salt = new byte[16];
encryptedData.read(salt);
// ParameterSpec
int count = 10000;
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count, iv);
// Cipher
Cipher cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
cipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
try (CipherInputStream decryptStream = new CipherInputStream(encryptedData, cipher)) {
// Decrypt
final byte[] bytes = new byte[1024];
for (int length = decryptStream.read(bytes); length != -1; length = decryptStream.read(bytes)) {
decryptedOut.write(bytes, 0, length);
}
}
}
}
}
Im trying to implement file decryption by referring this code:
Encryption part i have done this way:
https//stackoverflow.com/questions/64423926/encrypt-file-in-java-and-decrypt-in-openssl-with-key-aes
And the encrypted file im able to decrypt with openssl.
But the decrypt to file in java is causing error:
java.lang.IllegalArgumentException: Illegal base64 character 5f
at java.util.Base64$Decoder.decode0(Unknown Source)
at java.util.Base64$Decoder.decode(Unknown Source)
at java.util.Base64$Decoder.decode(Unknown Source)
at aes.AesEncryptTask.decryptNew(AesEncryptTask.java:107)
at aes.AesEncryptTask.main(AesEncryptTask.java:58)
Content in my encrypted file is:
Salted__¨‹–1ž#¡ð=—ÖÏùá•NEÄ
Note: Starting salted part is not base64encoded. following data is encoded.
Please suggest on correct implementation of file decryption.
static void decryptNew(String path,String password, String outPath) {
try{
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(outPath);
final byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
//final byte[] inBytes = Base64.getDecoder().decode(source);
String source = getFileContent(fis);
final Decoder decoder = Base64.getDecoder();
final byte[] inBytes = decoder.decode(source);
//final byte[] inBytes =source.getBytes();//DatatypeConverter.parseBase64Binary(source);
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
String contentDecoded = new String(clear, StandardCharsets.UTF_8);
fos.write(contentDecoded.getBytes());
fos.close();
//cipher.init(Cipher.DECRYPT_MODE, sks);
/*CipherInputStream cis = new CipherInputStream(fis, cipher);
int b;
byte[] d = new byte[8];
while((b = cis.read(d)) != -1) {
fos.write(d, 0, b);
}
fos.flush();
fos.close();
cis.close();*/
System.out.println("Decrypt is completed");
}catch(Exception e){
e.printStackTrace();
}
}
The below code is doing a complete file encryption and decryption and is compatible to the OpenSSL commands
encrypt: openssl enc -aes-256-cbc -pass pass:testpass -d -p -in plaintext.txt -out plaintext.txt.crypt -md md5
decrypt: openssl aes-256-cbc -d -in plaintext.txt.crypt -out plaintext1.txt -k testpass -md md5
I left out some variables as they are not used in decryption method and on the other hand substituded the byte[]
concatination with a pure Java version.
The simple output is:
encrypt done plaintext.txt
ciphertext: plaintext.txt
Decrypt is completed
Security warning: the key derivation is DEPRECATED and should not used any longer. The code does not have any exception handling and is for educational purpose only.
code:
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.Arrays;
public class MainSo {
public static void main(String[] args) {
System.out.println("https://stackoverflow.com/questions/64443373/decrypt-aes-encrypted-file-in-java-sha1-openssl?noredirect=1#comment113960588_64443373");
String plaintextFilename = "plaintext.txt";
String ciphertextFilename = "plaintext.txt.crypt";
String decryptedtextFilename = "plaintextDecrypted.txt";
String password = "testpass";
// openssl equivalent:
// decrypt: openssl aes-256-cbc -d -in plaintext.txt.crypt -out plaintext1.txt -k testpass -md md5
// encrypt: openssl enc -aes-256-cbc -pass pass:testpass -d -p -in sample.crypt -out sample.txt -md md5
String ciphertext = encryptfile(plaintextFilename, password);
System.out.println("ciphertext: " + ciphertext);
decryptNew(ciphertextFilename, password, decryptedtextFilename);
}
public static String encryptfile(String path, String password) {
try {
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));
final byte[] pass = password.getBytes(StandardCharsets.UTF_8);
final byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write("Salted__".getBytes(StandardCharsets.UTF_8));
fos.write(salt);
final byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = concatenateByteArrays(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
cos.flush();
cos.close();
fis.close();
System.out.println("encrypt done " + path);
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return path;
}
static void decryptNew(String path,String password, String outPath) {
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.UTF_8);
try{
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(outPath);
final byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
final byte[] inBytes = Files.readAllBytes(Paths.get(path));
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
String contentDecoded = new String(clear, StandardCharsets.UTF_8);
fos.write(contentDecoded.getBytes());
fos.close();
System.out.println("Decrypt is completed");
}catch(Exception e){
e.printStackTrace();
}
}
public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
return ByteBuffer
.allocate(a.length + b.length)
.put(a).put(b)
.array();
}
}
I am trying to decrypt files by Java and below codes work. However it will have OutOfMemory exception when decrypting large files. I tried to change to cipher.update but the program will freeze without any responding.
How can I change this from doFinal to update?
public File decryptDataFile(File inputFile, File outputFile, File keyFile, String correlationId) {
try {
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
byte[] byteInput = this.getFileInBytes(inputFile);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
byte[] salt = new byte[8];
System.arraycopy(byteInput, 8, salt, 0, 8);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
cipher.init(Cipher.DECRYPT_MODE, fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100)));
byte[] data = cipher.doFinal(byteInput, 16, byteInput.length-16);
OutputStream os = new FileOutputStream(outputFile);
os.write(data);
os.close();
if(outputFile.exists()) {
return outputFile;
} else {
return null;
}
} catch (IOException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException | InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException e) {
logger.WriteLog(appConfig.getPlatform(), "INFO", alsConfig.getProjectCode(), correlationId, alsConfig.getFunctionId(), "SCAN_DECRYPT", e.getClass().getCanonicalName() + " - " + e.getMessage() );
return null;
}
}
My non-working version:
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
byte[] byteInput = this.getFileInBytes(inputFile);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
byte[] salt = new byte[8];
System.arraycopy(byteInput, 8, salt, 0, 8);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
cipher.init(Cipher.DECRYPT_MODE, fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100)));
FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile);
CipherInputStream cis = new CipherInputStream(fis, cipher);
int b;
byte[] d = new byte[8];
while((b = cis.read(d)) != -1) {
fos.write(d, 0, b);
}
fos.flush();
fos.close();
cis.close();
if(outputFile.exists()) {
return outputFile;
} else {
return null;
}
Foreword: I couldn't decrypt with your original method a file that was encrypted with your openssl-command
openssl enc -aes-256-cbc -e -salt -in ${tarFile} -out ${encTarFile} -pass file:./${KEY_RANDOM}
but the following method should decode even large files similar to your original method - I tested files up to 1 GB size.
Edit: Regarding the OpenSSL statement, it's worth mentioning that since v1.1.0 the default digest has changed from MD5 to SHA256,
so for higher versions the -md MD5 option must be set explicitly for compatibility with the Java code. (thanks to #Topaco).
Please keep in mind that I don't care about correct file paths for
new FileInputStream(inputFile.toPath().toString())
and
new FileOutputStream(outputFile.toPath().toString())
as I'm working locally and with my folder, maybe you have to change the code to "find" your files. As well there is no exception handling in this example.
The code line
byte[] ibuf = new byte[8096];
is defining the buffer that is used - a larger buffer makes the decryption faster but consumes more memory (8096 means 8096 byte compared to 1 Gbyte when reading the complete file into memory and causing the out of memory error).
public static File decryptDataFileBuffered(File inputFile, File outputFile, File keyFile, String correlationId) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
byte[] salt = new byte[8];
byte[] salted = new byte[8]; // text SALTED__
try (FileInputStream in = new FileInputStream(inputFile.toPath().toString()); // i don't care about the path as all is lokal
FileOutputStream out = new FileOutputStream(outputFile.toPath().toString())) // i don't care about the path as all is lokal
{
byte[] ibuf = new byte[8096]; // thats the buffer used - larger is faster
int len;
in.read(salted);
in.read(salt);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
SecretKey secretKey = fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100));
System.out.println("secretKey length: " + secretKey.getEncoded().length + " data: " + bytesToHex(secretKey.getEncoded()));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
while ((len = in.read(ibuf)) != -1) {
byte[] obuf = cipher.update(ibuf, 0, len);
if (obuf != null)
out.write(obuf);
}
byte[] obuf = cipher.doFinal();
if (obuf != null)
out.write(obuf);
} catch (IOException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
if (outputFile.exists()) {
return outputFile;
} else {
return null;
}
}
Edit2: As commented by #Topaco the usage of CipherInput/OutputStream shortens the code and makes it better readable, so here is the code:
public static File decryptDataFileBufferedCipherInputStream (File inputFile, File outputFile, File keyFile, String correlationId) throws
IOException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, InvalidKeyException
{
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(keyFile.toPath())).trim();
byte[] salt = new byte[8];
byte[] salted = new byte[8]; // text SALTED__
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFile.toPath().toString()); // i don't care about the path as all is lokal
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(outputFile.toPath().toString())) // i don't care about the path as all is lokal
{
byte[] buffer = new byte[8192];
in.read(salted);
in.read(salt);
SecretKeyFactory fact = SecretKeyFactory.getInstance("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC");
SecretKey secretKey = fact.generateSecret(new PBEKeySpec(key.toCharArray(), salt, 100));
System.out.println("secretKey length: " + secretKey.getEncoded().length + " data: " + bytesToHex(secretKey.getEncoded()));
cipher.init(Cipher.DECRYPT_MODE, secretKey);
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
if (outputFile.exists()) {
return outputFile;
} else {
return null;
}
}
I am looking for any sample java code that will decrypt the messages encrypted using "openssl enc -aes-256-cbc) -a -salt" command provided the key is known.
https://pastebin.com/YiwbCAW8
So far i was able to get the following java code that encrypts and also decrypts the message. But i am not able to decrypt the encrypted message using openssl command. Getting "Bad Magic Number" error. Any idea ?
Encrypt the message using the code >
Encrypt("sample text", "test$password") = "i+5zkPPgnDdV7fr/w8uHkw=="
Decrypt("i+5zkPPgnDdV7fr/w8uHkw==", "test$password") = "sample text"
Decrypt the message using openssl >
F:\cipher>echo i+5zkPPgnDdV7fr/w8uHkw== | openssl aes-256-cbc -a -salt -d
enter aes-256-cbc decryption password:
bad magic number
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
private static final byte[] SALT = {
(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
(byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
};
private static final int ITERATION_COUNT = 65536;
private static final int KEY_LENGTH = 256;
private Cipher ecipher;
private Cipher dcipher;
AES(String passPhrase) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, secret);
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
}
public String encrypt(String encrypt) throws Exception {
byte[] bytes = encrypt.getBytes("UTF8");
byte[] encrypted = encrypt(bytes);
return Base64.getEncoder().encodeToString(encrypted);
}
public byte[] encrypt(byte[] plain) throws Exception {
return ecipher.doFinal(plain);
}
public String decrypt(String encrypt) throws Exception {
byte[] bytes = Base64.getDecoder().decode(encrypt);
byte[] decrypted = decrypt(bytes);
return new String(decrypted, "UTF8");
}
public byte[] decrypt(byte[] encrypt) throws Exception {
return dcipher.doFinal(encrypt);
}
public static void main(String[] args) throws Exception {
String message = "sample text";
String password = "test$password";
AES encrypter = new AES(password);
String encrypted = encrypter.encrypt(message);
String decrypted = encrypter.decrypt(encrypted);
System.out.println("Encrypt(\"" + message + "\", \"" + password + "\") = \"" + encrypted + "\"");
System.out.println("Decrypt(\"" + encrypted + "\", \"" + password + "\") = \"" + decrypted + "\"");
}
}
You may search stackoverflow for many similar questions.
you have multiple issues in your code:
You use different keys:
In Java you use PBKDF2 to generate an encryption key from the provided password. Openssl uses its EVP_BytesToKey. Search internet for Java implementation. Please note the hash used in the EVP_BytesToKey changed with some openssl version (from MD5 to SHA-1 SHA-256), if someone is having more details, please comment
And you use random IV. you don't pass the IV along the ciphertext, so you may be able to decrypt the ciphertext with the same cipher instance (kkeping the same iv), but lets try your Java code to decrypt your ciphertext other time or with other instance, it won't work. You need to pass IV along the ciphertext (usually it's prepended)
Openssl expect following format:
Salted_<8 byte salt>ciphertext
Salted__<8 byte salt>ciphertext
8 byte salt is a random byte array used to generate the encryption key and IV from the provided password. Try encrypt with openssl with -pparameter, it will print the salt, IV and Key generated so you can check and compare
Using CBC without any integrity check (hmac, ..) may be unsafe in many implementations
Suggestions:
you can find an openssl java library implementing the same required (EVP_BytesToKey)
you can implement EVP_BytesToKey yourself
you can use openssl directly with -K/-ivparameters providing the encryption key and IV (in hex format) instead of password, then openssl expects pure ciphertext (no Salted_ or salt inside the input)
Thanks a lot for the clues. As mentioned, did some search and modified the code from one of the post. I have seen similar code with EVP_BytesToKeys in many places, but took some time to figure out the usage. I am able to decrypt the msg encrypted by openssl.
Trying to search the code for encryption as well. Meanwhile any help of encryption is appreciated as well.
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class AES5 {
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 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 here
*
*/
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 byte[][] getKeyIV(byte[] headerSaltAndCipherText, Cipher aesCBC, String password) {
byte[] salt = Arrays.copyOfRange(headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
byte[][] keyAndIV=null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
keyAndIV = EVP_BytesToKey(KEY_SIZE_BITS / Byte.SIZE, aesCBC.getBlockSize(), md5, salt,
password.getBytes(ASCII), ITERATIONS);
} catch (Exception e) {e.printStackTrace();}
return keyAndIV;
}
// https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes
public static String decrypt(String encryptedMsg, String password) {
String decryptedMsg =null;
byte[] headerSaltAndCipherText = Base64.decodeBase64(encryptedMsg);
byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
try {
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
final byte[][] keyAndIV = getKeyIV(headerSaltAndCipherText, aesCBC, password);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
decryptedMsg = new String(decrypted, ASCII);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
//TODO - Encrypt the msg in same manner as "openssl enc -aes-256-cbc -a -salt"
public static String encrypt(String msg, String password) {
String decryptedMsg =null;
byte[] headerSaltAndCipherText = Base64.decodeBase64(msg);
byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
try {
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
final byte[][] keyAndIV = getKeyIV(headerSaltAndCipherText, aesCBC, password);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
aesCBC.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
decryptedMsg = new String(decrypted, ASCII);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
public static void main(String[] args) {
String msg = "the decrypted message is this";
String password = "pass";
System.out.println(encrypt(msg, password));
String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
System.out.println(decrypt(encryptedMsg, password));
}
}
Also got an improved solution from the following site. Got the code for both encryption and decryption for now...
http://qaru.site/questions/19874/java-equivalent-of-an-openssl-aes-cbc-encryption
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import static java.nio.charset.StandardCharsets.*;
/**
* Mimics the OpenSSL AES Cipher options for encrypting and decrypting messages using a
* shared key (aka password) with symetric ciphers.
*/
public class OpenSslAesQu {
/** OpenSSL magic initial bytes. */
private static final String SALTED_STR = "Salted__";
private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);
public static String encryptAndURLEncode(String password, String clearText) {
String encrypted = null;
try {
encrypted = URLEncoder.encode(encrypt(password, clearText),UTF_8.name());
} catch (Exception e) {e.printStackTrace();}
return encrypted;
}
/**
*
* #param password The password / key to encrypt with.
* #param data The data to encrypt
* #return A base64 encoded string containing the encrypted data.
*/
public static String encrypt(String password, String clearText) {
String encryptedMsg = null;
final byte[] pass = password.getBytes(US_ASCII);
final byte[] salt = (new SecureRandom()).generateSeed(8);
final byte[] inBytes = clearText.getBytes(UTF_8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
try {
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] data = cipher.doFinal(inBytes);
data = array_concat(array_concat(SALTED_MAGIC, salt), data);
//return Base64.getEncoder().encodeToString( data );
encryptedMsg = org.apache.commons.codec.binary.Base64.encodeBase64String(data);
} catch(Exception e) {e.printStackTrace();}
return encryptedMsg;
}
/**
* #see http://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption for what looks like a useful answer. The not-yet-commons-ssl also has an implementation
* #param password
* #param source The encrypted data
*/
public static String decrypt(String password, String source) {
String decryptedMsg = null;
final byte[] pass = password.getBytes(US_ASCII);
//final byte[] inBytes = Base64.getDecoder().decode(source);
final byte[] inBytes = Base64.decodeBase64(source);
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
try {
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
decryptedMsg = new String(clear, UTF_8);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
private static byte[] array_concat(final byte[] a, final byte[] b) {
final byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String msg = "the decrypted message is this";
String password = "pass";
System.out.println(">> "+encrypt(password,msg));
//System.out.println("<< "+decrypt(encrypt(msg, password), password));
String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
String encryptedMsg2 = "U2FsdGVkX1/B6oOznz5+nd7W/qXwXI7G7rhj5o9pjx8MS0TXp9SNxO3AhM9HBJ/z";
System.out.println(decrypt(password,encryptedMsg));
System.out.println(decrypt(password,encryptedMsg2));
System.out.println(decrypt(password,encrypt(password,msg)));
}
}
I am new to encryption and decryption. I was given a PSKC file and asked for decryption. I was given the password for decryption. The PSKC file doenot have initialization vector value.
I wrote the code trying to decrypt it. But i am unsuccessful in achieving the outcome.
below is the PSKC file example
<?xml version="1.0"?>
<pskc:KeyContainer xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc">
<pskc:EncryptionKey>
<xenc11:DerivedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#">
<xenc11:KeyDerivationMethod Algorithm="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2">
<pkcs5:PBKDF2-params xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#">
<Salt>
<Specified>EW0h0yUcDX72WU9UiKiCwDpXsJg=</Specified>
</Salt>
<IterationCount>128</IterationCount>
<KeyLength>16</KeyLength>
<PRF />
</pkcs5:PBKDF2-params>
</xenc11:KeyDerivationMethod>
<xenc:ReferenceList>
<xenc:DataReference URI="#ED" />
</xenc:ReferenceList>
<xenc11:MasterKeyName>Passphrase1</xenc11:MasterKeyName>
</xenc11:DerivedKey>
</pskc:EncryptionKey>
<pskc:MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1">
<pskc:MACKey>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH6Mlg</xenc:CipherValue>
</xenc:CipherData>
</pskc:MACKey>
</pskc:MACMethod>
<pskc:KeyPackage>
<pskc:DeviceInfo>
<pskc:Manufacturer>Gemalto</pskc:Manufacturer>
<pskc:SerialNo>GAKT000047A5</pskc:SerialNo>
</pskc:DeviceInfo>
<pskc:CryptoModuleInfo>
<pskc:Id>CM_ID_007</pskc:Id>
</pskc:CryptoModuleInfo>
<pskc:Key Id="GAKT000047A5" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:totp">
<pskc:Issuer>Issuer0</pskc:Issuer>
<pskc:AlgorithmParameters>
<pskc:ResponseFormat Encoding="DECIMAL" Length="6" />
</pskc:AlgorithmParameters>
<pskc:Data>
<pskc:Secret>
<pskc:EncryptedValue>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE</xenc:CipherValue>
</xenc:CipherData>
</pskc:EncryptedValue>
<pskc:ValueMAC>lbu+9OcLArnj6mS7KYOKDa4zRU0=</pskc:ValueMAC>
</pskc:Secret>
<pskc:Time>
<pskc:PlainValue>0</pskc:PlainValue>
</pskc:Time>
<pskc:TimeInterval>
<pskc:PlainValue>30</pskc:PlainValue>
</pskc:TimeInterval>
</pskc:Data>
</pskc:Key>
</pskc:KeyPackage>
</pskc:KeyContainer>
below is the java code which i have written for decryption.
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
public class test {
/**
* #param args
*/
public static void main(String[] args) {
test te = new test();
try {
te.decryptSeedValue();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
e.printStackTrace();
}
// TODO Auto-generated method stub
}
public static HashMap decryptSeedValue()throws Exception{
String password = "G?20R+I+3-/UcWIN";
String pbesalt ="EW0h0yUcDX72WU9UiKiCwDpXsJg=";
String iv = "aaaaaaaaaaaaaaaaaaaaaaaa";
int iteration = 128;
String value = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE";
String valueDigest = "lbu+9OcLArnj6mS7KYOKDa4zRU0=";
byte[] cipherText =null;
//some parameters need to decode from Base64 to byte[]
byte[] data = base64Decode(value.getBytes());
//System.out.println("data(hex string) = " + HexBin.encode(data));//debug
byte[] salt = base64Decode(pbesalt.getBytes());
//System.out.println("salt(hex string) = " + HexBin.encode(salt));//debug
byte[] initVec = base64Decode(iv.getBytes());
//System.out.println("iv(hex string) = " + HexBin.encode(initVec));//debug
//perform PBE key generation and AES/CBC/PKCS5Padding decrpyption
HashMap hs = myFunction(data, password, initVec, salt, iteration);
String seedValue = (String)hs.get("DECRYPTED_SEED_VALUE");
byte[] temp = (byte[])hs.get("HASH_OUTPUT");
//System.out.println("hashed output(hex string) = " + HexBin.encode(temp));//debug
//perform Base64 Encode
byte[] out = base64Encode(temp);
String output = new String((out));
System.out.println("output = "+output);
System.out.println("valueD = "+valueDigest);
//System.out.println("hashed output(base64) = " + output);
//compare the result
if(output.equals(valueDigest)){
System.out.println("Hash verification successful for:-->" );
System.out.println("\n");
//hs.put("SEED_VALUE", HexBin.encode(temp));
hs.put("SEED_VALUE", seedValue);
return hs;
}
else{
System.out.println("Hash verification failed for :-->");
return null;
}
}
public static HashMap myFunction(byte[] data, String password, byte[] initVec,
byte[] salt, int iteration) throws Exception{
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
byte[] pBytes = password.getBytes();
generator.init(pBytes, salt, iteration);
int keysize = 128;//fixed at AES key of 16 bytes
int ivsize = initVec.length;
ParametersWithIV params = (ParametersWithIV) generator.generateDerivedParameters(keysize, ivsize);
KeyParameter keyParam = (KeyParameter) params.getParameters();
//System.out.println("derived key = " + HexBin.encode(keyParam.getKey()));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec paramSpec = new IvParameterSpec(initVec);
SecretKeySpec key = new SecretKeySpec(keyParam.getKey(), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
//perform decryption
byte[] secret = cipher.doFinal(data);
//display the 20 bytes secret of the token
//System.out.println("token secret(hex string) = " + HexBin.encode(secret));
//perform HMAC-SHA-1
byte[] output = hmac_sha1(secret, keyParam.getKey());
HashMap hs = new HashMap();
hs.put("ENCRYPTION_KEY", HexBin.encode(keyParam.getKey()));
hs.put("HASH_OUTPUT", output);
hs.put("DECRYPTED_SEED_VALUE", HexBin.encode(secret));
return hs;
}
public static byte[] base64Encode(byte[] passwordBytes) throws NoSuchAlgorithmException {
Base64 base64 = new Base64();
byte[] hashBytes2 = base64.encode(passwordBytes);
return hashBytes2;
}
public static byte[] base64Decode(byte[] passwordBytes) throws NoSuchAlgorithmException {
Base64 base64 = new Base64();
byte[] hashBytes2 = base64.decode(passwordBytes);
return hashBytes2;
}
public static byte[] hmac_sha1(byte[] dataByte, byte[] keyByte) throws Exception{
Mac hmacSha1;
hmacSha1 = Mac.getInstance("HmacSHA1");
SecretKeySpec macKey = new SecretKeySpec(keyByte, "HmacSHA1");
hmacSha1.init(macKey);
byte[] result = hmacSha1.doFinal(dataByte);
return result;
}
/**
* Convert a byte array of 8 bit characters into a String.
*
* #param bytes the array containing the characters
* #param length the number of bytes to process
* #return a String representation of bytes
*/
private static String toString(
byte[] bytes,
int length)
{
char[] chars = new char[length];
for (int i = 0; i != chars.length; i++)
{
chars[i] = (char)(bytes[i] & 0xff);
}
return new String(chars);
}
}
it doesn't throw any exception, but it prints "Hash verification failed for" which is defined in my code when decryption fails.
Can some one please help me out.
As per the pskc standard http://www.rfc-editor.org/rfc/rfc6030.txt the IV is prepended to the ciphervalue. This is aes128, so it'll be the first 16 bytes once it's been base64 decoded.
Adding onto what bcharlton is describing; what you are not doing is check the hmac_sha1 for the encrypted data (which has the iv prepended in encrypted form), using the MACKey described in the xml document.
With AES-128 CBC the initialization vector is explicitly defined, and since there is no verification built into it, it uses HMAC for it.
So given your example the following will work:
public static HashMap decryptSeedValue() throws Exception
{
String password = "G?20R+I+3-/UcWIN";
String pbesalt = "EW0h0yUcDX72WU9UiKiCwDpXsJg=";
String iv = "aaaaaaaaaaaaaaaaaaaaaaaa";
int iteration = 128;
String value = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE";
String valueDigest = "lbu+9OcLArnj6mS7KYOKDa4zRU0=";
//YOU NEED THIS GUY BELOW TO VERIFY
String macKey = "jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH6Mlg";
byte[] cipherText = null;
//some parameters need to decode from Base64 to byte[]
byte[] data = base64Decode(value.getBytes());
//System.out.println("data(hex string) = " + HexBin.encode(data));//debug
byte[] salt = base64Decode(pbesalt.getBytes());
//System.out.println("salt(hex string) = " + HexBin.encode(salt));//debug
byte[] initVec = base64Decode(iv.getBytes());
//System.out.println("iv(hex string) = " + HexBin.encode(initVec));//debug
//perform PBE key generation and AES/CBC/PKCS5Padding decrpyption
HashMap hs = myFunction(data, password, base64Decode(macKey.getBytes()), salt, iteration);
String seedValue = (String) hs.get("DECRYPTED_SEED_VALUE");
byte[] temp = (byte[]) hs.get("HASH_OUTPUT");
//System.out.println("hashed output(hex string) = " + HexBin.encode(temp));//debug
//perform Base64 Encode
byte[] out = base64Encode(temp);
String output = new String((out));
System.out.println("output = " + output);
System.out.println("valueD = " + valueDigest);
//System.out.println("hashed output(base64) = " + output);
//compare the result
if (output.equals(valueDigest)) {
System.out.println("Hash verification successful for:-->");
System.out.println("\n");
//hs.put("SEED_VALUE", HexBin.encode(temp));
hs.put("SEED_VALUE", seedValue);
return hs;
} else {
System.out.println("Hash verification failed for :-->");
return null;
}
}
public static HashMap myFunction(byte[] data, String password, byte[] macData,
byte[] salt, int iteration) throws Exception
{
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
byte[] pBytes = password.getBytes();
generator.init(pBytes, salt, iteration);
byte[] iv = new byte[16];
int ivsize = iv.length;
byte[] encryptedData = new byte[data.length - ivsize];
System.arraycopy(data, 0, iv, 0, iv.length);
System.arraycopy(data, ivsize, encryptedData, 0, encryptedData.length);
byte[] maciv = new byte[16];
byte[] encryptedMac = new byte[macData.length - maciv.length];
System.arraycopy(macData, 0, maciv, 0, maciv.length);
System.arraycopy(macData, maciv.length, encryptedMac, 0, encryptedMac.length);
int keysize = 128;//fixed at AES key of 16 bytes
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iteration, keysize);
SecretKey tmp = factory.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decryptedData = dcipher.doFinal(encryptedData);
// decryptedData is your token value!
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(maciv));
byte[] decryptedMac = dcipher.doFinal(encryptedMac);
//display the 20 bytes secret of the token
//System.out.println("token secret(hex string) = " + HexBin.encode(secret));
//perform HMAC-SHA-1
//Use the decrypted MAC key here for hashing!
byte[] output = hmac_sha1(data, decryptedMac);
HashMap hs = new HashMap();
hs.put("ENCRYPTION_KEY", password);
hs.put("HASH_OUTPUT", output);
hs.put("DECRYPTED_SEED_VALUE", HexBin.encode(decryptedData));
return hs;
}
Keep in mind that as https://www.rfc-editor.org/rfc/rfc6030#section-6.2 describes, a different iv can be used for the MAC and the token key.