I am trying to implement symmetric encryption using AES, where I specify the key and IV. Within each ecosystem (Java → Java / .NET → .NET), encryption and decryption work the way they were designed, but if I try to go between them, I get a lot of errors regarding padding, etc. (Details below).
I read several posts, including http://blogs.msdn.com/b/dotnetinterop/archive/2005/01/24/java-and-net-aes-crypto-interop.aspx , but none of them have one the same use case. Here is my code:
Java
public class Main { public static void main(String[] args) throws Exception { String data = "Something to decrypt"; String encrypt = Encrypt(data); System.out.println(encrypt); String decrypt = Decrypt(encrypt); System.out.println(decrypt); } private static String Encrypt(String raw) throws Exception { Cipher c = getCipher(Cipher.ENCRYPT_MODE); byte[] encryptedVal = c.doFinal(raw.getBytes("UTF-8")); return new BASE64Encoder().encode(encryptedVal); } private static Cipher getCipher(int mode) throws Exception { Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", new SunJCE());
.NET
internal class Program { private static void Main(string[] args) { string encrypted = Encrypt("Something to decrypt"); Console.Out.WriteLine(encrypted); string decrypted = Decrypt(encrypted); Console.Out.WriteLine(decrypted); Console.Out.WriteLine("Press any key to continue"); Console.ReadKey(); } private static string Encrypt(string raw) { using (var csp = new AesCryptoServiceProvider()) { ICryptoTransform e = GetCryptoTransform(csp, true); byte[] inputBuffer = Encoding.UTF8.GetBytes(raw); byte[] output = e.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length); string encrypted = Convert.ToBase64String(output); return encrypted; } } public static string Decrypt(string encrypted) { using (var csp = new AesCryptoServiceProvider()) { var d = GetCryptoTransform(csp, false); byte[] output = Convert.FromBase64String(encrypted); byte[] decryptedOutput = d.TransformFinalBlock(output, 0, output.Length); string decypted = Encoding.UTF8.GetString(decryptedOutput); return decypted; } } private static ICryptoTransform GetCryptoTransform(AesCryptoServiceProvider csp, bool encrypting) { csp.Mode = CipherMode.CBC; csp.Padding = PaddingMode.PKCS7; var passWord = "Pass@word1"; var salt = "S@1tS@lt";
Outputs for applications:
Java
EO88Y8EjPAbaiZGoQM47z5i3vvy8jgVoehCPJDQES/4= Something to decrypt
.NET
T/m/GatOCvFEJ4frVIoY8CbuQ1av97HoKdhUhAZcXp0= Something to decrypt
When I try to decrypt a .NET string in Java, I get: An exception in the stream "main" javax.crypto.BadPaddingException: this final block is not filled correctly.
In .NET, I get a similar one: System.Security.Cryptography.CryptographicException was not handled HResult = -2146233296 Message = Padding is invalid and cannot be deleted.
In .NET, I use Rfc2898DeriveBytes to generate my key based on password, salt, and number of iterations. I assume this is the root cause, as in Java I use SecretKeyFactory, hoping to achieve the same result. However, if that's the reason, I'm not sure how to reconcile the two ...?
Thanks for any help.
** UPDATED - SOLVED **
Boy, I feel dumber than usual. The problem was a typo in my code. The .NET salt value has a value of "1" and "L", where the value of the Java salt value has a value of "1" and "1". Fixing this error fixed the problem. Sorry to spend any time.
.NET
var salt = "S@1tS@lt";
Java
byte[] salt = "S@1tS@1t"