.NET and Java: Generating Interoperable AES Key and IV

Let's assume we want to generate encryption key and initialization vector (IV) for AES encryption based on some passphrase. And we want to be able to generate the same key and IV for the same passphrase in .NET and Java - maybe we have Android app written in Java that needs to decrypt message from ASP.NET web app.

In .NET, Rfc2898DeriveBytes class is often used to derive keys of specified length according to given passphrase, salt, and iteration count (RFC2898 / PBKDF2). For 256-bit key and 128-bit key it is as simple as this:

var keyGen = new Rfc2898DeriveBytes(passwordBytes, 
    saltBytes, iterationCount);

byte[] key = keyGen.GetBytes(256 / 8);
byte[] iv = keyGen.GetBytes(128 / 8);

Fortunately, PBKDF2 implementation is also built-in in Java:

SecretKeyFactory factory = 
    SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passwordChars, 
    saltBytes, iterationCount, 256);
SecretKey secretKey = factory.generateSecret(spec);
byte[] key = secretKey.getEncoded();

We have the same key byte array, albeit with some more typing. And how about the initialization vector now? One could think that creating new PBEKeySpec with a length of 128 is the way to go. I know I did.

However, you would just get the same bytes as for the key (the first half of them). This key derivation algorithm is deterministic so for the same inputs you get the same output. Each call of GetBytes of .NET's Rfc2898DeriveBytes just returns more and more bytes generated by the algorithm whereas Java implementation needs to know the total output length upfront. So for 256-bit key and 128-bit IV we need to create PBEKeySpec with the length of 384 and split the result between key and IV:

KeySpec spec = new PBEKeySpec(passwordChars, 
    saltBytes, iterationCount, 256 + 128);
SecretKey secretKey = factory.generateSecret(spec);

byte[] data = secretKey.getEncoded();
byte[] keyBytes = new byte[256 / 8];
byte[] ivBytes = new byte[128 / 8];

System.arraycopy(data, 0, keyBytes, 0, 256 / 8);
System.arraycopy(data, 256 / 8, ivBytes, 0, 128 / 8);		

Note: All the Java stuff was tested only with Android.

