I'm facing a rather strange behavior about decrypting in Java. Using the code below
public void decrypt(File file, String output_file_path) throws FileNotFoundException, IOException, GeneralSecurityException {
String hex_enc_key = "346a23652a46392b4d73257c67317e352e3372482177652c";
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(HexParser.fromHexString(hex_enc_key), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
CipherOutputStream cos = new CipherOutputStream(new FileOutputStream(new File(output_file_path)), cipher);
FileInputStream fis = new FileInputStream(file);
doCopy(fis, cos);
}
I get random exceptions
java.security.InvalidKeyException: Illegal key size or default parameters
I googled the issue and found out about JCE unlimited strength, but I can't understand why I get those random exceptions even if I'm always using the same key (sometimes it works and sometimes not, based on the input file I need to decrypt).
For the sake of clarity I'm using
Cipher.getMaxAllowedKeyLength("AES")
to check for JCE limitations, and have no issue whatsoever encrypting with the same settings:
public void encrypt(File file, String output_file_path) throws FileNotFoundException, IOException, GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(HexParser.fromHexString(db_enc_key), "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
CipherInputStream cis = new CipherInputStream(new FileInputStream(file), cipher);
FileOutputStream os = new FileOutputStream(new File(output_file_path));
doCopy(cis, os);
cis.close();
os.close();
}
Can anyone point me in the right direction?
Thanks a lot, nicola
Related
Sorry, JAVA beginner here. I was trying out some encryption decryption examples. My methods were supposed to be returning an InputStream and were also supposed to take in an Inputstream as a parameter.
The signature of the method looked like this,
public static InputStream encriptFile(InputStream inputFile).
I researched a bit, and wrote some code confidently, but i don't think the code is properly encrypting a sample file because when I decrypt it and convert into string, it still shows me gibberish. I really don't know what's going wrong with encrypting and decrypting the InputStreams. The Java class looks like this,
private static final String key = "aesEncryptionKey";
private static final String initVector = "encryptionIntVec";
/*
* Getting a 128 bit key and iv for encryption
*/
public static InputStream encriptFile(InputStream inputFile) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
byte[] nonEncryptedByteArray = IOUtils.toByteArray(inputFile);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec secretkey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); //Cipher instance using AES encryption algorithm
cipher.init(Cipher.ENCRYPT_MODE, secretkey, iv);
byte[] encryptedByteArray = cipher.doFinal(nonEncryptedByteArray);
/*
* Used the cipher library to encrypt the stream to a byte array
*/
InputStream encryptedInputStream = new ByteArrayInputStream(encryptedByteArray);
/*
* Back to streams, but this time encrypted
*/
return encryptedInputStream;
}
public static InputStream decriptFile(InputStream inputFile) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
byte[] encrytToDecryptByteArray = IOUtils.toByteArray(inputFile);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec secretkey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretkey, iv);
byte[] decryptedByteArray = cipher.doFinal(encrytToDecryptByteArray);
/*
* dencrypted the encrypted data
*/
InputStream decryptedInputStream = new ByteArrayInputStream(decryptedByteArray);
return decryptedInputStream;
}
The main method looks like this,
File file = new File("test.txt");
InputStream is = new FileInputStream(file);
InputStream eis = encriptFile(is);
StringWriter writer = new StringWriter();
IOUtils.copy(eis, writer, "UTF-8");
String theString = writer.toString();
System.out.print(theString);
The contents of the text file are "Hello, file to be encrypted. Let's see if this works.".
The output which should have printed out an encrypted output looks like this.
��T��� ���N�?]�7!2. When I go ahead and decrypt it, it still shows me gibberish. Sorry for the really long question, any help is appreciated.
You should not return input streams at all. And the way you are using the streams, you're not actually streaming. If you have to use a stream, use CipherInputStream. Personally I'd always use CipherOutputStream for encryption and CipherInputStream for decryption (you are not likely to do anything with the encrypted data, after all, other than exporting it from your application).
A cipher furthermore returns binary data. That's not the same as UTF-8, and no encoding should be necessary for files either, as they accept binary data directly. This is likely the current problem. Just use FileOutputStream / FileInputStream instead of writers or readers.
I tested your code and I think you are printing the encrypted value (so, gibberish) and not the decrypted.
If you update the main to:
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream(new File("test.txt"));
InputStream eis = encriptFile(is);
InputStream result = decriptFile(eis); // <-- Decryption here
StringWriter writer = new StringWriter();
IOUtils.copy(result, writer, "UTF-8");
String theString = writer.toString();
System.out.print(theString);
}
You should be fine.
I tested just by changing the decryptFile() method to:
public static InputStream decriptFile(InputStream inputFile) throws Exception {
byte[] encrytToDecryptByteArray = new byte[inputFile.available()];
inputFile.read(encrytToDecryptByteArray);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8));
SecretKeySpec secretkey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
Cipher cipher = Cipher.getInstance(AES_CBC_PKCS_5_PADDING);
cipher.init(Cipher.DECRYPT_MODE, secretkey, iv);
byte[] decryptedByteArray = cipher.doFinal(encrytToDecryptByteArray);
System.out.println(new String(decryptedByteArray));
return new ByteArrayInputStream(decryptedByteArray);
}
And calling it with the result from the encriptFile() and it worked properly.
I'm having some issues with my code whereby I am not using the same IV for encryption and decryption. I know in order to do this correctly I have to write the IV to my output file before the data however I am struggling implementing this. Could anyone help me with this issue?
Edited code code again to show full scope
public class TestFileEncryption {
private static void mainCrypto(int cipherMode, File inputFile, File outputFile) throws Exception{
//Let the user enter the key they wish to use
Key secretKey = new SecretKeySpec(UITest.getStoreKey().getBytes(), UITest.getSendAlg()); //Generates a key based on the default keysize for the specified algorithm
//Generate an Initialization Vector (IV)
final int ALG_KEYLENGTH = UITest.getStoreKey().length(); //Change this as desired for the security level you want
byte[] iv = new byte[ALG_KEYLENGTH]; //Save the IV bytes or send it in plaintext with the encrypted data so you can decrypt the data later
SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length() - ALG_KEYLENGTH];
inputStream.read(iv);
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(iv);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
}
public static void encrypt(File inputFile, File outputFile) throws Exception {
mainCrypto(Cipher.ENCRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
}
public static void decrypt(File inputFile, File outputFile) throws Exception {
mainCrypto(Cipher.DECRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
}
public static void main(String[] args) {}
}
You just have to write the IV before the ciphertext:
outputStream.write(iv);
outputStream.write(outputBytes);
Then, when decrypting, read the IV and the ciphertext:
byte[] iv = new byte[ALG_BLOCKSIZE];
byte[] inputBytes = new byte[(int) inputFile.length() - ALG_BLOCKSIZE];
inputStream.read(iv);
inputStream.read(inputBytes);
Here ALG_BLOCKSIZE needs to be 16 for AES-CBC.
Just extending answer of #Javier.
it looks like you'd like to use the same method for encryption and decrpytion (depending on the mode) however there's a difference in handling the IV.
You generated a random IV, then you overwrote it with the input of the (plain) input and at the end you wrote it to the output (regardless it's decryption).
So you have to distinguish if the mode is
encryption - the IV is generated and written to the output before ciphertext
decryption - the IV is read from the input and used for decryption, but not written to the output
something like that:
private void encrypt(File inputFile, File outputFile) {
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
byte[] iv = new byte[16]; // 16 for AES-CBC
SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(iv);
outputStream.write(outputBytes);
outputStream.flush();
inputStream.close();
outputStream.close();
}
}
private void decrypt(File inputFile, File outputFile) {
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()-16];
byte[] iv = new byte[16]; // 16 for AES-CBC
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
inputStream.read(iv);
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
outputStream.flush();
inputStream.close();
outputStream.close();
}
To leave out some detail, maybe you could directly use Java CipherOutputStream and CiptherInputStream and the implementation will handle these details for you (if you don't care about exact format).
Next what are you missing is an authentication tag, at least hash of the plaintext assuring integrity of the ciphertext. (it's called authenticated encryption)
I am using Bouncycastle to do AES 256 bits encryption of file which will then be uploaded into the backend.
My app works perfectly on an Android 4.0 device. When I tested the same app on an Android 4.3 device, first it crashes showing OutofMemory exception while doing encryption. So I added in try catch exceptions.
It completed the encryption but the subsequent file sent to backend when decrypted file shows gibberish.
Not sure how this is possible since it is the same app with the same keys used.
Any idea how I can resolve this issue? Am I doing something wrong? Below is my encryption code:
public void encryptfile(String encpathname , String destencfile){
try {
byte enc[] = null;
File file = new File(encpathname);
byte[] data = new byte[(int) file.length()];
FileInputStream fis;
fis = new FileInputStream(file);
fis.read(data);
fis.close();
enc = encrypt(passphrase, data);
FileOutputStream stream = new FileOutputStream(destencfile);
stream.write(enc);
stream.close();
file.delete();
System.gc();
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static byte [] encrypt(String passphrase, byte [] inbytes) throws Exception {
SecretKey key = generateKey(passphrase);
Cipher cipher = Cipher.getInstance("AES/CTR/NOPADDING");
// Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key, generateIV(cipher), random);
return cipher.doFinal(inbytes);
}
private static SecretKey generateKey(String passphrase) throws Exception {
PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(), salt.getBytes(), iterations, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
return keyFactory.generateSecret(keySpec);
}
private static IvParameterSpec generateIV(Cipher cipher) throws Exception {
byte [] ivBytes = new byte[cipher.getBlockSize()];
random.nextBytes(ivBytes);
return new IvParameterSpec(ivBytes);
}
I wrote two functions as follows which encrypt and decrypt the data.
public static void encrypt() throws Exception {
// Add the BouncyCastle Provider
//Security.addProvider(new BouncyCastleProvider());
// Generate the key
byte[] keyBytes = "AAAAAAAAAAAAAAAA".getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
// Generate the IV
byte[] ivBytes = "AAAAAAAAAAAAAAAA".getBytes();
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// Create the cipher object and initialize it
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
// Read all bytes from a file into a bytes array
byte[] inputBytes = GCM.readFile("input");
byte[] cipherBytes = cipher.doFinal(inputBytes);
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("output.enc"));
outputStream.write(cipherBytes);
outputStream.close();
}
public static void decrypt() throws Exception {
// Add the BouncyCastle Provider
//Security.addProvider(new BouncyCastleProvider());
// Generate the key
byte[] keyBytes = "AAAAAAAAAAAAAAAA".getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
// Generate the IV
byte[] ivBytes = "AAAAAAAAAAAAAAAA".getBytes();
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// Create the cipher object and initialize it
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
// Read all bytes from a file into a bytes array
byte[] cipherBytes = GCM.readFile("ouput.enc");
byte[] decBytes = cipher.doFinal(cipherBytes);
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("regen.plain"));
outputStream.write(decBytes);
outputStream.close();
}
I realize that the code has the key set as "AAAAAAAAAAAAAAAA".getBytes() ; however please bear with me as this is just an example.
When I run the program I get the following stack trace :-
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at GCM.decrypt(GCM.java:80)
at GCM.main(GCM.java:90)
I'm having trouble figuring out why I'm encountering this error. Any hints on how I could solve the issue?
[EDIT]
It seems that when I write out data there are 16 bytes in all but only 15 bytes when I read it back in.
On your update: Well, that's easy then, fix the part that reads the file because the ciphertext needs to be N * blocksize, thus 16 bytes. I don't see any other blatant errors.
Possible problem (unless it is a typo) you write to output.enc but read from ouput.enc.
I have been able to use the algorithm to encrypt and decrypt files , but when I go to try and send a file from Android to a WAS server, it fails. Here is the encrypt side
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom random = new SecureRandom();
keygen.init(random);
SecretKey key = keygen.generateKey();
// wrap with RSA public key
ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream (getFileLocation(PUBLIC_KEY, localTest)));
Key publicKey = (Key) keyIn.readObject();
keyIn.close();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE, publicKey);
byte[] wrappedKey = cipher.wrap(key);
DataOutputStream out = new DataOutputStream(new FileOutputStream(getFileLocation(SIGN_FILE, localTest)));
out.writeInt(wrappedKey.length);
out.write(wrappedKey);
InputStream in = new ByteArrayInputStream(message.getBytes());
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
crypt(in, out, cipher);
in.close();
out.close();
FileInputStream fis = new FileInputStream(getFileLocation(SIGN_FILE, localTest));
byte[] buffer = new byte[fis.available()];
int i =0;
while (i< buffer.length ){
buffer[i]= (byte)fis.read();
i++;
}
String ss = encodeMsg(buffer);
return ss;
Here is the decrypt side
Security.addProvider(new BouncyCastleProvider());
byte[] arr = decodeMsg(encrypted);
DataInputStream in = new DataInputStream(new ByteArrayInputStream(arr));
int length = in.readInt();
byte[] wrappedKey = new byte[length];
in.read(wrappedKey, 0, length);
// unwrap with RSA private key
ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream (getFileLocation(PRIVATE_KEY, localTest)));
Key privateKey = (Key) keyIn.readObject();
keyIn.close();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.UNWRAP_MODE, privateKey);
Key key = cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
OutputStream out = new FileOutputStream(getFileLocation(DECRYPTED, localTest));
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
crypt(in, out, cipher);
in.close();
out.close();
FileInputStream fis = new FileInputStream(getFileLocation(DECRYPTED, localTest));
byte[] buffer = new byte[fis.available()];
int i =0;
while (i< buffer.length ){//!= 0) {
buffer[i]= (byte)fis.read();
i++;
}
String ss = new String(buffer);
return ss;
Again, on my workstation, this works. When doing the mobile request to the WAS web server, it fails. At first, it argued with the object class and so I recreated the keys using Java 1.6. I have recompiled the war into Java 1.6 as well. It errors as below.
--cipher unwrap
java.security.InvalidKeyException com.ibm.crypto.provider.RSA.engineUnwrap(Unknown Source)
javax.crypto.Cipher.unwrap(Unknown Source)
com.webapp.web.security.RSAEncrypt.decrypt(RSAEncrypt.java:161)
com.webapp.web.MobileRequest.doPost(MobileRequest.java:81)
javax.servlet.http.HttpServlet.service(HttpServlet.java:738)
javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
...
Does the WAS environment have to be updated to handle this? ideas?
UPDATE the keysize is set to 2048
This could be due to key policy settings, do you have the Unlimited Strength Juristiction Policies installed on both machines? They can be found at the bottom of this page: http://www.oracle.com/technetwork/java/javase/downloads/index.html
Else, how are you sending the data to the server?
The Unlimited Jurisdiciton policy might work, but I attempted to use the IBMJCE without success as well. Then, I switched to use the SunJCE provider (version Java 1.6) and now I am able to do the encryption and decryption in both Android and Websphere. I am having the administrators look into the policy files to see if BouncyCastle could be enabled, but I am ok with using the Sun provider files.