How to Produce an Actual Encrypted Files with AES? [JAVA] - java

Hi i have explore many good site about AES Encryption, Most Site will be nicely detail about how to encrypt files and really help me understand the AES Encryption.
but i am still unclear about how to produce files that is encrypted. this tutorial example explain how AES encryption was done but i still cannot see the physical encrypted file. Most example show only how to encrypt and decrypt but did not explain about how to produce encrypted physical file.
My Question here is how do we actually produce an actual encrypted files, i believe this question is relevance to SO citizen as this might help other in future.
Answer
The code below will encrypt a text file with physical encrypted file.
final Path origFile = Paths.get("C:\\3.txt");
final byte[] contents = Files.readAllBytes(origFile);
// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(contents.toString().getBytes());
System.out.println("encrypted string: " + encrypted.toString());
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original =cipher.doFinal(encrypted);
String originalString = new String(original);
System.out.println("Original string: " +originalString);
final Path newFile = Paths.get("C:\\3encrypted.aes");
Files.write(newFile, encrypted, StandardOpenOption.CREATE);
}
As fge suggest, this is not suite for encrypting large file. ill provide new answer when i done with my research.

Your code is not correct; you try and read bytes from a file and then put it into a StringBuffer which is a character sequence. Don't do that!
Read the bytes directly:
final Path origFile = Paths.get("C:\\3.txt");
final byte[] contents = Files.readAllBytes(origFile);
Then encrypt like you do, and write your encrypted byte array into a new file:
final Path newFile = Paths.get("C:\\3encrypted.aes"); // or another name
Files.write(newFile, encrypted, StandardOpenOption.CREATE);
It is very important to understand that String is not suitable for binary data. Please see this link for more details.

Related

How to Store secretKey and IV in a single file for AES Encryption and Decryption using Java?

I have to encrypt my file using AES -256 cipher with AES 256 key and 16 bytes IV and I want to save key and IV in one file and reuse it for Decryption. But currently i can save it individually. Can any one help us how to store key and IV in a single file.
here is my code
SecureRandom srandom = new SecureRandom();
byte[] iv = new byte[16];
srandom.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
FileOutputStream ivOutFile = new FileOutputStream("C:\\iv.key");
ivOutFile.write(iv);
ivOutFile.close();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(256);
SecretKey skey = kgen.generateKey();
FileOutputStream out = new FileOutputStream("C:\\AES.key");
byte[] keyb = skey.getEncoded();
out.write(keyb);
out.close();
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);
FileEncryptionUtils fileEncryptionUtils =new FileEncryptionUtils();
fileEncryptionUtils.processFile(ci, inFile, outFile);
You're approach to using IV's is incorrect. IV's aren't secret and shouldn't be reused. Generate a new one every single time you encrypt and just store it alongside the ciphertext, not with the key!
See the examples in this repository for best practices when it comes to symmetric encryption.
I found a way to store in one file and used that file for decryption and its working. here is my approach
while writing IV and key in 2 different files, i have written in one file. And for Decryption i read the file like first 16bytes for IV and last 32 bytes for secretkey.
FileOutputStream OutFile = new FileOutputStream("C:\\SecretFile.key");
OutFile.write(iv);
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(256);
SecretKey skey = kgen.generateKey();
byte[] keyb = skey.getEncoded();
OutFile.write(keyb);
OutFile.close();

AES-256-CBC encrypted with PHP and decrypt in Java

