title: File Encryption with the Java Cryptography Architecture using AES with a 128-bit key and PBKDF2
body: <p>I am getting this error when I am decrypting a file</p>
  
  <p>I am using PBKDF2 to convert a passphrase to a key and then using it. The encryption is working good but when I am trying to decrypt the same file it is giving the below error. The decrypted file gives a correct data except for the last few lines(probably the padding area). I have debugged it by outputting the IV and key while encrypting and decrypting and they both are the same but the error still exists. </p>
  
  <pre class="lang-java prettyprint-override"><code>public class FileEncryptorSkeleton{
  
      private static final String progName = "FileEncryptor";
      private static final int bufSize = 128;
  
      /**
          * @param args
          */
      public static void main(String[] args) throws UnsupportedEncodingException {
  
          BufferedInputStream in = null;          // A buffered input stream to read from
          BufferedOutputStream out = null;        // And a buffered output stream to write to
          SecretKeyFactory kf = null;             // Something to create a key for us
          KeySpec ks = null;                      // This is how we specify what kind of key we want it to generate
          byte[] salt = new byte[20];             // Some salt for use with PBKDF2, only not very salty
          SecretKey key = null;                   // The key that it generates
          Cipher cipher = null;                   // The cipher that will do the real work
          SecretKeySpec keyspec = null;           // How we pass the key to the Cipher
          int bytesRead = 0;                      // Number of bytes read into the input file buffer
          byte[] iv = new byte[16];
          // First, check the user has provided all the required arguments, and if they haven't, tell them then exit
          if(args.length != 4) {
              printUsageMessage(); System.exit(1);
          }
  
          // Open the input file
          try {
              in = new BufferedInputStream(new FileInputStream(args[1]));
          } catch (FileNotFoundException e) {
              printErrorMessage("Unable to open input file: " + args[1], null);
              System.exit(1);
          }
  
          // And then the output file
          try {
              out = new BufferedOutputStream(new FileOutputStream(args[2]));
          } catch (FileNotFoundException e) {
              printErrorMessage("Unable to open output file: " + args[2], e);
              System.exit(1);
          }
  
              try {
                  kf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
              } catch (NoSuchAlgorithmException ex) {
                  Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
              }
  
          // Set up a KeySpec for password-based key generation of a 128-bit key
              ks = new PBEKeySpec(args[3].toCharArray(), salt, 1000, 128);
  
  
  
  
          // Now run the passphrase through PBKDF2 to get the key
              try {
                      key = kf.generateSecret(ks);
                      }catch(InvalidKeySpecException e){
          System.exit(1);
                      }
  
          // Get the byte encoded key value as a byte array
          byte[] aeskey = key.getEncoded();
  
          // Now generate a Cipher object for AES encryption in ECBC mode with PKCS #5 padding
          // Use ECB for the first task, then switch to CBC for versions 2 and 3
          try {
              cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
          } catch (NoSuchAlgorithmException e) {
              printErrorMessage("No Such Algorithm Exception when creating main cipher", e);
              System.exit(2);
          } catch (NoSuchPaddingException e) {
              printErrorMessage("No Such Padding Exception when creating main cipher",e);
              System.exit(2);
          }
  
          // Set a variable to indicate whether we're in encrypt or decrypt mode, based upon args[0]
          int cipherMode = -1;
          char mode = Character.toLowerCase(args[0].charAt(0));
          switch (mode) {
              case 'e' : cipherMode = Cipher.ENCRYPT_MODE; break;
              case 'd' : cipherMode = Cipher.DECRYPT_MODE; break;
              default: printUsageMessage(); System.exit(1);
          }
  
          // Set up a secret key specification, based on the 16-byte (128-bit) AES key array previously generated
          keyspec = new SecretKeySpec(aeskey, "AES");
          IvParameterSpec ivspec = new IvParameterSpec(iv);
          // Now initialize the cipher in the right mode, with the keyspec and the ivspec
          try {
  
              cipher.init(cipherMode, keyspec,ivspec);
  
          } catch (InvalidKeyException e) {
              printErrorMessage("Invalid Key Spec",e); System.exit(2);
          } catch (InvalidAlgorithmParameterException ex) {
                  Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
              }
  
          // Set up some input and output byte array buffers
          byte[] inputBuffer = new byte[bufSize];
          byte[] outputBuffer = null;
  
          // "Prime the pump" - we've got to read something before we can encrypt it
          // and not encrypt anything if we read nothing.
          try {
              bytesRead = in.read(inputBuffer);
          } catch (IOException e) {
              printErrorMessage("Error reading input file " + args[1],e); System.exit(1);
          }
  
          // As long as we've read something, loop around encrypting, writing and reading
          // bytesRead will be zero if nothing was read, or -1 on EOF - treat them both the same
          while (bytesRead &gt; 0) {
  
              // Now encrypt this block
              outputBuffer = cipher.update(inputBuffer.toString().getBytes("UTF-8"));         
              // Write the generated block to file
              try {
                  out.write(outputBuffer);
              } catch (IOException e) {
                  printErrorMessage("Error writing to output file " + args[2],e); System.exit(1);
              }
  
              // And read in the next block of the file
              try {
                  bytesRead = in.read(inputBuffer);
              } catch (IOException e) {
                  printErrorMessage("Error reading input file " + args[1],e); System.exit(1);
              }
          }
  
              try {
                  // Now do the final processing
                  outputBuffer =cipher.doFinal(inputBuffer.toString().getBytes("UTF-8"));
                  cipher.init(cipherMode, keyspec,ivspec);
                  System.out.println(ivspec+"   "+cipherMode+"   "+keyspec);
              } catch (IllegalBlockSizeException ex) {
                  Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
              } catch (BadPaddingException ex) {
                  Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
              } catch (InvalidKeyException ex) {
                  Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
              } catch (InvalidAlgorithmParameterException ex) {
                  Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
              }
  
          // Write the final block of output
          try {
              out.write(outputBuffer);
          } catch (IOException e) {
              printErrorMessage("Error on final write to output file " + args[2],e); System.exit(1);
          }
  
          // Close the output files
          try {
              in.close();
              out.close();
          } catch (IOException e) {
              printErrorMessage("Error closing file", e);
          }
  
          // If we were continuing beyond this point, we should really overwrite key material, drop KeySpecs, etc.
      }
  
      /**
          * Print an error message on , optionally picking up additional detail from
          * a passed exception
          * @param errMsg
          * @param e
          */
      private static void printErrorMessage(String errMsg, Exception e) {
          System.err.println(errMsg);
          if (e != null) 
              System.err.println(e.getMessage());
      }
  
      /**
          * Print a usage message
          */
      private static void printUsageMessage() {
          System.out.println(progName + " $Revision: 1.1 $: Usage: " + progName + " E/D infile outfile passphrase");
      }
  
  }
  
  </code></pre>
  
  <blockquote>
  <pre><code>Oct 18, 2019 11:27:46 PM FileEncryptorSkeleton main
  SEVERE: null
  javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
          at com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975)
          at com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056)
          at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
          at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
          at javax.crypto.Cipher.doFinal(Cipher.java:2164)
          at FileEncryptorSkeleton.main(FileEncryptorSkeleton.java:174)
  </code></pre>
  </blockquote>