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.
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.
Conclusion: data and protocol standards, when they are supported, beget interop.
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!