1

Error : javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher

Tried Solutions: I've tried to to change the padding to "AES/ECB/NoPadding", "AES/ECB/PKCS5", "AES/CBC/NoPadding", "AES/CBC/PKCS5Padding" and still received the same error or an error stating only AES or Rijndael required. Then I tried making the key use "AES" parameter and ALGO set to "AES/CBC/PKCS5Padding", but I recieved a missing parameter error which I tried to fix my adding new IvParameterSpec(new byte[16]) to cipher.init. It still resulted into the 16 bit issue. So I'm stuck now.

import java.util.Scanner;
import java.io.File;
import java.io.IOException;

import java.io.BufferedReader;
import java.io.FileReader;

import java.security.Key;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.*;

import java.io.PrintWriter;
import java.io.FileWriter;

import java.util.*;
import java.io.*;  

// Don't forget to import any supporting classes you plan to use.

public class Crypto
{
  private Scanner fileText; 
  private PrintWriter fileEncrypt;
  private Scanner inputFile;
  private PrintWriter outputFile;

  private static final String ALGO = "AES/CBC/PKCS5Padding";
  private byte[] keyValue;

  public Crypto(String key)
      {
        keyValue = key.getBytes();
      }

  public String encrypt(String Data) throws Exception 
      {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGO);
        c.init(Cipher.ENCRYPT_MODE, key);
        byte[] encVal = c.doFinal(Data.getBytes());
        String encryptedValue = new BASE64Encoder().encode(encVal);
        return encryptedValue;
      }

  public String decrypt(String encryptedData) throws Exception
      {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGO);
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decodedValue = new BASE64Decoder().decodeBuffer(encryptedData);
        byte[] decValue = c.doFinal(decodedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
      }

  public Key generateKey() throws Exception
      {
        Key key = new SecretKeySpec(keyValue, "AES");
        return key;
      }

   // encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt", "CryptoCiphertext.txt" )
   // encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt", "CryptoDeciphered.txt")

  public void encrypt_decrypt(String function_type , String source_file , String 
  target_file)
  {
    String lineValue = ""; 
    String convertedValue = "";
    try
    {
      inputFile = new Scanner(new File(source_file));
    } 
    catch(Exception e)
    {
      System.out.println("( " + source_file + ") - File Opening Error");
    }
    try
    {
      outputFile = new PrintWriter(new FileWriter(target_file));
    }
    catch(Exception e)
    {
      System.out.println("( " + target_file + ") - File Opening Error");
    }

    while(inputFile.hasNext())
    { 
      lineValue = inputFile.nextLine();
      System.out.println("Source Line: " + lineValue);

      try
      {
        if (function_type == "ENCRYPT")
        {
          convertedValue = encrypt(lineValue);
        }
        else if (function_type == "DECRYPT")
        {
          convertedValue = decrypt(lineValue);
        }
      }
      catch(Exception e)
      {
        System.out.println(e);
      }
      System.out.println("Converted Line : " + convertedValue);

      outputFile.write(convertedValue);
    }

    inputFile.close();
    outputFile.close();
  }  

 public static void main( String args[] ) throws IOException
 {

      // Write your code here...
      // You will read from CryptoPlaintext.txt and write to 
      CryptoCiphertext.txt.
      Crypto c = new Crypto("dk201anckse29sns");
      c.encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt", "CryptoCiphertext.txt" 
      );
      c.encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt", 
      "CryptoDeciphered.txt");
      //
      // And then read from CryptoCiphertext.txt and write to 
      CryptoDeciphered.txt.
      //
      // DON'T forget your comments!
      // =============================== DO NOT MODIFY ANY CODE BELOW HERE 
       ==============================

     // Compare the files

      System.out.println(compareFiles() ? "The files are identical!" : "The 
      files are NOT identical.");   
 }

 /**  
  *  Compares the Plaintext file with the Deciphered file.
  *
  *    @return  true if files match, false if they do not
  */

  public static boolean compareFiles() throws IOException
  {

       Scanner pt = new Scanner(new File("CryptoPlaintext.txt")); // Open the 
       plaintext file
       Scanner dc = new Scanner(new File("CryptoDeciphered.txt"));  // Open the 
       deciphered file

       // Read through the files and compare them record by record.
       // If any of the records do not match, the files are not identical.

       while(pt.hasNextLine() && dc.hasNextLine())
         if(!pt.nextLine().equals(dc.nextLine())) return false;

       // If we have any records left over, then the files are not identical.

       if(pt.hasNextLine() || dc.hasNextLine()) return false;

       // The files are identical.

       return true;




  }
}
Rajesh Pandya
  • 1,540
  • 4
  • 18
  • 31
