24 January 2005

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

Filed under: , ,
 

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# xyombie said:
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.
30 January 05 at 8:03 PM
# Nirbhay Singh said:

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

03 November 08 at 3:02 AM
# DotNetInterop said:

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

you'll have to be more clear.

04 November 08 at 4:31 PM
# Me 2 You said:

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.

02 January 09 at 3:56 PM
# DotNetInterop said:

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

09 January 09 at 2:56 PM
# Bobster said:

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 :(

14 May 09 at 9:26 AM
# DotNetInterop said:

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).  

29 May 09 at 11:07 AM
# Varun said:

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 ?

23 June 09 at 8:02 AM
# DotNetInterop said:

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.

24 June 09 at 7:08 PM
# Dave said:

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?

29 July 09 at 1:54 PM
# Dave said:

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.

30 July 09 at 10:37 AM
# DotNetInterop said:

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.

01 August 09 at 3:27 AM
# venkat said:

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

06 August 09 at 12:33 PM
# DotNetInterop said:

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

09 August 09 at 3:02 AM

Leave a Comment

Comment Policy: No HTML allowed. URIs and line breaks are converted automatically. Your e–mail address will not show up on any public page.

(required) 
(optional)
(required) 

  
Enter Code Here: Required
Page view tracker