16 thoughts on “.NET and Java: Generating Interoperable AES Key and IV

  1. Hi,
    Have been tring to decode a string encoded in c# for a while now without any luck. You seem to know what you are talking about so please – how do I use the keyBytes and ivBytes in the next step to decode my string. I read everything I can find and I have tried everything I can think of without luck.
    /carl-johan

  2. I have exactly the same circumstance described in your blog. I have encrypted key from C# and I want to decode it in Java. I was following your example and I’m getting exception in initializing the cypher where it throws the errors saying illegal key size. My method looks like this:

    private static byte[] decrypt(byte[] dataToDecrypt, char[] password, byte[] salt, int pbkdfRounds) throws Exception {
    if (dataToDecrypt.length == 0) {
    throw new IllegalArgumentException(“Data to decrypt cannot be zero”);
    }

    SecretKeyFactory factory = SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA1”);

    //char[] passwordAsChars = convertToCharArray(password);
    //PBEKeySpec pbeKeySpec = new PBEKeySpec(password, salt, pbkdfRounds, 384);
    PBEKeySpec pbeKeySpec = new PBEKeySpec(password, salt, pbkdfRounds, 384);
    //PBEKeySpec pbeKeySpec = new PBEKeySpec(password, salt, pbkdfRounds);
    Key secretKey = factory.generateSecret(pbeKeySpec);
    byte[] key = new byte[32];
    byte[] iv = new byte[16];

    byte[] encodedKey = secretKey.getEncoded();

    System.arraycopy(encodedKey, 0, key, 0, 32);
    System.arraycopy(encodedKey, 32, iv, 0, 16);
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, “AES”);
    //SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), “AES”);
    AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);

    //byte[] encyptedData = checkHmac(dataToDecrypt, salt, secretKey);

    Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);

    return cipher.doFinal(dataToDecrypt);
    }

    I’m not sure what I’m missing?

    • I got around illegal key size issue turns out i was missing correct JCE files. But now I’m getting crypto.BadPaddingException: Given final block not properly padded

      • Any updates on the question I posted I’m still getting the same error. My password is a hexadecimal value and I have converted it to char array as needed for key initialization. I’m not sure what else I’m missing any help would be highly appreciated.

      • One thing I see is that you are using PKCS5Padding, you could try PKCS7Padding instead (in Cipher.getInstance call).

  3. I don’t think you understand the point of the IV – by generating the IV and key at the same time you might as well not use an IV at all. The IV is used to protect you against producing the same ciphertext if you encrypt the same plaintext with the same key.

    You start of my generating a derived key from a password and a pseudorandom salt (preferably 160-bit if using SHA-1) using PBKDF2 – every time you do this you will get a different key (you save the salt with the ciphertext so you can reverse the process). If you generate a unique key every time, you don’t need to worry about an IV, but if not then every time you encrypt using this key you need a unique IV to protect against the same ciphertext being produced for the same plaintext.

  4. I’m getting this exception when I change the block size from 256 to 384: java.security.InvalidKeyException: Invalid AES key length: 48 bytes

    Added the Jars necessarily to my java lib/security folder. Is there anything else that I am missing here. Please suggest.

    public static byte[] generateSecretRfc2898DeriveBytes(String pwd, String saltKey, int dkLen, int count)
    throws NoSuchAlgorithmException, InvalidKeySpecException{

    PBEKeySpec keySpec = new PBEKeySpec(pwd.toCharArray(), saltKey.getBytes(), count, dkLen+128);
    SecretKeyFactory factory = SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA1”);

    byte[] data = factory.generateSecret(keySpec).getEncoded();
    byte[] keyBytes = new byte[256 / 8];
    byte[] ivBytes = new byte[128 / 8];

    System.arraycopy(data, 0, keyBytes, 0, 256 / 8);
    System.arraycopy(data, 256 / 8, ivBytes, 0, 128 / 8);

    //byte[] out = factory.generateSecret(keySpec).getEncoded();

    //return out;
    return data;
    }

    public static String encrypt(String strToEncrypt)
    {
    try
    {
    Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);
    byte[] key = generateSecretRfc2898DeriveBytes(pwdKey, saltKey, dkLen, rounds);
    final SecretKeySpec secretKey = new SecretKeySpec(key, “AES”);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    final String encryptedString = new String(Base64.encodeBase64(cipher.doFinal(strToEncrypt.getBytes())));
    return encryptedString;
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    return null;

    }
    public static void main(String args[]) throws NoSuchAlgorithmException
    {
    final String encryptedStr = TestAESAlgorithm.encrypt(strToEncrypt.trim());
    System.out.println(“String to Encrypt : ” + strToEncrypt);
    System.out.println(“Encrypted : ” + encryptedStr);
    }

    • This post has been really helpful. Thank you.

      Found the issue. It was because I didn’t add IV keybytes value.

  5. Hi, I also have same use case where encryption application (.net) is encrypting message using derived key and iv from base64 encoded secret key, salt bytes and iteration count. I have to decrypt the message in java using the same secret key and salt. I am using PBEKeySpec to derive the key and iv but not successful. Could you please help me how to convert base 64 encoded secret key to char[] password required in PBEKeySpec.

  6. I want decryption in java for this .net encryption:

    public string _Encrypt(string TextoEncrypt)
    {
    // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
    // so that the same Salt and IV values can be used when decrypting.
    var saltStringBytes = Generate256BitsOfRandomEntropy();
    var ivStringBytes = Generate256BitsOfRandomEntropy();
    var plainTextBytes = Encoding.UTF8.GetBytes(TextoEncrypt);
    var keyBytes = new Rfc2898DeriveBytes(“MM”, saltStringBytes, DerivationIterations).GetBytes(Keysize / 8);
    using (var symmetricKey = new RijndaelManaged())
    {
    symmetricKey.BlockSize = 256;
    symmetricKey.Mode = CipherMode.CBC;
    symmetricKey.Padding = PaddingMode.PKCS7;
    using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
    {
    using (var memoryStream = new MemoryStream())
    {
    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
    {
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
    cryptoStream.FlushFinalBlock();
    // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
    var cipherTextBytes = saltStringBytes;
    cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
    cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
    memoryStream.Close();
    cryptoStream.Close();
    return Convert.ToBase64String(cipherTextBytes);
    }
    }
    }
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *