title: Not Encrypting & Decrypting Properly
body: <p>Something is funky with the following AES class I've written to do encryption and decryption. When I copy of the AES object and I choose to encrypt plain text, and then I immediately attempt to decrypt the text I just encrypted, it doesn't decrypt it fully (and does it differently every time).</p>
  
  <p>e.g. I'm initializing it with a simple JSP like this:</p>
  
  <pre><code>&lt;%@page import="com.myclass.util.AES"%&gt;
  &lt;%
          String hexMessage = "0xe800a86d90d2074fbf339aa70b6d0f62f047db15ef04c86b488a1dda3c6c4f2f2bbb444a8c709bbb4c29c7ff1f1e"; 
          String keyText = "12345678abcdefgh";//*/
  
          AES e = new AES();
          //e.setKey(keyText);
          String plaintext = "This should decode &amp; encode!";
          String ciphertext = e.encrypt(plaintext);
          out.println(ciphertext);
          out.println("&lt;BR&gt;");
          out.println(e.decrypt(ciphertext));    
  %&gt;
  </code></pre>
  
  <p>The output varies on each page load:
  One time:</p>
  
  <pre><code>0x663D64E6A0AE455AB3D25D5AF2F77C72202627EBA068E6DEBE5F22C31
  This should decoÁdìmèåV4ÉkÓ 
  </code></pre>
  
  <p>Another:</p>
  
  <pre><code>0x5F5CF31961505F01EA9D5B7D7BFC656BD3117725D2EA041183F48
  This s2??XêêÈ&amp;ÀÜ§F?ÒDÒ­?
  </code></pre>
  
  <p>etc:</p>
  
  <pre><code>0xC7178A34C59F74E5D68F7CE5ED655B670A0B4E715101B4DDC2122460E8
  Tà@¼R×ËÖ?_U?xÎÚ?Ba?b4r!©F 
  </code></pre>
  
  <p>The class I created is below:</p>
  
  <pre><code>package com.myclass.util;
  
  import java.io.UnsupportedEncodingException;
  import java.security.InvalidAlgorithmParameterException;
  import java.security.InvalidKeyException;
  import java.security.NoSuchAlgorithmException;
  import java.security.NoSuchProviderException;
  import java.security.Security;
  import java.util.regex.Pattern;
  import javax.crypto.*;
  import javax.crypto.spec.*;    
  
     public class AES {
         private static String provider = "AES/CTR/NoPadding";
         private static String providerkey = "AES";
         private static int size = 128;
         private SecretKeySpec key;
         private Cipher cipher;
         private byte[] ivBytes = new byte[size/8];
         private IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
  
  
         public AES() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException{
             Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
             KeyGenerator kgen = KeyGenerator.getInstance(providerkey);
             kgen.init(size); // 192 and 256 bits may not be available
             SecretKey skey = kgen.generateKey();
             byte[] raw = skey.getEncoded();
             key = new SecretKeySpec(raw, providerkey);
             cipher = Cipher.getInstance(provider);
             for(int x = 0; x &lt; (size/8); x++)
                 ivBytes[x] = 00;
             ivSpec = new IvParameterSpec(ivBytes);
         }
  
         public void setKey(String keyText){
             byte[] bText = new byte[size/8];
             bText = keyText.getBytes();
             key = new SecretKeySpec(bText, providerkey);
         }
  
         public void setIV(String ivText){
             setIV(ivText.getBytes());
         }
  
         public void setIV(byte[] ivByte){
             byte[] bText = new byte[size/8];
             bText = ivByte;
             ivBytes = bText;
             ivSpec = new IvParameterSpec(ivBytes);
         }
  
         public String encrypt(String message) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException{
             cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
             byte[] encrypted = cipher.doFinal(message.getBytes());
             return byteArrayToHexString(encrypted);
         }
         public String decrypt(String hexCiphertext) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException{
             cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
             byte[] dec = hexStringToByteArray(hexCiphertext);
             byte[] decrypted = cipher.doFinal(dec);
             return new String(decrypted);
         }
  
         private static String byteArrayToHexString( byte [] raw ) {
              String hex = "0x";
              String s = new String(raw);
              for(int x = 0; x &lt; s.length(); x++){
                  char[] t = s.substring(x, x + 1).toCharArray();
                  hex += Integer.toHexString((int) t[0]).toUpperCase();
              }
              return hex;
         }
  
         private static byte[] hexStringToByteArray(String hex) {
              Pattern replace = Pattern.compile("^0x");
              String s = replace.matcher(hex).replaceAll("");
  
              byte[] b = new byte[s.length() / 2];
              for (int i = 0; i &lt; b.length; i++){
                int index = i * 2;
                int v = Integer.parseInt(s.substring(index, index + 2), 16);
                b[i] = (byte)v;
              }
              return b;
         }
  
  
     }
  </code></pre>
  
  <p>Based on the varied results, I'm wondering if something is getting messed up with the IV somehow, but I don't really understand why...</p>
  
  <p>[EDIT] Looks like its not the IV, if I hard code that the decrypting still varies. If I hard code the key it stops varying, but still doesn't decrypt the text properly :-(.</p>
  
  <p>--------------------- ===================== ---------------------</p>
  
  <p>Adding the final solution I created below, based on owlstead's code and suggestions. It does the following:<br>
  1) Has a random key and iv on initialization.<br>
  2) Allows you to specify a key or iv as either a regular string, or as a hex encoded string.<br>
  3) Automatically truncates or null pads any given key or iv to make it the appropriate length.<br>
  <br>
  NOTE: Item #3 could be viewed as extremely insecure since it allows you to do something stupid. For my purposes I need it, but please use with caution. If you null pad a short string for a key, your content is not going to be very secure.<br></p>
  
  <p>--------------------- ===================== ---------------------</p>
  
  <pre><code>package com.myclass.util;
  
  import java.io.UnsupportedEncodingException;
  import java.nio.charset.Charset;
  import java.security.InvalidAlgorithmParameterException;
  import java.security.InvalidKeyException;
  import java.security.NoSuchAlgorithmException;
  import java.security.NoSuchProviderException;
  import java.security.spec.InvalidParameterSpecException;
  import java.util.regex.Pattern;
  import javax.crypto.BadPaddingException;
  import javax.crypto.Cipher;
  import javax.crypto.IllegalBlockSizeException;
  import javax.crypto.KeyGenerator;
  import javax.crypto.NoSuchPaddingException;
  import javax.crypto.SecretKey;
  import javax.crypto.spec.IvParameterSpec;
  import javax.crypto.spec.SecretKeySpec;
  
     public class AES {
         private static Charset PLAIN_TEXT_ENCODING = Charset.forName("UTF-8");
         private static String CIPHER_TRANSFORMATION = "AES/CTR/NoPadding";
         private static String KEY_TYPE = "AES";
         private static int KEY_SIZE_BITS = 128;
  
         private SecretKey key;
         private Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
         private byte[] ivBytes = new byte[KEY_SIZE_BITS/8];
  
     public AES() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidParameterSpecException, InvalidKeyException, InvalidAlgorithmParameterException{
         KeyGenerator kgen = KeyGenerator.getInstance(KEY_TYPE);
         kgen.init(KEY_SIZE_BITS); 
         key = kgen.generateKey();
         cipher.init(Cipher.ENCRYPT_MODE, key);
         ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
     }
  
     public String getIVAsHex(){
         return byteArrayToHexString(ivBytes);
     }
  
     public String getKeyAsHex(){
         return byteArrayToHexString(key.getEncoded());
     }
  
     public void setStringToKey(String keyText){
         setKey(keyText.getBytes());
     }
  
     public void setHexToKey(String hexKey){
         setKey(hexStringToByteArray(hexKey));
     }
  
     private void setKey(byte[] bArray){
         byte[] bText = new byte[KEY_SIZE_BITS/8];
         int end = Math.min(KEY_SIZE_BITS/8, bArray.length);
         System.arraycopy(bArray, 0, bText, 0, end);
         key = new SecretKeySpec(bText, KEY_TYPE);
     }
  
     public void setStringToIV(String ivText){
         setIV(ivText.getBytes());
     }
  
     public void setHexToIV(String hexIV){
         setIV(hexStringToByteArray(hexIV));
     }
  
     private void setIV(byte[] bArray){
         byte[] bText = new byte[KEY_SIZE_BITS/8];
         int end = Math.min(KEY_SIZE_BITS/8, bArray.length);
         System.arraycopy(bArray, 0, bText, 0, end);
         ivBytes = bText;
     }
  
      public String encrypt(String message) throws InvalidKeyException,
              IllegalBlockSizeException, BadPaddingException,
              InvalidAlgorithmParameterException {
          cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivBytes));
          byte[] encrypted = cipher.doFinal(message.getBytes(PLAIN_TEXT_ENCODING));
          return byteArrayToHexString(encrypted);
      }
  
      public String decrypt(String hexCiphertext)
              throws IllegalBlockSizeException, BadPaddingException,
              InvalidKeyException, InvalidAlgorithmParameterException,
              UnsupportedEncodingException {
          cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivBytes));
          byte[] dec = hexStringToByteArray(hexCiphertext);
          byte[] decrypted = cipher.doFinal(dec);
          return new String(decrypted, PLAIN_TEXT_ENCODING);
      }
  
      private static String byteArrayToHexString(byte[] raw) {
          StringBuilder sb = new StringBuilder(2 + raw.length * 2);
          sb.append("0x");
          for (int i = 0; i &lt; raw.length; i++) {
              sb.append(String.format("%02X", Integer.valueOf(raw[i] &amp; 0xFF)));
          }
          return sb.toString();
      }
  
     private static byte[] hexStringToByteArray(String hex) {
          Pattern replace = Pattern.compile("^0x");
          String s = replace.matcher(hex).replaceAll("");
  
          byte[] b = new byte[s.length() / 2];
          for (int i = 0; i &lt; b.length; i++){
            int index = i * 2;
            int v = Integer.parseInt(s.substring(index, index + 2), 16);
            b[i] = (byte)v;
          }
          return b;
     }
  </code></pre>
  
  <p>}</p>