Gagan
  • 13
  • 3
  • Possible duplicate of [How do I compare strings in Java?](https://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java) – azro May 11 '18 at 08:47

2 Answers2

0

There are two errors in your code:

  1. You forgot to generate a random IV and prefix it to your ciphertext (before encoding it to base 64). You'd need to find the IV from your ciphertext and then retrieve it back again during decryption.

Note that CBC code requires an IV indistinguishable from random. You can create it using new SecureRandom (only during encryption, of course) and IvParameterSpec. The code will probably run without this as the default implementation in Java defaults on an all-zero IV. Possibly that is enough for this assignment.

But that's not what generates the error; that's much more of a banality:

  1. You're calling outputFile.write instead of outputFile.println which means that newlines aren't inserted, and all base 64 encodings are put in a single line.

Note that you should not use any classes from sun.misc. Those are private to the Java implementation and are not part of the Java API. The new Java versions have java.util.Base64 for your convenience. Actually, the sun.misc version may insert line endings within the base 64 encoding that will break your code for longer lines.


For example:

package nl.owlstead.stackoverflow;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.Key;
import java.util.Base64;
import java.util.Scanner;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Crypto {
    private Scanner inputFile;
    private PrintWriter outputFile;

    private static final String ALGO = "AES/CBC/PKCS5Padding";
    private byte[] keyValue;

    public Crypto(String key) {
        keyValue = key.getBytes();
    }

    public String encrypt(String Data) throws Exception {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGO);
        c.init(Cipher.ENCRYPT_MODE, key,
                new IvParameterSpec(new byte[c.getBlockSize()]));
        byte[] encVal = c.doFinal(Data.getBytes());
        String encryptedValue = Base64.getEncoder().encodeToString(encVal);
        return encryptedValue;
    }

    public String decrypt(String encryptedData) throws Exception {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGO);
        c.init(Cipher.DECRYPT_MODE, key,
                new IvParameterSpec(new byte[c.getBlockSize()]));
        byte[] decodedValue = Base64.getDecoder().decode(encryptedData);
        byte[] decValue = c.doFinal(decodedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }

    public Key generateKey() throws Exception {
        Key key = new SecretKeySpec(keyValue, "AES");
        return key;
    }

    public void encrypt_decrypt(String function_type, String source_file,
            String target_file) {
        String lineValue = "";
        String convertedValue = "";
        try {
            inputFile = new Scanner(new File(source_file));
        } catch (Exception e) {
            System.out.println("( " + source_file + ") - File Opening Error");
        }
        try {
            outputFile = new PrintWriter(new FileWriter(target_file));
        } catch (Exception e) {
            System.out.println("( " + target_file + ") - File Opening Error");
        }

        while (inputFile.hasNext()) {
            lineValue = inputFile.nextLine();
            System.out.println("Source Line: " + lineValue);

            try {
                if (function_type == "ENCRYPT") {
                    convertedValue = encrypt(lineValue);
                } else if (function_type == "DECRYPT") {
                    convertedValue = decrypt(lineValue);
                }
            } catch (Exception e) {
                System.out.println(e);
            }
            System.out.println("Converted Line : " + convertedValue);

            outputFile.println(convertedValue);
        }

        inputFile.close();
        outputFile.close();
    }

    public static void main(String args[]) throws IOException {

        Crypto c = new Crypto("dk201anckse29sns");
        c.encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt",
                "CryptoCiphertext.txt");
        c.encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt",
                "CryptoDeciphered.txt");

        System.out.println(compareFiles() ? "The files are identical!"
                : "The files are NOT identical.");
    }

    /**
     * Compares the Plaintext file with the Deciphered file.
     *
     * @return true if files match, false if they do not
     */

    public static boolean compareFiles() throws IOException {

        Scanner pt = new Scanner(new File("CryptoPlaintext.txt")); // Open the
        Scanner dc = new Scanner(new File("CryptoDeciphered.txt")); // Open the

        // Read through the files and compare them record by record.
        // If any of the records do not match, the files are not identical.

        while (pt.hasNextLine() && dc.hasNextLine()) {
            String ptl = pt.nextLine();
            String dcl = dc.nextLine();
            if (!ptl.equals(dcl))
            {
                System.out.println(ptl);
                System.out.println(dcl);
                continue;
//              return false;

        }
            }
        // If we have any records left over, then the files are not identical.

        if (pt.hasNextLine() || dc.hasNextLine())
            return false;

        // The files are identical.

        return true;
    }
}
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • I tried your second solution of "println" and I still received this error : javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher Can you also give an example for the first solution, I've added the IVParameterSec as the other answer suggested below, but still received same issue. The issue is the my program is not even decrypting the encrypted file since it has been written to a length that is not a multiple of 16. – Gagan May 12 '18 at 03:34
  • You probably forgot about my base 64 statement. See code & good night. – Maarten Bodewes May 12 '18 at 03:38
-1

A working solution for you:

Just added a random IV value while initiating your cipher during encrypting and decrypting.

c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[16]));

package com.samples;

import java.util.Scanner;
import java.io.File;
import java.io.IOException;

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.PrintWriter;
import java.io.FileWriter;  

// Don't forget to import any supporting classes you plan to use.

public class Crypto
{
    private Scanner fileText; 
    private PrintWriter fileEncrypt;
    private Scanner inputFile;
    private PrintWriter outputFile;

