Sabbir Hussain Meraj
Sabbir Hussain Meraj

Reputation: 11

Java - AES Encryption Invalid Key Size Error

I am getting Invalid Key Size error when I try to use 256 bits key in AES encryption. I know I have to use JCE unlimited policy. I am using Jre1.8.0_181. So, I have set crypto.policy=unlimited in java.security file. I have also checked that I am using the correct jvm.

Even I am getting "unlimited" value whenever I use this code.

String value = System.getProperties("crypto.policy"); //getting value = "unlimited"

But after this, I am getting key size to 128 bits.

Cipher.getMaxAllowedKeyLength("AES");

I have followed this code from the following site: https://howtodoinjava.com/java/java-security/aes-256-encryption-decryption/

Code Added

  public static void main(String[] args) {
    String originalString = "howtodoinjava.com";
 
    String encryptedString = AES256.encrypt(originalString);
    String decryptedString = AES256.decrypt(encryptedString);
 
    System.out.println(originalString);
    System.out.println(encryptedString);
    System.out.println(decryptedString);
  }
}
public class AES256 {
  private static final String SECRET_KEY = "my_super_secret_key_ho_ho_ho";
  private static final String SALT = "ssshhhhhhhhhhh!!!!";
 
  public static String decrypt(String strToDecrypt) {
    try {
      byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      IvParameterSpec ivspec = new IvParameterSpec(iv);
 
      SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
      KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT.getBytes(), 65536, 256);
      SecretKey tmp = factory.generateSecret(spec);
      SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
      cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
      return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
    } catch (Exception e) {
      System.out.println("Error while decrypting: " + e.toString());
    }
    return null;
  }
public static String encrypt(String strToEncrypt) {
    try {
      byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      IvParameterSpec ivspec = new IvParameterSpec(iv);
 
      SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
      KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT.getBytes(), 65536, 256);
      SecretKey tmp = factory.generateSecret(spec);
      SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
      return Base64.getEncoder()
          .encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
    } catch (Exception e) {
      System.out.println("Error while encrypting: " + e.toString());
    }
    return null;
  }

}

Upvotes: 1

Views: 1901

Answers (2)

Sabbir Hussain Meraj
Sabbir Hussain Meraj

Reputation: 11

Though it was displaying jre1.8.0_181 in cmd but my project was running on jre1.7. As @dave_thompson_085 stated I used System.properties("java.version") to get the jre version.

Upvotes: 0

Michael Fehr
Michael Fehr

Reputation: 6394

Thanks for editing your post. Your given code runs successfully on my system and seems to be correct.

My simple program will test for something that is called "unlimited crypto policies". Due to US export restrictions, the US companies were not allowed to ship their programs with an unlimited key format. You are using AES with a 32 bytes long key, so it is called AES-256 (32 bytes * 8 bit = 256) but the government allowed only 16 bytes long keys ("AES 128").

My program runs the test depending on the Java version that is in use. For an easier test, I set up a JFiddle-link where you can test the program directly and for convinience change the Java version. The JFiddle is available under this link: https://www.jdoodle.com/a/2nZe

When running the test the Java version is JDK 11.0.4, but you can change (please scroll down) to JDK 1.8.0_66.

output Java 11:

Check for unlimited crypto policies
restricted cryptography: false Notice: 'false' means unlimited policies
Security properties: unlimited
Max AES key length = 2147483647

output for Java 1.8.0_66:

restricted cryptography: true Notice: 'false' means unlimited policies
Security properties: null
Max AES key length = 128

To enable the unlimited policy for older Java's like 1.8.0_66 your clients have to load two small files from Oracle's pages and place them in their Java VM. As you stated your manager does not allow this, there is only one way left - you have to use (only) 16 byte long AES keys.

Security warning: due to upcoming Quantum computers, the actual recommendation for AES keys is 32 byte long keys.

code:

import javax.crypto.Cipher;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

public class UnlimitedCryptoPoliciesCheck {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        System.out.println("Check for unlimited crypto policies");
        //Security.setProperty("crypto.policy", "limited"); // muss ganz am anfang gesetzt werden !
        System.out.println("restricted cryptography: " + restrictedCryptography() + " Notice: 'false' means unlimited policies"); // false mean unlimited crypto
        System.out.println("Security properties: " + Security.getProperty("crypto.policy"));
        int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
        System.out.println("Max AES key length = " + maxKeyLen);
  }

    /**
     * Determines if cryptography restrictions apply.
     * Restrictions apply if the value of {@link Cipher#getMaxAllowedKeyLength(String)} returns a value smaller than {@link Integer#MAX_VALUE} if there are any restrictions according to the JavaDoc of the method.
     * This method is used with the transform <code>"AES/CBC/PKCS5Padding"</code> as this is an often used algorithm that is <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#impl">an implementation requirement for Java SE</a>.
     *
     * @return <code>true</code> if restrictions apply, <code>false</code> otherwise
     * https://stackoverflow.com/posts/33849265/edit, author Maarten Bodewes
     */
    public static boolean restrictedCryptography() {
        try {
            return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding") < Integer.MAX_VALUE;
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalStateException("The transform \"AES/CBC/PKCS5Padding\" is not available (the availability of this algorithm is mandatory for Java SE implementations)", e);
        }
    }
}

Upvotes: 1

Related Questions