Java and .NET – AES Crypto Interop


Crypto Interop


There are 999 different ways to connect Java applications to .NET applications, this blog has explored some of them. Check the archives here for examples of Java communicating to .NET via MQSeries, and also via web services and XML document exchange. Regardless of how you do the connection, and regardless of the data format (XML, or even a Java object serialization stream), there may be a need to encrypt that data as it is transported. A very common scenario.


Now, if you are using Web services, you can get encryption, along with a bunch of other whiz-bang features, in WS-Security. For the .NET Framework, the WSE 2.0 add-on provides an implementation of WS-Security and other, related WS- specs. WSE is supported by Microsoft, and you can use it today. If you are connecting .NET-to-.NET, then WSE is all you need. If one of the endpoints is Java, then you will need a web services framework that supports Ws-Security. I think IBM may have one in WebSphere v6, but I haven’t tested it yet. The WS-Security in WAS v5 was not compliant with the final WS-Security spec, and so was not interoperable with .NET.


But, in some cases, many cases maybe?, the Java-to-.NET interop is not via web services, or, is not via a WS-Security -enabled stack. Then what?


Standards to the Rescue


Ahh, the Joy of Standards. The Rijndael symmetric-key (or secret key) encryption algorithm was approved by the United States National Institude of Standards and Technology (NIST) as the Advanced Encryption Standard, or AES. And, owing to this data format standard, interop flourishes. You can argue whether Uncle Sam is a bona fide standards body, and thus whether AES is a dejure standard. But the practical fact is, AES is widely supported. It’s a standard, practically speaking.


Both .NET v1.1 and Java v1.4.x include implementations of the AES. Which means, you can encrypt data on a Java platform, transmit it any old way you want, then decrypt it on a .NET platform. In theory, it’s easy. Is anyone doing this? Here’s what the code looks like on the .NET side:



    1         AesCipher= new RijndaelManaged();


    2         AesCipher.KeySize = 128; // 192, 256


    3         AesCipher.BlockSize = 128;


    4         AesCipher.Mode = CipherMode.CBC;


    5         AesCipher.Padding = PaddingMode.PKCS7;


    6         AesCipher.IV = b0;  // byte array 


    7         AesCipher.Key = b1; // byte array


    8         ICryptoTransform crypto = AesCipher.CreateEncryptor();


    9         byte[] cipherText = crypto.TransformFinalBlock(plainText, 0, plainText.Length);


And here’s what the code looks like on the Java side, using the JCE:



    1         Cipher AesCipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”, ProviderString);


    2         SecretKeySpec KeySpec = new SecretKeySpec(keyBytes, “AES”);


    3         AesCipher.init(Cipher.ENCRYPT_MODE, KeySpec);


    4         CipherText= AesCipher.doFinal(PlainText.getBytes()); // ASCII encoding



Cryptography Support in .NET


With .NET, the AES implementation is built-in. There’s only one, and it is implemented in Managed code. For Windows Server 2003, Microsoft delivered a native implementation, and the nice people at Mentalis put together a .NET wrapper on that native class, boasting that it delivers 7x the performance of a the fully-managed implementation. Mentalis also has other nifty stuff, like a secure sockets library. Sweet! Even without the Mentalis goodies, there are also other crypto algorithms built-into the .NET base class library, including TripleDES, RC2, RSA, and so on.


Cryptography Support in Java


With Java, it is a bit more complicated. Sun specified the JCE, which is a service provider interface. There can be many implementations of crypto, and even many implementations of AES. Sun makes a provider available in their JVM, and supports AES. IBM makes a different JCE provider available on their JVM, and also supports AES. Bouncy Castle delivers a JCE provider that can be installed into either IBM’s or Sun’s JVM, and also supports AES. What’s the difference between all these? I don’t know, there might be performance or operational differences, I did not study them. IBM’s JCE is certainly broader, offering more crypto options, than Sun’s at the moment, according to my survey.


Using the built-in support on either side, I put together a simple example to show what is possible and get you started. There’s a .NET app and a Java app that can each encrypt and decrypt.  They both use RFC2898 to generate keys from a password. 


The Apps


Here’s the Java app, encrypting a message from TR:


Here’s the .NET app, decrypting what the Java app encrypted:


Wow, that Java app looks like a Winforms app, doesn’t it? It’s built using the SWT framework from Eclipse. But let me tell you, it wasn’t easy building that thing. I took about 1 hour on the .NET version and … oh, forget it, I’m not even going to tell you how long that SWT took, building it with no visiual designer.


And yes, you can download the code for both of these: Java   .NET

Conclusion: data and protocol standards, when they are supported, beget interop.


Q&A


Q. Will crypto interop between Java and .NET work only with AES?
A. Nope, it should work with TripleDES and other common crypto algorithms.


