Udostępnij za pośrednictwem


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

  • Anonymous
    January 30, 2005
    The comment has been removed

  • Anonymous
    November 02, 2008
    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

  • Anonymous
    November 04, 2008
    sorry, I don't know what you are talking about!! you'll have to be more clear.

  • Anonymous
    January 02, 2009
    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.

  • Anonymous
    January 09, 2009
    Yes - you are quite right, Me2You.  Good points.

  • Anonymous
    May 14, 2009
    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 :(

  • Anonymous
    May 29, 2009
    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).  

  • Anonymous
    June 23, 2009
    The comment has been removed

  • Anonymous
    June 24, 2009
    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.

  • Anonymous
    July 29, 2009
    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?

  • Anonymous
    July 30, 2009
    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.

  • Anonymous
    July 31, 2009
    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.

  • Anonymous
    August 06, 2009
    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

  • Anonymous
    August 08, 2009
    I updated the code so that it behaves properly with the Initialization Vector.  

  • Anonymous
    December 23, 2009
    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.

  • Anonymous
    December 25, 2009
    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

  • Anonymous
    January 13, 2010
    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

  • Anonymous
    January 13, 2010
    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");

  • Anonymous
    December 21, 2010
    Can the cryptography support be used on .NET MicroFramework?

  • Anonymous
    May 06, 2012
    The comment has been removed

  • Anonymous
    May 06, 2012
    The comment has been removed

  • Anonymous
    November 26, 2012
    I'm interesting on getting the samople codes, but the links are broken .. Thanks !!