53

I am working on a text encryption and decryption project (following Struts 2)

Whenever I enter the password and the plain text I get a Invalid AES Key Length error.

The Service Class

package com.anoncrypt.services;

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class SymAES
{
    private static final String ALGORITHM = "AES";
    private static byte[] keyValue= new byte[] { 'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' };

     public  String encode(String valueToEnc) throws Exception {
        Key key = new SecretKeySpec(keyValue, ALGORITHM);
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.ENCRYPT_MODE, key);
        byte[] encValue = c.doFinal(valueToEnc.getBytes());
        String encryptedValue = new BASE64Encoder().encode(encValue);
        return encryptedValue;
    }

    public  String decode(String encryptedValue) throws Exception {
        Key key = new SecretKeySpec(keyValue, ALGORITHM);
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedValue);
        byte[] decValue = c.doFinal(decordedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }

    public  void start(String passcode)throws Exception
    {
        keyValue = passcode.getBytes();
    }
}

And this is the error

java.security.InvalidKeyException: Invalid AES key length: 6 bytes
    com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
    com.sun.crypto.provider.ElectronicCodeBook.init(ElectronicCodeBook.java:93)
    com.sun.crypto.provider.CipherCore.init(CipherCore.java:582)
    com.sun.crypto.provider.CipherCore.init(CipherCore.java:458)
    com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:307)
    javax.crypto.Cipher.implInit(Cipher.java:797)
    javax.crypto.Cipher.chooseProvider(Cipher.java:859)
    javax.crypto.Cipher.init(Cipher.java:1229)
    javax.crypto.Cipher.init(Cipher.java:1166)
    com.anoncrypt.services.SymAES.encode(SymAES.java:35)
    com.anoncrypt.actions.SymEncrypt.execute(SymEncrypt.java:24)
Vic Seedoubleyew
  • 9,888
  • 6
  • 55
  • 76
Rishabh Upadhyay
  • 559
  • 1
  • 4
  • 7
  • I guess you don't have Unlimited Strength Jurisdiction Policy (which are not default with JRE) Files.http://stackoverflow.com/questions/2568841/aes-encryption-java-invalid-key-length – kosa Mar 30 '15 at 19:13
  • i do have Unlimited Strength Jurisdiction Policy jars@nambari – Rishabh Upadhyay Mar 30 '15 at 19:20
  • 1
    16 bytes means 16 characters here in layman terms. – Kumar Abhishek Dec 18 '17 at 03:48
  • 1
    No, a character can be more than one byte, best lookup unicode. ‍‍‍ is 25 byes: (F0 9F 91 A8 E2 80 8D F0 9F 91 A9 E2 80 8D F0 9F 91 A6 E2 80 8D F0 9F 91 A6). Some thing simpler, € is 3 bytes (E2 82 AC), it is the Euro currency symbol. Hint: You can delete your answer. – zaph Dec 18 '17 at 03:59
  • @zaph Depends on encoding you use.. € is 3 bytes in UTF-8 I think? Looking at unicode would not help you, you need the encoding used. – Koray Tugay Mar 09 '21 at 18:48

4 Answers4

90

Things to know in general:

  1. Key != Password
  • SecretKeySpec expects a key, not a password. See below
  1. It might be due to a policy restriction that prevents using 32 byte keys. See other answer on that

In your case

The problem is number 1: you are passing the password instead of the key.

AES only supports key sizes of 16, 24 or 32 bytes. You either need to provide exactly that amount or you derive the key from what you type in.

There are different ways to derive the key from a passphrase. Java provides a PBKDF2 implementation for such a purpose.

I used erickson's answer to paint a complete picture (only encryption, since the decryption is similar, but includes splitting the ciphertext):

SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);

KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 1000000, 256); // AES-256
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] key = f.generateSecret(spec).getEncoded();
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");

byte[] ivBytes = new byte[16];
random.nextBytes(ivBytes);
IvParameterSpec iv = new IvParameterSpec(ivBytes);

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] encValue = c.doFinal(valueToEnc.getBytes());

byte[] finalCiphertext = new byte[encValue.length+2*16];
System.arraycopy(ivBytes, 0, finalCiphertext, 0, 16);
System.arraycopy(salt, 0, finalCiphertext, 16, 16);
System.arraycopy(encValue, 0, finalCiphertext, 32, encValue.length);

return finalCiphertext;