Q. Why did you use a symmetric algorithm like AES? and not a public-key algorithm.
A. Ideally the symmetric and the public-key algorithm each play a role in a secure conversation. Transmit the secret key using public key crypto, then use the shared secret key to do AES. Why both? Because public key simplifies key management, but AES (like most symmetric algorithms) is faster than a public-key approach. This particular app just demonstrates how to use a symmetric-key crypto algorithm, and assumes you are using some sort of public/private key crypto to exchange the secure keys.


Q. This isn’t very secure, it shows the pass phrase in the UI.
A. Yes, right. Good point. This is a demonstration. It’s not for use in production. It’s used to illustrate interop. Ideally the key would be a secure-random stream of bytes, a session key.  The code uses RFC2898 to derive the key from the password.


Q. Why does the Java side have perf measurements while the .NET app doesn’t?
A. The Java app took a little longer to load the JCE provider. So I was timing it while developing. The .NET side is pretty quick. After theJava side runs once, the libraries are loaded and it is also lightning quick.


Q. What’s the perf difference between them?
A. I didn’t measure it. But, you could tweak this pair of apps to measure performance over 1000’s of iterations, for various sizes of message. I’d be interested in the results you get.


Q. Do I have to use a shared file to transmit the encrypted data?
A. No. You can use whatever you want. MQ, sockets, smoke signals. In this illustration, the encryption is independent of the transport. If you want an example of MQ interop between Java and .NET, see here.  But the shared file is simple and easy to use.  After you encrypt with one app, save the data, then load the data into the other app, and decrypt.  It should just work. 


Q. Why did you use only 128-bit key sizes?
A. 128-bit crypto is US export-friendly.  Both .NET and Java can go higher, to 256-bit keys, but the 256-bit AES encryption is export-protected.  As a commenter has pointed out, with Sun’s JRE, if you are in the USA and promise not to export it, it is possible to get 192 and 256-bit AES encryption.  For JRE 6.0, go to java.com and look for “Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6.0”.  The same sort of crypto export restriction is placed on .NET, of course, but it’s just that i have a US-unrestricted version of .NET and Windows on my machine, so I was unaware of it.  To use 256-bit encryption, you will have to modify the source code, to set the desired key length to 256. 


Q. You used PKCS5 padding for Java and PKCS7 for .NET. What gives?
A. They are the same for block sizes up to… I think… 256. Since we did block sizes of 128, they are completely the same. Fully interoperable.


Q. Where’d ya get the IBM JDK 1.5.0 on Windows? IBM doesn’t seem to distribute it any longer?
A. They don’t distribute it independently, but you can get it with an eval copy of WebSphere App Server 6.1, Among other IBM products.


Q. That SWT app looks pretty solid! Just like WinForms!
A. Don’t even go there. One word for the developer experience: Yikes!


-Dino

