Why Does My Aes Encryption Throws an Invalidkeyexception

Why does my AES encryption throws an InvalidKeyException?

If you use a block-chaining mode like CBC, you need to provide an IvParameterSpec to the Cipher as well.

So you can initialize an IvParameterSpec like this:

    // build the initialization vector.  This example is all zeros, but it 
// could be any value or generated using a random number generator.
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);

Then for encryption, change your code where you init the cipher to this:

ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);

And where you decrypt:

c.init(Cipher.DECRYPT_MODE, keySpec, ivspec);

So your complete code should look like this (It works for me):

    public class KeyHandler {

Scanner scan = new Scanner(System.in);

public KeyHandler() {
try {
startMenu();
} catch (Exception e) {
System.out.println("fel någonstanns :)");
}
}

public void startMenu() throws Exception {

System.out.println("Hej. Med detta program kan du generera en hemlig nyckel" + "\n" + "Vill du:" + "\n" + "1. Generera en nyckel" + "\n" + "2. Avsluta");
int val = Integer.parseInt(scan.nextLine());
do {
switch (val) {
case 1:
generateKey();
break;
case 2:
System.exit(1);

default:
System.out.println("Du måste välja val 1 eller 2");
}
} while (val != 3);
}

public void generateKey() throws Exception {
try {
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(128);

SecretKey key = gen.generateKey();
byte[] keyBytes = key.getEncoded();
System.out.print("Ge nyckeln ett filnamn: ");
String filename = scan.next();
System.out.println("Genererar nyckeln...");
FileOutputStream fileOut = new FileOutputStream(filename);
fileOut.write(keyBytes);
fileOut.close();
System.out.println("Nyckeln är genererad med filnamnet: " + filename + "...");
System.exit(1);
} catch (NoSuchAlgorithmException e) {
}

}

public static void main(String[] args) {
new KeyHandler();
}
}

public class EncryptHandler {

private String encryptedDataString;
private Cipher ecipher;
AlgorithmParameterSpec paramSpec;
byte[] iv;

public EncryptHandler(String dataString, String secretKey, String encryptedDataString) {
this.encryptedDataString = encryptedDataString;
try {
encryptFile(dataString, secretKey);
} catch (Exception e) {
}
}

public void encryptFile(String dataString, String secretKey) throws Exception {

FileInputStream fis = new FileInputStream(secretKey);
ByteArrayOutputStream baos = new ByteArrayOutputStream();

int theByte = 0;
while ((theByte = fis.read()) != -1) {
baos.write(theByte);
}
fis.close();

byte[] keyBytes = baos.toByteArray();
baos.close();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

// build the initialization vector. This example is all zeros, but it
// could be any value or generated using a random number generator.
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);

try {
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);

} catch (Exception e) {
e.printStackTrace();
}

System.out.println("Encrypting file...");
try {

encryptStream(new FileInputStream(dataString), new FileOutputStream(encryptedDataString));
} catch (Exception e) {
e.printStackTrace();
}

}

public void encryptStream(InputStream in, OutputStream out) {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
try {
out = new CipherOutputStream(out, ecipher);

// read the cleartext and write it to out
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);

}
bOut.writeTo(out);
out.close();
bOut.reset();

} catch (java.io.IOException e) {
}

}

public static void main(String[] args) {
String data = "test.txt";
String keyFileName = "a";
String encryptedFile = "krypterad.txt";
//String encryptedFile =args[2];
new EncryptHandler(data, keyFileName, encryptedFile);
}
}

public class DecryptHandler {

public DecryptHandler() {

try {
decryptFile();
} catch (Exception e) {
e.printStackTrace();
System.out.println("något gick fel :) ");
}
}

public void decryptFile() throws Exception {
byte[] buf = new byte[1024];
String keyFilename = "hemlig";
FileInputStream fis = new FileInputStream(keyFilename);
ByteArrayOutputStream baos = new ByteArrayOutputStream();

int theByte = 0;
while ((theByte = fis.read()) != -1) {
baos.write(theByte);
}
fis.close();

byte[] keyBytes = baos.toByteArray();
baos.close();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

// build the initialization vector. This example is all zeros, but it
// could be any value or generated using a random number generator.
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
System.out.println("här");
c.init(Cipher.DECRYPT_MODE, keySpec, ivspec);

System.out.println("Decrypting file...");
try {
decryptStream(new FileInputStream("krypterad.txt"), new FileOutputStream("Dekryperad.txt"), c, buf);
} catch (java.io.IOException e) {
}
System.out.println("File decrypted!");
}

public void decryptStream(InputStream in, OutputStream out, Cipher dcipher, byte[] buf) {
try {

in = new CipherInputStream(in, dcipher);

// Read in the decrypted bytes and write the cleartext to out
int numRead = 0;

while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);

}
out.close();

} catch (java.io.IOException e) {
}
}

public static void main(String[] args) {
new DecryptHandler();
}
}

What causes the error java.security.InvalidKeyException: Parameters missing?

The main issue in your code was caused by a failure to specify an IV value. You must specify an IV value when doing CBC-mode encryption and use that same value when performing the CBC-mode decryption.

