Android decryption: Error while finalizing cipher - java

I am using Android to encrypt and encrypt images sent between apps.
The encryption works well but when the file arrives at the destination it will not decrypt. Now I have copied the file at the destination app and decrypted it successfully using 3rd-party software.
The error I get is:"Error while finalizing cipher" at CipherInputStream (CipherInputStream.java:107) caused by IllegalBlockSizeException.
The encryption & decryption code is below:
public static String encrypt(String plainFile, String encryptedFile) throws IOException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException {
// Here you read the cleartext.
File extStore = Environment.getExternalStorageDirectory();
FileInputStream fis = new FileInputStream(plainFile);
// This stream write the encrypted text. This stream will be wrapped by
// another stream.
FileOutputStream fos = new FileOutputStream(encryptedFile);
// Length is 16 byte
SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
// Create cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, sks);
// Wrap the output stream
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
// Write bytes
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
// Flush and close streams.
cos.flush();
cos.close();
fis.close();
return encryptedFile;
}
static String decrypt(String plainFile, String encryptedFile) throws IOException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException {
File encFile=new File(encryptedFile);
FileInputStream fis = new FileInputStream(encFile);
FileOutputStream fos = new FileOutputStream(plainFile);
SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(),
"AES");
Cipher cipher = Cipher.getInstance("AES");
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();
return plainFile;
}
Any ideas? Thanks!
Ronan
Update:
The received encrypted file is consistently 1 byte smaller that the original file which seems to be generating the error. The error re block size is triggered at the code line
while ((b = fis.read(d)) != -1) { in the decrypt function.
Update:
Thanks for the feedback. The ultimate solution is as defined at last block incomplete with CipherInputStream/CipherOutputStream, even with padding AES/CBC/PKCS5Padding
Ronan

Related

Getting BadPaddingException when trying to decrypt - Java

I have this method for encryption:
private byte[] encrypt(byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
BadPaddingException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, myPublicKey);
ByteArrayInputStream input = new ByteArrayInputStream(data);
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[64];
int bytes;
ByteArrayOutputStream aux;
try {
while ((bytes = input.read(buffer)) != -1) {
aux = new ByteArrayOutputStream();
aux.write(buffer, 0, bytes);
byte[] fragment = aux.toByteArray();
byte[] encryptedFragment = cipher.doFinal(fragment);
output.write(encryptedFragment);
}
} catch (IOException e) {
e.printStackTrace();
}
byte[] result = output.toByteArray();
return result;
}
And this one for decryption:
public static String decrypt(byte[] data) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, myPrivateKey);
int bitLenght = ((java.security.interfaces.RSAPrivateKey) privateKey).getModulus().bitLength();
int blockSize = bitLenght / 8;
byte[] buffer = new byte[blockSize];
int bytes;
byte[] decrypted;
ByteArrayOutputStream aux;
ByteArrayInputStream input = new ByteArrayInputStream(data);
ByteArrayOutputStream output = new ByteArrayOutputStream();
while ((bytes = input.read(buffer)) != -1) {
aux = new ByteArrayOutputStream();
aux.write(buffer, 0, bytes);
byte[] fragment = aux.toByteArray();
byte[] decryptedFragment = cipher.doFinal(fragment);
output.write(decryptedFragment);
}
decrypted = output.toByteArray();
return new String(decrypted);
}
But I'm getting this exception:
javax.crypto.BadPaddingException: Decryption error
As I can see I've configured the Cipher to have the same PKCS1Padding so I can't guess why I'm getting that error.
I've created my private key as follows:
openssl genrsa -out myPrivateKey.key 2048
And the public one:
openssl rsa -in myPrivateKey.pem -pubout -out myPublicKey.key
As far as I can see with that command they are both PKCS1, in fact my private key starts with -----BEGIN RSA PRIVATE KEY-----.
What am I missing?
NOTE: I've also tried with blockSize = 64, same result.
Encrypting a stream - correctly you should have cipher.update(..) in the loop and .doFinal(..) called only once after processing all data.
When decrypting if you call doFinal on a partial message you may get the exception. Regardless that it is not apparent from your code if that is the issue you face. (assuming you have the keypair correcly imported)
And indeed RSA is intended only for short (117 bytes) messages. Otherwise you may search for "hybrid encryption"
P. S.: the way your process the streams and arrays is screaming for optimalization, so have a look at it too, but that is for different question