I am in a situation where a JSON is encrypted in PHP's openssl_encrypt and needs to be decrypted in JAVA.
$encrypted = "...ENCRYPTED DATA...";
$secretFile = "/path/to/secret/saved/in/text_file";
$secret = base64_decode(file_get_contents($secretFile));
var_dump(strlen($secret)); // prints : int(370)
$iv = substr($encrypted, 0, 16);
$data = substr($encrypted, 16);
$decrypted = openssl_decrypt($data, "aes-256-cbc", $secret, null, $iv);
This $decrypted has correct data which is now decrypted.
Now, the problem is when I try to do same things in Java it doesn't work :(
String path = "/path/to/secret/saved/in/text";
String payload = "...ENCRYPTED DATA...";
StringBuilder output = new StringBuilder();
String iv = payload.substring(0, 16);
byte[] secret = Base64.getDecoder().decode(Files.readAllBytes(Paths.get(path)));
String data = payload.substring(16);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(), 0, cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); // This line throws exception :
cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
Here it is:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 370 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:346)
at javax.crypto.Cipher.init(Cipher.java:1394)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.sample.App.main(App.java:70)
I have already visited similar question like
AES-256 CBC encrypt in php and decrypt in Java or vice-versa
openssl_encrypt 256 CBC raw_data in java
Unable to exchange data encrypted with AES-256 between Java and PHP
and list continues.... but no luck there
btw, this is how encryption is done in PHP
$secretFile = "/path/to/secret/saved/in/text_file";
$secret = base64_decode(file_get_contents($secretFile));
$iv = bin2hex(openssl_random_pseudo_bytes(8));
$enc = openssl_encrypt($plainText, "aes-256-cbc", $secret, false, $iv);
return $iv.$enc;
and yes, I forgot to mention that my JRE is already at UnlimitedJCEPolicy and I can't change PHP code.
I am totally stuck at this point and can't move forward. Please help out.
EDIT#1
byte[] payload = ....;
byte[] iv = ....;
byte[] secret = ....; // Now 370 bits
byte[] data = Base64.getDecoder().decode(payload);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOfRange(secret, 0, 32), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv, 0, cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] output = cipher.doFinal(data);
System.out.println(new String(output).trim());
Above snippet seems to be working with openssl_encrypt
EDIT#2
I am not sure if this is correct, but following is what I have done and encryption-decryption on both side are working fine.
Encrypt in PHP, Decrypt in JAVA use AES/CBC/NoPadding
Encrypt in JAVA, Decrypt in PHP use AES/CBC/PKCS5Padding
I won't provide a complete solution, but there are a few differences you should take care of
Encoding:
String iv = payload.substring(0, 16);
String data = payload.substring(16);
are you sure the IV and data are the same in Java and PHP (The IV is string?)? If the data are encrypted, they should be treated as a byte array, not string. Just REALLY make sure they are THE SAME (print hex/base64 in php and java)
For the IV you at the end call iv.getBytes(), but the locale encoding may/will corrupt your values. The String should be use only when it's really string (text). Don't use string for binaries.
Simply treat data and iv as byte[]
Key generation according to the openssl
AES key must have length of 256 bit for aes-256-cbc used. The thing is - openssl by default doesn't use the provided secret as a key (I believe it can, but I don't know how it is to be specified in PHP).
see OpenSSL EVP_BytesToKey issue in Java
and here is the EVP_BytesToKey implementation: https://olabini.com/blog/tag/evp_bytestokey/
you should generate a 256 bit key usging the EVP_BytesToKey function (it's a key derivation function used by openssl).
Edit:
Maarten (in the comments) is right. The key parameter is the key. Seems the PHP function is accepting parameter of any length which is misleading. According to some articles (e.g. http://thefsb.tumblr.com/post/110749271235/using-opensslendecrypt-in-php-instead-of) the key is trucated or padded to necessary length (so seems 370 bit key is truncated to length of 256 bits).
According to your example, I wrote fully working code for PHP and Java:
AesCipher class: https://gist.github.com/demisang/716250080d77a7f65e66f4e813e5a636
Notes:
-By default algo is AES-128-CBC.
-By default init vector is 16 bytes.
-Encoded result = base64(initVector + aes crypt).
-Encoded/Decoded results present as itself object, it gets more helpful and get possibility to check error, get error message and get init vector value after encode/decode operations.
PHP:
$secretKey = '26kozQaKwRuNJ24t';
$text = 'Some text'
$encrypted = AesCipher::encrypt($secretKey, $text);
$decrypted = AesCipher::decrypt($secretKey, $encrypted);
$encrypted->hasError(); // TRUE if operation failed, FALSE otherwise
$encrypted->getData(); // Encoded/Decoded result
$encrypted->getInitVector(); // Get used (random if encode) init vector
// $decrypted->* has identical methods
JAVA:
String secretKey = "26kozQaKwRuNJ24t";
String text = "Some text";
AesCipher encrypted = AesCipher.encrypt(secretKey, text);
AesCipher decrypted = AesCipher.decrypt(secretKey, encrypted);
encrypted.hasError(); // TRUE if operation failed, FALSE otherwise
encrypted.getData(); // Encoded/Decoded result
encrypted.getInitVector(); // Get used (random if encode) init vector
// decrypted.* has identical methods

How to generate secret key in Java once and use that key in 2 different programs

My aim is to write a Java program to encrypt a text file (cipher text) using AES algorithm. And then, write another program to decrypt that encrypted file (cipher text) to get the plain text back. I want to use same key (same key, generate once, save it somewhere, and use it in both encryption and decryption program) for encryption and decryption process. If I generate key and do the encryption and decryption line by line in the same program then it works perfectly. Here is the working code snippet for that:
String strDataToEncrypt = new String();
String strCipherText = new String();
String strDecryptedText = new String();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKey = keyGen.generateKey();
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE,secretKey);
strDataToEncrypt = "any text input";
byte[] byteDataToEncrypt = strDataToEncrypt.getBytes();
byte[] byteCipherText = aesCipher.doFinal(byteDataToEncrypt);
strCipherText = new BASE64Encoder().encode(byteCipherText);
System.out.println("cipher text: " +strCipherText);
aesCipher.init(Cipher.DECRYPT_MODE,secretKey,aesCipher.getParameters());
byte[] byteDecryptedText = aesCipher.doFinal(new BASE64Decoder().decodeBuffer(strCipherText));
strDecryptedText = new String(byteDecryptedText);
System.out.println("plain text again: " +strDecryptedText);
But, I need to have two different programs (java files) for encryption and decryption. So, I have to somehow generate a key and save that somewhere. Then use the same key for both encryption and decryption program. How can I do that?
EDIT_1
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKey = keyGen.generateKey();
byte[] encoded = secretKey.getEncoded();
System.out.println("key: "+encoded);// key: [B#52b2a2d8
I can get the encoded key value using the above program. But my question is how to generate the SecretKey using this value in my decryption program?
Forgive me if I misunderstood your question but I believe you wish to reconstruct a SecretKey object from a existing key encoded in a byte array.
This can be performed simply by using the javax.crypto.spec.SecretKeySpec's constructor as such:
byte[] encoded = //Key data
SecretKey secretKey = new SecretKeySpec(encoded, "AES");
Since SecretKeySpec is a subclass of SecretKey no casting is needed. Should your encryption/decrption algorithm change please make sure to change the string literal used in the constructor AES to whatever algorithm you decided to use in the future.
Here's one way to print out the values in a byte[] array in hex:
byte[] a = {-120, 17, 42,121};
for (byte b : a)
{
System.out.printf("%2X",b);
}
System.out.println();

Decrypting Blowfish/CBC in Java

I have Perl code that decrypts a String and I want to do the same in Java. This is the Perl code:
my $c = Crypt::CBC->new( -key => $keyString, -cipher => 'Blowfish', -header => 'randomiv');
return $c->decrypt_hex($config->{encrypted_password})
This is my attempt at the Java code:
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
// setup an IV (initialization vector) that should be
// randomly generated for each input that's encrypted
byte[] iv = new byte[cipher.getBlockSize()];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// decrypt
SecretKey secretKey = new SecretKeySpec(Base64.decodeBase64(keyString), "Blowfish");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decrypted = cipher.doFinal(Base64.decodeBase64(input));
return Hex.encodeHexString(decrypted);
I'm getting:javax.crypto.BadPaddingException: Given final block not properly padded. But according to this, the Crypt CBC library uses PKCS5 as the default padding.
Also, am I doing the hex encoding at the end right?
One of the problems you have is that you generate a random IV instead of importing the one used for encryption. Do you have access to the IV used at encryption? Could it be at the start of the ciphertext?
I don't do Perl, so I'm not quite sure if my response is valid. Base64 is probably not the right decoding you're looking for.
For creating your SecretKeySpec, try doing something like:
SecretKey secretKey = new SecretKeySpec(keyString.getBytes("ASCII"), "Blowfish");
For decoding the text, check out Hex.decodeHex(char[]) which can be found at http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Hex.html ... so your code might look something like this:
byte[] decrypted = cipher.doFinal(Hex.decodeHex(input.toCharArray()));
String unencryptedStuff = new String(decrypted);

Encryption/Decryption - iphone to java - BadPaddingException: Given final block not properly padded

My objective is to encrypt data in Iphone and decrypt it on java server.
I am using Symmetric encryption .
I have generated the key using KeyGenerator at the java side.
code for generating key is as follows:
//Java Code for key generation
File keyFile = new File("F:/key","mykey.key");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey skey = kgen.generateKey();
byte[] enc= skey.getEncoded();
FileUtils.writeStringToFile(keyFile ,Base64.encodeBase64String(enc),"UTF-8");
Following is the java code for decryption:
//get key from file
File file = new File("F:/key", "mykey.key");
SecretKeySpec keySpec= null;
try {
byte[] keyBytes = Base64.decodeBase64(FileUtils.readFileToString(file,"UTF-8"));
keySpec= new SecretKeySpec(keyBytes, 0, 16, "AES");
byte[] raw = keySpec.getEncoded();
} catch (Exception e) {
e.printStackTrace();
}
//Decrypt String encryptedString(from Iphone)
byte[] tempByte = Base64.decodeBase64(encryptedString);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] cipherData = cipher.doFinal(tempByte);
String ttt = new String(cipherData ,"UTF-8");
System.out.println(ttt);
And the iphone code is similar to th code given in following link:
Encrypting data with Objective-C and decrypt it with Java Problem
I am getting the following exception while decrypting in java.
javax.crypto.BadPaddingException: Given final block not properly padded
Please help...
Well the padding and mode has to match. If you copied the Objective-C code, then the ciphertext on the Objective-C side has the ECB mode and the PKCS7 padding.
By default the java AES cipher has the CBC mode and PKCS5 padding (though I'm not sure, and AFAIK PKCS5 and PKCS7 are somewhat compatible). I guess you have to specify these explicitly. Those settings have to match otherwise something goes wrong. So you have to create the cipher like that:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
Btw. if you can choose the encryption-mode you should use CBC (but then on both sides).

Categories

Resources