    private static final String ALGO = "AES/CBC/PKCS5Padding";
    private byte[] keyValue;

    public Crypto(String key)
    {
        keyValue = key.getBytes();
    }

    public String encrypt(String Data) throws Exception 
    {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGO);
        c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[16]));
        byte[] encVal = c.doFinal(Data.getBytes());
        String encryptedValue = new BASE64Encoder().encode(encVal);
        return encryptedValue;
    }

    public String decrypt(String encryptedData) throws Exception
    {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGO);
        c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[16]));
        byte[] decodedValue = new BASE64Decoder().decodeBuffer(encryptedData);
        byte[] decValue = c.doFinal(decodedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }

    public Key generateKey() throws Exception
    {
        Key key = new SecretKeySpec(keyValue, "AES");
        return key;
    }

    // encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt", "CryptoCiphertext.txt" )
    // encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt", "CryptoDeciphered.txt")

    public void encrypt_decrypt(String function_type, String source_file, String target_file)
    {
        String lineValue = ""; 
        String convertedValue = "";
        try
        {
            inputFile = new Scanner(new File(source_file));
        } 
        catch(Exception e)
        {
            System.out.println("( " + source_file + ") - File Opening Error");
        }
        try
        {
            outputFile = new PrintWriter(new FileWriter(target_file));
        }
        catch(Exception e)
        {
            System.out.println("( " + target_file + ") - File Opening Error");
        }

        while(inputFile.hasNext())
        { 
            lineValue = inputFile.nextLine();
            System.out.println("Source Line: " + lineValue);

            try
            {
                if (function_type == "ENCRYPT")
                {
                    convertedValue = encrypt(lineValue);
                }
                else if (function_type == "DECRYPT")
                {
                    convertedValue = decrypt(lineValue);
                }
            }
            catch(Exception e)
            {
                System.out.println(e);
            }
            System.out.println("Converted Line : " + convertedValue);

            outputFile.write(convertedValue);
        }

        inputFile.close();
        outputFile.close();
    }  

    public static void main( String args[] ) throws IOException
    {

        // Write your code here...
        // You will read from CryptoPlaintext.txt and write to CryptoCiphertext.txt.
        Crypto c = new Crypto("dk201anckse29sns");
        c.encrypt_decrypt("ENCRYPT", "C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoPlaintext.txt", "C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoCiphertext.txt" 
                );
        c.encrypt_decrypt("DECRYPT", "C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoCiphertext.txt", 
                "C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoDeciphered.txt");
        //
        // And then read from CryptoCiphertext.txt and write to CryptoDeciphered.txt.
        //
        // DON'T forget your comments!
        // =============================== DO NOT MODIFY ANY CODE BELOW HE     ==============================

        // Compare the files

        System.out.println(compareFiles() ? "The files are identical!" : "The files are NOT identical.");   
    }

    /**  
     *  Compares the Plaintext file with the Deciphered file.
     *
     *    @return  true if files match, false if they do not
     */

    public static boolean compareFiles() throws IOException
    {

        Scanner pt = new Scanner(new File("C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoPlaintext.txt")); // Open the       plaintext file
        Scanner dc = new Scanner(new File("C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoDeciphered.txt"));  // Open the       deciphered file

        // Read through the files and compare them record by record.
        // If any of the records do not match, the files are not identical.

        while(pt.hasNextLine() && dc.hasNextLine())
            if(!pt.nextLine().equals(dc.nextLine())) return false;

        // If we have any records left over, then the files are not identical.

        if(pt.hasNextLine() || dc.hasNextLine()) return false;

        // The files are identical.

        return true;




    }
}
piy26
  • 1,574
  • 11
  • 21
  • While trying this solution, I received an error stating: no suitable method found for encode(java.lang.String) method sun.misc.CharacterEncoder.encode(byte[]) is not applicable (argument mismatch; java.lang.String cannot be converted to byte[]) method sun.misc.CharacterEncoder.encode(java.nio.ByteBuffer) is not applicable (argument mismatch; java.lang.String cannot be converted to java.nio.ByteBuffer) – Gagan May 11 '18 at 10:02
  • Thank you for your correction, but I still recieve the original error message of :javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher ........ I believe this is because after the first file is encrypted, it become out of the range of multiple of 16. Is the anyway to control this with an if statement or any other solution ? – Gagan May 11 '18 at 10:18
  • Check the corrected and working code. @MaartenBodewes: I had faced the issue earlier where passing the byte[] directly as encrypted text was resulting in an incorrect encryption and decryption operations. It fixed once I created a new String() from the byte[] and hence the suggestion. – piy26 May 11 '18 at 10:36
  • Removed the last comment because you fixed it. Still, the actual issue is resolved by using println (see my answer). Maybe you tested with a single line text file? – Maarten Bodewes May 12 '18 at 03:00
  • I have tried this solution and I still received an error. My program is able to encrypt the original file, but when it comes to decrypting, I received the following error: javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher I also tried changing the outputFile.write to outputFile.println and still received the same error. – Gagan May 12 '18 at 03:41