views:

141

answers:

4

Hi, I got an exception in the following code for AES algorithm in java.

Code decryptes an encrypted string and returns the original string.

Plz help me to fix this.

Code:

public class AES 

{

public byte[] encrypted;

 public byte[] original;

 public String originalString;

public static String asHex (byte buf[]) 

{ 

StringBuffer strbuf = new StringBuffer(buf.length * 2);

 int i; for (i = 0; i < buf.length; i++) 

{

 if (((int) buf[i] & 0xff) < 0x10) strbuf.append("0"); 

strbuf.append(Long.toString((int) buf[i] & 0xff, 16)); 

}

 return strbuf.toString();

 }

 public String AESencryptalgo(byte[] text)

 { 

String newtext=""; 

// Get the KeyGenerator

 try

 {

    KeyGenerator kgen = KeyGenerator.getInstance("AES");

    kgen.init(128); // 192 and 256 bits may not be available

 // Generate the secret key specs. 

SecretKey skey = kgen.generateKey();

 byte[] raw = skey.getEncoded();

 SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");

 // Instantiate the cipher Cipher cipher = Cipher.getInstance("AES"); 

cipher.init(Cipher.ENCRYPT_MODE, skeySpec); encrypted = cipher.doFinal(text); 

System.out.println("encrypted string: " + asHex(encrypted)); 

cipher.init(Cipher.DECRYPT_MODE, skeySpec); original = cipher.doFinal(encrypted); 

originalString = new String(original); System.out.println("Original string: " + originalString + " " + asHex(original));

 } 

catch(Exception e)

 { } 

finally 

{

 newtext=new String(encrypted);

 System.out.println("ENCRYPTED "+newtext);

//AESdecryptalgo(newtext.getBytes()); 

return newtext;

 }

 } 

public String AESdecryptalgo(byte[] text)

 { 

// Get the KeyGenerator

 try

 {

 KeyGenerator kgen = KeyGenerator.getInstance("AES");

 kgen.init(128); // 192 and 256 bits may not be available 

// Generate the secret key specs. 

SecretKey skey = kgen.generateKey();

 byte[] raw = skey.getEncoded(); 

SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); 

// Instantiate the cipher

 Cipher cipher = Cipher.getInstance("AES"); 

cipher.init(Cipher.DECRYPT_MODE, skeySpec);

 original = cipher.doFinal(text); //Exception occurs here

 originalString = new String(original);

 System.out.println("Original string: " + originalString + " " + asHex(original)); 

}

 catch(Exception e)

 {

 System.out.println("exception"); 

}

 finally

{ 

System.out.println("DECRYPTED "+originalString);

 return originalString;

 } 

} 

public static void main(String[] args)