Trying to remove the last 16 bytes that I appended onto the byte[] (which is the IV) then decrypt

Here is my encryption class:
public static void encrypt(byte[] file, String password, String fileName, String dir) throws Exception {
SecureRandom r = new SecureRandom();
//128 bit IV generated for each file
byte[] iv = new byte[IV_LENGTH];
r.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);
FileOutputStream fos = new FileOutputStream(dir + fileName);
fos.write(iv);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
// Have to append IV --------
cos.write(file);
fos.flush();
cos.flush();
cos.close();
fos.close();
}
This is my Decryption method:
public static void decrypt(byte[] file, String password, String fileName, String dir) throws Exception
{
// gets the IV
int ivIndex = file.length - 16;
byte[] truncatedFile = Arrays.copyOfRange(file, 0, file.length - 16);
SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(truncatedFile, ivIndex, 16));
//IvParameterSpec ivspec = new IvParameterSpec(iv);
//
//cipher.init(Cipher.DECRYPT_MODE, keySpec, ivspec);
FileOutputStream fos = new FileOutputStream(dir + fileName);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
cos.write(file);
fos.flush();
cos.flush();
cos.close();
fos.close();
}
}
As you can see I generated a 16 byte long IV that I have appended to the end of the encrypted file. This is so that I pull off the IV for decryption as well as every filing have a unique IV. I am currently getting the error:
java.lang.IllegalArgumentException: IV buffer too short for given offset/length combination
Aside from the problem generating the error, is my logic correct? will this work?
I generated a 16 byte long IV that I have appended to the end of the encrypted file.
No you didn't. You pre-pended it. Which is a better idea anyway. So you have to read it first, and then construct your Cipher and CipherInputStream and decrypt the remainder of the file input stream. You don't need to read the entire file into memory to accomplish that:
public static void decrypt(File file, String password) throws Exception
{
byte[] iv = new byte[16];
DataInputStream dis = new DataInputStream(new FileInputStream(file));
dis.readFully(iv);
SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
CipherInputStream cis = new CipherInputStream(dis, cipher);
// Now just read plaintext from `cis` and do whatever you want with it.
cis.close();
}

Appending IV onto encrypted file for later decryption

I am trying to append a 16 byte securely generated IV onto each file after it is encrypted but before it is written. This is so that I can decrypt it later by pulling the IV off the file. This is my code so far:
public static void encrypt(byte[] file, String password, String fileName, String dir)
throws Exception {
SecureRandom r = new SecureRandom();
//128 bit IV generated for each file
byte[] iv = new byte[IV_LENGTH];
r.nextBytes(iv);
SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);
FileOutputStream fos = new FileOutputStream(dir + fileName);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
// Have to append IV --------
cos.write(file);
fos.flush();
cos.flush();
cos.close();
fos.close();
Any suggestions on how to do this?
FileOutputStream fos = new FileOutputStream(dir + fileName);
fos.write(iv);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
should do the trick. The IV can be written to the file before the Cipher is even set up.

Cannot decrypt the encrypted file?

