2

I want to do the AES 256 encryption/decryption using Java and .Net. Which Means i should be able to encrypt with Java and decrypt with .Net and Vice vesra. Following is the Java AES 256 encryption.

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(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");
System.out.println(Cipher.getMaxAllowedKeyLength("AES"));
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
return new String(Base64.encodeBase64(cipher.doFinal(strToEncrypt.getBytes("UTF-8"))));
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Sumanth Varada
  • 1,122
  • 1
  • 16
  • 17

1 Answers1

4

1) The Java-decryption part is given by

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(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.decodeBase64(strToDecrypt.getBytes("UTF-8"))));

where strToDecrypt are the Base64 encoded encrypted data (given by the return value of the encryption part) and which deviates from the encryption part only in the last two lines.

Note: In practice, the IV should be randomly generated (e.g. Generating random IV for AES in Java). But I assume that's clear and the 0-sequence is just for testing purposes.

2) Concerning the key derivation PBKDF2WithHmacSHA256 the C#-solution depends on your .NET-framework version. For V4.7.2 and above the key can be derived with:

// .NET Framework 4.7.2 +
byte[] secretKey = null;
using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(KEY, Encoding.UTF8.GetBytes(SALT), 65536, HashAlgorithmName.SHA256))   
{
    secretKey = rfc2898.GetBytes(32);                                                                                                                                                                                
}

Note: Previous implementations (prior to v4.7.2) of Rfc2898DeriveBytes use SHA1 (hard coded) instead of SHA256 and thus, there's no ctor expecting 4 parameters. Moreover, the Rfc2898DeriveBytes-class requires a salt-length of at least 8 bytes, otherwise a System.ArgumentException: Salt is not at least eight bytes is thrown.

An alternative is:

// .NET Framework 4.5 + 
byte[] secretKey = null;
KeyDerivationPrf keyDerivationPrf = KeyDerivationPrf.HMACSHA256;
secretKey = KeyDerivation.Pbkdf2(KEY, Encoding.UTF8.GetBytes(SALT), keyDerivationPrf, 65536, 32);

The latter works for V4.6.1 and above, but you need the Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivation-class which you can find e.g. at https://www.nuget.org/packages/Microsoft.AspNetCore.Cryptography.KeyDerivation/. For the installation you can use e.g. the Package Manager Console (Tools - NuGet Package Manager - Package Manager Console). Just enter the corresponding command as described in the link. Maybe you get an IDE-error CS0012. In this case you have to add <Reference Include="netstandard" /> to the reference-section of your csproj-file (see also https://github.com/dotnet/standard/issues/542). The KeyDerivationPrf-class doesn't restrict the salt-length.

There are also other possibilities (like Bouncy Castle) which I didn't try, but maybe they are better alternatives for you. This topic is also discussed at Rfc2898 / PBKDF2 with SHA256 as digest in c#.

3) An example for the C#-encryption-method is:

public string Encrypt(string plainText)
{
    // PBKDF2WithHmacSHA256 Key derivation
    // ...

    using (RijndaelManaged cipher = new RijndaelManaged())
    {
        cipher.Key = secretKey;
        cipher.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        cipher.Mode = CipherMode.CBC;
        cipher.Padding = PaddingMode.PKCS7;

        byte[] encryptedData;
        using (ICryptoTransform encryptor = cipher.CreateEncryptor())
        {
            using (System.IO.MemoryStream memoryStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
                    {
                        streamWriter.Write(plainText);
                    }
                    encryptedData = memoryStream.ToArray();
                }
            }
        }

        return Convert.ToBase64String(encryptedData);
    }
}

where plainText is a string containing your plain text (corresponding to strToEncrypt). The encrypted data become base64 encoded and are returned as string (analogous to your Java-method).

Test case:

String KEY = "The Password";
String SALT = "The Salt";
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

gives

Plain text:       This is a plain text that needs to be encrypted
Key (hex):        2D7664713D701C58FC506F93CEA3194671AD3B5C034255A4AC04AF46EADC89BC
Base64 encoded
encrypted data:   ksUYjmbP9ga39LXr3wXQ34Bp32UlloYPxg3WWuW0iovWbg/GxHJrIuF3jrDvjr/Q

4) An example for the C#-decryption-method is:

public string Decrypt(string encryptedText)
{
    // PBKDF2WithHmacSHA256 Key derivation
    // ...

    using (RijndaelManaged cipher = new RijndaelManaged())
    {
        cipher.Key = secretKey;
        cipher.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        cipher.Mode = CipherMode.CBC;
        cipher.Padding = PaddingMode.PKCS7;

        string decryptedText;
        using (ICryptoTransform decryptor = cipher.CreateDecryptor())
        {
            using (System.IO.MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(encryptedText)))
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader streamReader = new StreamReader(cryptoStream))
                    {
                        decryptedText = streamReader.ReadToEnd();
                    }
                }
            }
        }

        return decryptedText;
    }
}

where encryptedText is the return value of the encryption-part (encrypted data which are base64 encoded, corresponding to strToDecrypt). The method returns the decrypted text (analogous to the Java-method).

Topaco
  • 40,594
  • 4
  • 35
  • 62