{

AES a=new AES();

a.AESencryptalgo("hello".getBytes());

System.out.println(); 

}} 
`

exception:

javax.crypto.BadPaddingException: Given final block not properly padded 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*..) 
A: 

Well if this is the error Input length must be multiple of 16 when decrypting with padded cipher. Than the answear is obvious, the length of your buffer must be a multiple of 16. Have you checked the length of buf[]?

inf.ig.sh
+5  A: 

According to Java™ Cryptography Architecture (JCA) Reference Guide (emphasis mine):

Cipher objects are obtained by using one of the Cipher getInstance() static factory methods. Here, the algorithm name is slightly different than with other engine classes, in that it specifies not just an algorithm name, but a "transformation". A transformation is a string that describes the operation (or set of operations) to be performed on the given input to produce some output. A transformation always includes the name of a cryptographic algorithm (e.g., DES), and may be followed by a mode and padding scheme.

A transformation is of the form:

  • "algorithm/mode/padding" or
  • "algorithm"

For example, the following are valid transformations:

  • "DES/CBC/PKCS5Padding"
  • "DES"

If just a transformation name is specified, the system will determine if there is an implementation of the requested transformation available in the environment, and if there is more than one, returns there is a preferred one.

If both a transformation name and a package provider are specified, the system will determine if there is an implementation of the requested transformation in the package requested, and throw an exception if there is not.

If no mode or padding is specified, provider-specific default values for the mode and padding scheme are used. For example, the SunJCE provider uses ECB as the default mode, and PKCS5Padding as the default padding scheme for DES, DES-EDE and Blowfish ciphers. This means that in the case of the SunJCE provider:

Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");

and

Cipher c1 = Cipher.getInstance("DES");

are equivalent statements.

Using modes such as CFB and OFB, block ciphers can encrypt data in units smaller than the cipher's actual block size. When requesting such a mode, you may optionally specify the number of bits to be processed at a time by appending this number to the mode name as shown in the "DES/CFB8/NoPadding" and "DES/OFB32/PKCS5Padding" transformations. If no such number is specified, a provider-specific default is used. (For example, the SunJCE provider uses a default of 64 bits for DES.) Thus, block ciphers can be turned into byte-oriented stream ciphers by using an 8 bit mode such as CFB8 or OFB8.

Appendix A of this document contains a list of standard names that can be used to specify the algorithm name, mode, and padding scheme components of a transformation.

The objects returned by factory methods are uninitialized, and must be initialized before they become usable.

Because your code does not specify mode or padding, provider-specific default values are being used. It appears that your provider is SunJCE and that it's default padding is probably "NoPadding". With this padding, you are responsible for ensuring that the size of the byte array being encrypted is a multiple of the number of bytes in the secret key. You can make you're life easier by specifying the mode and padding in your transformation:

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

WARNING: You should not use ECB mode in real code. Try CBC instead.

Update: I didn't think it was fair to recommend CBC mode without offering a little sample of how it works:

public static void main(String... args) throws Exception {
    byte[] data = "hello".getBytes();

    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    keyGenerator.init(128); // 192 and 256 bits may not be available

    SecretKey secretKey = keyGenerator.generateKey();

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    // By initializing the cipher in CBC mode, an "initialization vector" has been randomly
    // generated. This initialization vector will be necessary to decrypt the encrypted data.
    // It is safe to store the initialization vector in plain text for later use. You can obtain
    // it's bytes by calling iv.getIV().
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
    byte[] encryptedData = cipher.doFinal(data);

    // When decrypting the encrypted data, you must provide the initialization vector used
    // during the encryption phase.
    cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
    byte[] decryptedData = cipher.doFinal(encryptedData);

    if (!Arrays.equals(data, decryptedData)) {
        throw new Exception("Data was not decrypted successfully");
    }
}
Adam Paynter
+1 for ECB warning + sample
Jan Gorzny
Thanks Adam..but I am still getting problems with this..
sparkle
@sparkle: Is it the same error?
Adam Paynter
No, I am gtting IILegal block size exception.The new version of the code is at....http://stackoverflow.com/questions/3181535/exception-in-aes-decryption-algorithm-in-java
sparkle
A: 

Your code manages to get almost everything wrong. Just for a start, your mistakes include:

  1. generating a new random symmetric key before encryption and decryption. You have to use the same key to decrypt that was used to encrypt.
  2. Using String as a container for binary data. cipher output is cannot be reliably made into a String unless you use an encoding, such as base64.
  3. Your exception handling is incorrect.

Furthermore, your code runs without exceptions for me.

GregS
A: 

My code is a controller class that is used to generate a secret key and return its bytes to be written directly into a FileOutputStream, and a function to encrypt data using the Seecret Key bytes read from the same file it was written to and this has no problem.

The problem occurs when I try to decrypt a String type passed to the function and the key bytes read from the file, I get a "Pad Block Corrupted" exception, and that is because of when I use encrypted.getBytes()... I have figured I should convert encrypted from Hex represented as String back to byte[] and I did a lot of research and couldn't figure out how to do so.

I tried using the toByteArray() function included in my code but in vain.

This is my Encryption output to help you visualize: 46c7c89b9194334b61a47b2d96c656f5

I need to fix this as fast as possible as it's a part of my graduation project due on next Monday and this issue is giving me hell, please if you know of a solution I will be very thankful.

public class SecurityController {
 Cipher cipher;
 SecretKeySpec skeySpec;

 public byte[] generateKey() throws Exception{
  KeyGenerator kgen = KeyGenerator.getInstance("AES");

  kgen.init(128); // 192 and 256 bits may not be available


  // Generate the secret key specs.
  SecretKey skey = kgen.generateKey();

  byte[] raw;

  return raw = skey.getEncoded();
 }

 public String encrypt(String message, byte[] keyBytes) throws Exception{

//  skeySpec = new SecretKeySpec(keyBytes,0, 16, "AES");   

  // Instantiate the cipher
  cipher = Cipher.getInstance("AES");  

  cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
  byte[] encrypted = cipher.doFinal(message.getBytes());    
  return toHex(encrypted);

 }

 public String decrypt(String encrypted, byte[] keyBytes) throws Exception {

//  skeySpec = new SecretKeySpec(keyBytes, 0, 16, "AES");

  // Instantiate the cipher
  cipher = Cipher.getInstance("AES");     

  cipher.init(Cipher.DECRYPT_MODE, skeySpec);
  byte[] original = cipher.doFinal((encrypted.getBytes()));

  return toHex(original);
 }

 public static String toHex (byte buf[]) {

  StringBuffer strbuf = new StringBuffer(buf.length * 2);
  int i;


  for (i = 0; i < buf.length; i++) {
   if (((int) buf[i] & 0xff) < 0x10)
    strbuf.append("0");

   strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
  }
  return strbuf.toString();

 }

 public static byte[] toByteArray(String s) {
     int len = s.length();
     byte[] data = new byte[len / 2];
     for (int i = 0; i < len; i += 2) {
         data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                              + Character.digit(s.charAt(i+1), 16));
     }
     return data;
 }

}
Sharkawy