I tried to encrypt my file by this way:
Encrypt:
static void encrypt(String strInput , String strOutput) throws IOException,
NoSuchAlgorithmException,NoSuchPaddingException, InvalidKeyException {
FileInputStream fis = new FileInputStream(strInput);
FileOutputStream fos = new FileOutputStream(strOutput);
SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(),
"AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, sks);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
// Flush and close streams.
cos.flush();
cos.close();
fis.close();
}
and decrypt it back by:
Decrypt:
static String decrypt(String strInput) throws IOException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException {
FileInputStream fis = new FileInputStream(strInput);
int endFile = strInput.length() - 4;
String strOut = strInput.substring(0, endFile) + "xx.jpg";
FileOutputStream fos = new FileOutputStream(strOut);
SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(),
"AES");
Cipher cipher = Cipher.getInstance("AES");
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();
return strOut;
}
However, the result file's size is 0 kb and when I tried to troubleshoot b = cis.read(d) in decrypt, always returns -1, also cis.available() always returns 0. Can anyone advise me which part of my code is wrong?
Note: I can ensure that the file that is going to be decrypted is always exist.
I believe that this problem is because you are trying to decrypt data that is not encrypted (or not properly encrypted).
In your decrypt() method, the CipherOutputStream hides all exception that the Cipher class may be throwing. See javadoc for CipherOutputStream:
Moreover, this class catches all exceptions that are not thrown by its ancestor classes.
To expose the problem, you may want to implement the cipher usage manually. Here is a quick example:
static String decrypt(String strInput) throws IOException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
FileInputStream fis = new FileInputStream(strInput);
int endFile = strInput.length() - 4;
String strOut = strInput.substring(0, endFile) + "xx.txt";
FileOutputStream fos = new FileOutputStream(strOut);
SecretKeySpec sks = new SecretKeySpec("MyDifficultPassw".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, sks);
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
fos.write(cipher.update(d));
}
fos.write(cipher.doFinal());
fos.flush();
fos.close();
fis.close();
return strOut;
}
The algorithm you posted in your question seems to work fine for valid inputs. For example, let`s assume the following main:
public static void main(String[] argv) {
try {
encrypt("test.txt", "XXX.txt");
decrypt("XXX.txt");
}
catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
Using this, and testing both with a text file and a JPG file, your algorithms executed flawlessly. However, when using an invalid input to the decryption algorithm, then the problem you described started to appear.
For testing, lets imagine that we make the "mistake" of trying to decrypt the file that was in clear like so (just changing the parameter passed to decrypt() in the main):
encrypt("test.txt", "XXX.txt");
decrypt("test.txt");
Then of course the padding on the input to the decrypt() method will be wrong and we should get an exception.
Using your version of decrypt()however, there is no exception. All we get is an empty file.
Using the modified version fo the decrypt() method that is shown above we get the following exception:
javax.crypto.BadPaddingException: Given final block not properly padded
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
at javax.crypto.Cipher.doFinal(Cipher.java:1970)
at MainTest.decrypt(MainTest.java:71)
at MainTest.main(MainTest.java:21)

Strange padding after AES Cipher decryption(I THINK)

this is my code for Java AES encryption and decryption : the parameters are obtained using fileinputstream and secretkeyfactory.
public byte[] encrypt(byte[] plainText, SecretKey secretKey, String outputFilePath) throws Exception
{
//select putput file for encrypted text
FileOutputStream fos = new FileOutputStream(outputFilePath);
//Cipher in encrypt mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
fos.write(cipher.doFinal(plainText));
fos.close();
//Encrypted text is returned in a byte array.
return cipher.doFinal(plainText);
}
// Decryption function
public String decrypt(String sharedKeyFilepath, String cipherTextFilepath, String plainTextFilepath) throws Exception
{
FileInputStream fis = null;
File sharedKeyFile = new File(sharedKeyFilepath);
byte[] sharedKeyByte = new byte[(int)sharedKeyFile.length()];
File cipherTextFile = new File(cipherTextFilepath);
byte[] cipherText = new byte[(int)cipherTextFile.length()];
File plainTextFile = new File(plainTextFilepath);
byte[] plainText = new byte[(int)plainTextFile.length()];
fis = new FileInputStream(sharedKeyFile);
fis.read(sharedKeyByte);
fis.close();
fis = new FileInputStream(cipherTextFile);
fis.read(cipherText);
fis.close();
fis = new FileInputStream(plainTextFile);
fis.read(plainText);
fis.close();
SecretKey sharedKey = new SecretKeySpec(sharedKeyByte, 0, sharedKeyByte.length, "AES");
// Cipher in decrypt mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, sharedKey, new IvParameterSpec(iv));
// Select output file for decrypted cipher Text
FileOutputStream fos = new FileOutputStream("Receiver/DecryptedPlainText.txt");
String decrypted = new String(cipher.doFinal(cipherText));
byte [] decryptedBytes = cipher.doFinal(cipherText);
fos.write(decryptedBytes);
fos.close();
// Return the decrypted text as a string.
return decrypted;
}
The file i send is like :
but the decrypted version is like :
any reasons for this ? Thanks :]

Categories