Comments (22)

  1. xyombie says:

    Nice article.

    I wanted to update your comment you made about the JCE only supporting 128 bit keys. Your right, out of the box, Java only supports 128bit AES keys due to US Export restrictions. However, if your in the US, and you agree not to export it outside of the US, you can download a free update to get Sun’s implementation of the JCE to work with 192 & 256 bit keys. Go to http://java.sun.com/, choose the J2SE version that you are working with on the top right part of the screen, and then scroll to the bottom and you will see "Java Cryptography Extension (JCE)

    Unlimited Strength Jurisdiction Policy Files 5.0". Click the download button and follow the directions. You have to unpack the contents of the archive to the directory specified in the directions and then your J2SE environment should accept 192 & 256 bit keys.

  2. After closing the Application again running and passing the Plain Text Nirbhay gettting the different Encrypted Value that should not happen how to over come from this problem. Please help me soom

  3. DotNetInterop says:

    sorry, I don’t know what you are talking about!!

    you’ll have to be more clear.

  4. Me 2 You says:

    Regarding the difference between asymmetric versus symmetric key crypto, it’s not necessarily true that one is stronger than the other. They’re in fact two distinct algorithms and the theoretical strength is dependent on the key size; e.g., 128 bit AES is stronger than 1024 bit RSA.

    The use of two algorithms in a single protocol run leverages the benefit of strong AND fast data privacy using a symmetric key algorithm plus the benefit of secure key management provided made possible by a public key environment.

  5. DotNetInterop says:

    Yes – you are quite right, Me2You.  Good points.

  6. Bobster says:

    Nirbhay appears to be correct – whenever you restart the .Net app it generates different encrypted data for the same inputs. If you try to decrypt data generated in a previous run it doesn’t work :(

  7. DotNetInterop says:

    The reason the encrypted data differs is likely because the app is using a different IV (Initialization Vector).  If you look at the code, it calls GenerateIV(), which is likely producing a different IV each time through.  

    To get the encrypted output to be the same each time through, use the same IV.  (Specify it explicitly).  

  8. Varun says:

    There seems to be a problem regarding ciphertext size.

    For some reason if I directly use a byte array(read from disk), instead of base64 encoded string both Java and dotnet behave differently. and there seems to be no interoperability .

    When I directly encrypt a file, read in bytes in Java using same key and IV, and then try to decrypt it from .net code, there is an exception thrown complaining about invalid cipher text size ?

    Any Clue ?

  9. DotNetInterop says:

    Are you saying that the encryption behaves differently if you get bytes from the disk, as opposed to getting them from a string?  What if they are exactly the same bytes?   I think there must be some other error in the code, having to do with buffers and lengths and so on.  

    When I originally wrote the code, I used a debugger to check the bytes before and after encryption and decryption.  Doing that may help you, too.  Good luck.

  10. Dave says:

    When running the sample, I notice that the first 16 bytes are garbled, but the rest of the text is correct.  This is going from .NET to Java or vice versa.   Additionally, if I encrypt the string in Java on one machine, and decrypt on a separate machine, I see the same result.  But if I encrypt in .NET on one machine and decrypt in Java on a 2nd machine, the decryption fails every time.  Thoughts?

  11. Dave says:

    I was able to figure out a solution to both issues.  There are 2 places in the code where the IV is auto-generated.  If I change both of those to a constant value and use the same constant value in both Java and .NET, everything works.

    I don’t think this will have a negative effect on the security of the encrypted text.  Please respond if you know of regative effects.

  12. DotNetInterop says:

    Yep, I think there is some slop in that code.  

    About the IV  – Often the IV is zero’d or set to a specific known value. So, no, it will not affect the security of the system.

  13. venkat says:

    Dave,

    Can you please specify where in the code both Java and .Net you commented out the auto generated IV code. We are facing the problem when encrypting it in .net and decrypting the same in Java.

    Thanks,

    Venkat

  14. DotNetInterop says:

    I updated the code so that it behaves properly with the Initialization Vector.  

  15. AJ says:

    I am using a 3rd party .NET product which requires encrypted text with AES ( keysize = 256, block size=256, PKCS7, CBC).

    My client program that encrypt the text is in java. I am using JCE. I am not able to figure out how to set the block size to 256 in java. By default the size is 128. I am using PKCS5 padding with CBC mode.

    Please advice how to set the block size to 256 in java JCE for AES. Request your urgent advice.

  16. DotNetInterop says:

    256-bit AES Encryption in Java requires an extra download.  See

    http://java.sun.com/j2se/1.5.0/docs/guide/security/jce/JCERefGuide.html#AppE

    Download it for Java 6 here: http://java.sun.com/javase/downloads/index.jsp

  17. wildbill says:

    In Java, when I use the the AES/CBC/PKCS5Padding w/ key – FFD5A3B89789987DFFD5A3B89789987D022343ADFE992D233FFF234429002382 I get and Illegal Key size….

    The key was created using .Net PKCS7…any help would be great to get this decrypted using WebSphere 6.1 / jdk1.5

    Thanks!

    wildbill

  18. wildbill says:

    Great example and explanation, thank for that.  I was using this for my Java code….

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    byte[] keyBytes= new byte[64];

    byte[] b= KEY.getBytes("UTF-8");

    int len= b.length;

    if (len > keyBytes.length) len = keyBytes.length;

    System.arraycopy(b, 0, keyBytes, 0, len);

    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

    IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);

    cipher.init(Cipher.DECRYPT_MODE,keySpec,ivSpec);

    BASE64Decoder decoder = new BASE64Decoder();

    byte [] results = cipher.doFinal(decoder.decodeBuffer(text));

    return new String(results,"UTF-8");

  19. sharada says:

    Can the cryptography support be used on .NET MicroFramework?

  20. Ihab says:

    dear Cheeso

    we have impliminted your RFC2898 algorithm i shared key between .Net API and java Application and it was working well

    recently our system began processing huge numbers of transactions wich means big messages transfered between java and .Net through decrypt and encrypt for 13 MB files it start show an exception like following

    System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.

      at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)

      at MerchantClientService.Rfc2898.DecryptMessage(Byte[] cipherText

    after debug we explore that file length is bigger than int value passed into cipherText.Length as following code

    ICryptoTransform transform = _aesCipher.CreateDecryptor();

    byte[] plainText = transform.TransformFinalBlock(cipherText, 0, cipherText.Length);

    there is no overload for TransformFinalBlock accept Int64 or even long value

    any Help please

  21. dear Cheeso

    we have impliminted your RFC2898 algorithm i shared key between .Net API and java Application and it was working well

    recently our system began processing huge numbers of transactions wich means big messages transfered between java and .Net through decrypt and encrypt for 13 MB files it start show an exception like following

    System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.

     at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)

     at MerchantClientService.Rfc2898.DecryptMessage(Byte[] cipherText

    after debug we explore that file length is bigger than int value passed into cipherText.Length as following code

    ICryptoTransform transform = _aesCipher.CreateDecryptor();

    byte[] plainText = transform.TransformFinalBlock(cipherText, 0, cipherText.Length);

    there is no overload for TransformFinalBlock accept Int64 or even long value

    any Help please

  22. Leonardo SAiler says:

    I'm interesting on getting the samople codes, but the links are broken ..

    Thanks !!