Other things to keep in mind:

  • Always use a fully qualified Cipher name. AES is not appropriate in such a case, because different JVMs/JCE providers may use different defaults for mode of operation and padding. Use AES/CBC/PKCS5Padding. Don't use ECB mode, because it is not semantically secure.
  • If you don't use ECB mode then you need to send the IV along with the ciphertext. This is usually done by prefixing the IV to the ciphertext byte array. The IV is automatically created for you and you can get it through cipherInstance.getIV().
  • Whenever you send something, you need to be sure that it wasn't altered along the way. It is hard to implement a encryption with MAC correctly. I recommend you to use an authenticated mode like CCM or GCM.
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • 2
    Additional thing to keep in mind, for key size over 128 bit (16 bytes), the jre needs to be setup with the proper permissions. For more information see this question and answer : https://stackoverflow.com/questions/6481627/java-security-illegal-key-size-or-default-parameters. In the code that can be accessed via `Cipher.getMaxAllowedKeyLength("AES")` that return a value in bit not bytes. – bric3 Feb 28 '18 at 12:02
  • How can we use **GCMParameterSpec**? – IgorGanapolsky May 27 '18 at 18:50
  • @IgorGanapolsky No idea, have a look yourself: https://stackoverflow.com/search?q=%5Bjava%5D+GCMParameterSpec+is%3Aanswer – Artjom B. May 27 '18 at 20:25
  • @IgorGanapolsky This looks like a valid example: https://stackoverflow.com/a/44429596/1816580 – Artjom B. May 27 '18 at 21:06
  • @ArtjomB. Hmm, it doesn't mention encrypt/decrypt – IgorGanapolsky May 27 '18 at 22:24
  • byte[] key = f.generateSecret(spec).getEncoded(); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); c.init(Cipher.ENCRYPT_MODE, keySpec, iv); – Eugene Chung Nov 08 '18 at 14:38
  • Do I need the same "salt" to decrypt? – ed22 Aug 08 '21 at 17:56
  • 1
    @ed22 Yes, you do, but the salt doesn't need to be secret (it can be secret but then you would need to transmit the salt to the receiver somehow). – Artjom B. Aug 13 '21 at 17:46
11

I was facing the same issue then i made my key 16 byte and it's working properly now. Create your key exactly 16 byte. It will surely work.

vineet patel
  • 135
  • 1
  • 6
6

You can verify the key length limit:

int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
System.out.println("MaxAllowedKeyLength=[" + maxKeyLen + "].");
AOL
  • 61
  • 1
  • 2
-2

You can use this code, this code is for AES-256-CBC or you can use it for other AES encryption. Key length error mainly comes in 256-bit encryption.

This error comes due to the encoding or charset name we pass in the SecretKeySpec. Suppose, in my case, I have a key length of 44, but I am not able to encrypt my text using this long key; Java throws me an error of invalid key length. Therefore I pass my key as a BASE64 in the function, and it converts my 44 length key in the 32 bytes, which is must for the 256-bit encryption.

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.Security;
import java.util.Base64;

public class Encrypt {

    static byte [] arr = {1,2,3,4,5,6,7,8,9};

    // static byte [] arr = new byte[16];

      public static void main(String...args) {
        try {
         //   System.out.println(Cipher.getMaxAllowedKeyLength("AES"));
            Base64.Decoder decoder = Base64.getDecoder();
            // static byte [] arr = new byte[16];
            Security.setProperty("crypto.policy", "unlimited");
            String key = "Your key";
       //     System.out.println("-------" + key);

            String value = "Hey, i am adnan";
            String IV = "0123456789abcdef";
       //     System.out.println(value);
            // log.info(value);
          IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
            //    IvParameterSpec iv = new IvParameterSpec(arr);

        //    System.out.println(key);
            SecretKeySpec skeySpec = new SecretKeySpec(decoder.decode(key), "AES");
         //   System.out.println(skeySpec);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //    System.out.println("ddddddddd"+IV);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
       //     System.out.println(cipher.getIV());

            byte[] encrypted = cipher.doFinal(value.getBytes());
            String encryptedString = Base64.getEncoder().encodeToString(encrypted);

            System.out.println("encrypted string,,,,,,,,,,,,,,,,,,,: " + encryptedString);
            // vars.put("input-1",encryptedString);
            //  log.info("beanshell");
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
    }
}
David Buck
  • 3,752
  • 35
  • 31
  • 35
  • Please do not use the above code as it uses a fixed initialization vector, the key should not get derived from a string but better using a key derivation like PBKDF2. The conversions from string to byte array and vice versa are without encodings so there is a high chance that decryption will fail on another systems with different standard encoding. – Michael Fehr Aug 20 '20 at 07:42
  • the IV vector is used for my own purpose, if you have your own you can use there, if you don't want to use any initialized IV vector then use " static byte [] arr = new byte[16];" this line and pass this arr vector in the place of IV.getBytes(); – The_real_codder Aug 20 '20 at 17:31