0

I'm trying to use an asymmetric private and public key combination to generate a symmetric key for encrypting and decrypting some text, but, I'm stuck unable to use the generated key as it is 128bytes in size and this is unacceptable for the AES encryption. I'd like to solve this problem using just the JRE (no external libraries). Do you have a solution?

I've included my example code below, there's a comment indicating the line I get the exception thrown.

(encryptCipher.init(Cipher.ENCRYPT_MODE, tomSecretKeySpec, iv);)

I read about KDF hashing, but Java doesn't seem to have an obvious way of invoking this on my 128byte key. Also, Im not sure this is the right answer since my understanding is that the longer the key, the more secure the encryption (for a given algorithm). Perhaps I need to switch from using AES/CBC/PKCS5Padding, but none of the other algorithms included with the JDK as standard seem to support the 128byte key either.

public void demoSymmetricEncryption() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException {

    String keyAlgorithm = "DiffieHellman";
    String keyAgreementAlgorithm = "DiffieHellman";
    String keySpecAlgorithm = "AES";
    String cipherAlgorithm = "AES/CBC/PKCS5Padding";

    KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
    keyGenerator.initialize(1024, new SecureRandom());
    KeyPair tomKeyPair = keyGenerator.generateKeyPair();
    PrivateKey tomPrivateKey = tomKeyPair.getPrivate();
    PublicKey tomPublicKey = tomKeyPair.getPublic();

    KeyPair steveKeyPair = keyGenerator.generateKeyPair();
    PrivateKey stevePrivateKey = steveKeyPair.getPrivate();
    PublicKey stevePublicKey = steveKeyPair.getPublic();

    int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
    System.out.println("Limited encryption policy files installed : " + (maxKeyLen == 128)); // returns false

    KeyAgreement tomKeyAgreement = KeyAgreement.getInstance(keyAgreementAlgorithm);
    keyGenerator.initialize(1024, new SecureRandom());
    tomKeyAgreement.init(tomPrivateKey);
    tomKeyAgreement.doPhase(stevePublicKey, true);
    byte[] tomSecret = tomKeyAgreement.generateSecret();

    SecretKeySpec tomSecretKeySpec = new SecretKeySpec(tomSecret, keySpecAlgorithm);

    KeyAgreement steveKeyAgreement = KeyAgreement.getInstance(keyAgreementAlgorithm);
    steveKeyAgreement.init(stevePrivateKey);
    steveKeyAgreement.doPhase(tomPublicKey, true);
    byte[] steveSecret = steveKeyAgreement.generateSecret();

    SecretKeySpec steveSecretKeySpec = new SecretKeySpec(steveSecret, keySpecAlgorithm);

    System.out.println("Secret Keys are identical : " + steveSecretKeySpec.equals(tomSecretKeySpec)); // returns true

    String initVector = "RandomInitVector";

    Cipher encryptCipher = Cipher.getInstance(cipherAlgorithm);
    IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));

    // fails because AES key is 128 bytes not 128 bits in length - think I need to use KDF hash to shrink it appropriately.
    encryptCipher.init(Cipher.ENCRYPT_MODE, tomSecretKeySpec, iv);


    // Attempt to use the cipher

    byte[] encryptedData = encryptCipher.doFinal("Hello".getBytes());

    Cipher decryptCipher = Cipher.getInstance(cipherAlgorithm);
    iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
    decryptCipher.init(Cipher.DECRYPT_MODE, steveSecretKeySpec, iv);

    byte[] decryptedData = decryptCipher.doFinal(encryptedData);

    System.out.println("Decrypted Data : " + new String(decryptedData));

}

The output from the program is as follows:

Limited encryption policy files installed : false
Secret Keys are identical : true
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 128 bytes
    at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
    at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
    at com.sun.crypto.provider.CipherCore.init(CipherCore.java:582)
    at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:339)
    at javax.crypto.Cipher.implInit(Cipher.java:806)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
    at javax.crypto.Cipher.init(Cipher.java:1396)
    at javax.crypto.Cipher.init(Cipher.java:1327)
    at crypto.SymetricEncryptionTest.demoSymmetricEncryption(SymetricEncryptionTest.java:76)
    at crypto.SymetricEncryptionTest.main(SymetricEncryptionTest.java:29)
extorn
  • 611
  • 1
  • 5
  • 11
  • See for example http://stackoverflow.com/questions/992019/java-256-bit-aes-password-based-encryption – Mark Rotteveel Mar 31 '16 at 07:52
  • 2
    1. What is the stacktrace? 2. not sure but i don't think we can define a key of that size (it makes 1024 bits) Do you really need it?, 128 bit would be enough? See: http://stackoverflow.com/questions/25922682/implementing-aes-256-with-1024-byte-key – pdem Mar 31 '16 at 08:07
  • I've added the application output to the question. I don't really mind what the key size is, but you don't seem able to limit the key size enough to make it work when using the DiffieHellman algorithm. When I tried to use the RSA algorithm, this didn't work but for a different reason. – extorn Mar 31 '16 at 11:42

2 Answers2

0

The error is: * Invalid AES key length: 128 bytes*

Valid AES key sizes are 128-bits, 192-bits and 256-bits or in bytes: 16-bytes, 24-bytes and 32-bytes.

Use an AES key size that is valid.

The general method of generation a symmetric key is just to get the bytes from a cryptographic PRNG. For Java see Class SecureRandom.

For key derivation use PBKDF2, see Class SecretKeyFactory and Java Cryptography Architecture Standard Algorithm Name Documentation "PBKDF2WithHmacSHA1" (Constructs secret keys using the Password-Based Key Derivation Function function).
For an example see OWASP Hashing Java but use "PBKDF2WithHmacSHA1" as the algorithm.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • Thank you for your comment. I'll research those pages and post back if I get some working code that will work using a hash of the public private key combination I'm currently working with. – extorn Apr 02 '16 at 11:06
0

The reason the code wasn't working was that I was using incompatible algorithms. The corrections are as follows: Replace lines:

String keyAlgorithm = "DiffieHellman";
String keyAgreementAlgorithm = "DiffieHellman";

with

String keyAlgorithm = "EC";
String keyAgreementAlgorithm = "ECDH";
int keySize = 128;

and replace lines

keyGenerator.initialize(1024, new SecureRandom());

with

keyGenerator.initialize(keySize, new SecureRandom());

Program now produces output:

Limited encryption policy files installed : false
Secret Keys are identical : true
Decrypted Data : Hello

Technically, you probably also want to Base64 encode the encrypted output and then decode it again prior to the decode as below:

String encryptedData = Base64.encode(encryptCipher.doFinal("Hello".getBytes()));
byte[] decryptedData = decryptCipher.doFinal(Base64.decode(encryptedData));
extorn
  • 611
  • 1
  • 5
  • 11