Another problem is the mix and match of creating strings from byte arrays and base64-encoding. You also return null from your decrypt method every time. Even if you meant return original.toString();, that's still wrong (because toString() doesn't do what you wish it would on a byte array).

Below is an improved version of your code. It's far from optimal, but it compiles and works. You need to improve this to use a random IV. Also, if you plan to derive keys from passwords, don't just get the bytes, use a derivation function such as PBKDF2. You can see an example of using PBKDF2 in the JNCryptor source.

public class EncryptionTest {

public static void main(String[] args) {
try {

String key = "ThisIsASecretKey";
byte[] ciphertext = encrypt(key, "1234567890123456");
System.out.println("decrypted value:" + (decrypt(key, ciphertext)));

} catch (GeneralSecurityException e) {
e.printStackTrace();
}
}

public static byte[] encrypt(String key, String value)
throws GeneralSecurityException {

byte[] raw = key.getBytes(Charset.forName("UTF-8"));
if (raw.length != 16) {
throw new IllegalArgumentException("Invalid key size.");
}

SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec,
new IvParameterSpec(new byte[16]));
return cipher.doFinal(value.getBytes(Charset.forName("UTF-8")));
}

public static String decrypt(String key, byte[] encrypted)
throws GeneralSecurityException {

byte[] raw = key.getBytes(Charset.forName("UTF-8"));
if (raw.length != 16) {
throw new IllegalArgumentException("Invalid key size.");
}
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec,
new IvParameterSpec(new byte[16]));
byte[] original = cipher.doFinal(encrypted);

return new String(original, Charset.forName("UTF-8"));
}
}

java.security.InvalidKeyException thrown only for older java versions during AES decryption

I didn't test you your code but 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 setup 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 for 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);
}
}
}

java.security.InvalidKeyException thrown during Java AES key exchange using RSA

First, you can't use SecretKeySpec to restore an RSA public key. In your Server's constructor, change

Key clientRSAKey = new SecretKeySpec(encodedClientKey, 0, encodedClientKey.length, "RSA");

to

Key clientRSAKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(encodedClientKey));

Second, you need to decode the base64 encoded encrypted key. In your Client constructor, change

String encryptedKey = receiveUnencrypted();

to

byte[] encryptedKey = Base64.getDecoder().decode(receiveUnencrypted());

Finally, in your Client constructor, change

byte[] AESKey = c.doFinal(encryptedKey.getBytes());

to

byte[] AESKey = c.doFinal(encryptedKey);

How to fix Invalid AES key length?

Things to know in general:

  1. Key != Password
    • SecretKeySpec expects a key, not a password. See below
  2. 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, 65536, 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.

Invalidkeyexception in AES algorithm

The code you've listed is bad for a number of reasons (using ECB, Cipher is not thread safe, not doing padding correctly, etc.), I highly suggest that you do not use it.

I had this lying around, you should probably use it instead.

public static void main(String[] args) throws DecoderException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException {
//Hex encoding/decoding done with Apache Codec library.
//key MUST BE 16, 24 or 32 random bytes.
//Do not reuse this key! Create your own.
byte[] key = Hex.decodeHex("a3134dfd51c30f6e25343d861320668e".toCharArray());
String text = "This is some test text.";

byte[] encrypted = encrypt(key, text.getBytes());
byte[] decrypted = decrypt(key, encrypted);

System.out.println("Text: " + text);
System.out.println("Encrypted: " + Hex.encodeHexString(encrypted));
System.out.println("Decrypted: " + new String(decrypted));
}

public static byte[] encrypt(byte[] key, byte[] unencrypted) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException{
//Create an initialization vector
//SecureRandom is not available on J2ME, so we use Bouncy Castle's DigestRandomGenerator instead.
DigestRandomGenerator rnd = new DigestRandomGenerator(new SHA1Digest());
//SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[16];
rnd.nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);

//Set up the cipher and encrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);
byte[] encrypted = cipher.doFinal(unencrypted);

//Append the encrypted text to the IV
byte[] output = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, output, 0, iv.length);
System.arraycopy(encrypted, 0, output, iv.length, encrypted.length);
return output;
}

public static byte[] decrypt(byte[] key, byte[] encrypted) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException{
//Separate the IV and encrypted text
byte[] iv = new byte[16];
byte[] encryptedText = new byte[encrypted.length - iv.length];
System.arraycopy(encrypted, 0, iv, 0, iv.length);
System.arraycopy(encrypted, iv.length, encryptedText, 0, encryptedText.length);

//Decrypt the encrypted text
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);
byte[] decrypted = cipher.doFinal(encryptedText);

return decrypted;
}

java.security.InvalidKeyException: Parameters missing

You are using AES/CBC/PKCS5Padding but you are not passing an IV to your cipher.init calls.

CBC mode requires a random IV for each encryption operation, you should generate this using SecureRandom each time you encrypt, and pass the value in as an IvParameterSpec. You'll need the same IV for decryption. It is common to prepend the IV to the ciphertext and retrieve when required.

On a seperate note, encrypting passwords is really quite a terrible idea. The fact that you have to ask this question at all somewhat proves that you aren't in a position to be making security-related decisions. Do yourself and your project a favour and hash your passwords instead. PBKDF2 and bcrypt are both decent ways of doing so.



Related Topics



Leave a reply



Submit