Encryption for Java and C++ Interoperability

octobre 26, 2005

So you have an application written in Java talking to a server written in C++ (or the other way around) and you need to transfer sensitive information (like a password or your girlfriend’s weight) across the wire. It won’t be a surprise to anybody that this can be achieved but when you actually got to do it, you find that there is no really good cookbook on the topic. So here is my shot at making one.

Libraries

Java has its own Java Crypto package and it’s included in the basic Java Development Kit since 1.4. Nothing to download here.

On the C++ side, I used OpenSSL DES which should be shipped with most Linux distributions.

Encryption Scheme

I’m sure there are many encryption schemes that works, but I only played with DES EDE3. Both OpenSSL and Java Crypto handle that one.

The Java End

This article explain all you need to know on the Java end of the protocol. It even convert the password to Base64 to make sure you can transfer it over a text-based protocol (like XML.) The key item here is… well… the secret key. Make sure it’s at least 24 characters long (exceeding characters are ignored.)

The C++ End

I had a little more problem with this end, but things are now working.

First, you need to encode and decode Base64 strings. You can use this library to do so. It’s quite simple and doesn’t depend on any other libraries.

Now that you can convert the Base64 encrypted stuff, you need to decrypt it. That’s easy when you know how to do it. This first code segment initiate everything:

#include <openssl/des.h>

...

char *keyString = SECRET_KEY;

unsigned char key1[8];
unsigned char key2[8];
unsigned char key3[8];

memcpy(key1, keyString, 8);
memcpy(key2, keyString+8, 8);
memcpy(key3, keyString+16, 8);

DES_key_schedule schedule1;
DES_key_schedule schedule2;
DES_key_schedule schedule3;

DES_set_key(&key1, &schedule1);
DES_set_key(&key2, &schedule2);
DES_set_key(&key3, &schedule3);

Make sure SECRET_KEY is the exact same String as the secret key you picked on the Java end. Ok, so everything has been initialized. You’re almost there, keep reading. You basically just need to decrypt:

DES_cblock ivec;
memset(&ivec, 0, sizeof(DES_cblock));
DES_ede3_cbc_encrypt(toDecrypt,
        decrypted, length, &schedule1,
        &schedule2, &schedule3, &ivec, DES_DECRYPT);

ivec is, if I got it right (and correct me if I’m wrong) a cyclic thing that will make sure that the encryption scheme changes as you decrypt, making it impossible (well, almost…) for somebody to decrypt what you’re sending even if he has the password, if he doesn’t have all the encrypted data from the beginning. toDecrypt is the binary data to be decrypted. decrypted needs to point to a chunk of data big enough to contain the decrypted data. The length is the expected length of the decrypted string.

To encrypt, use DES_ENCRYPT instead. The first argument become the text to encrypt, and the second is a pointer where to store the encrypted data.

And it works!

The nicest thing about all that is that it works. You can encrypt a String in your Java application, send it to your server written in C++, and it can decrypt and use it without any problem. It also work the other way around. No longer will you have to create a clumsy JNI component because your encryption toolkit is only available in C++. (That was the other option, before I found out about Java Crypto library.)

My life would have been a lot easier if I had had all those links and information when I started, so I wrote this up in case it can be of any use to somebody. If you happen to know anything about security, could you look over the code and let me know if this make any sense? Is it secure enough? Is there any big flaw in there?

Update from 2005/10/27: DES EDE3 messages need to be padded. This is a way to validate, on the decrypting end, that the decrypted data is complete and to determine the exact length of the decrypted message. The Java crypto library takes care of that for you, but the C++ library doesn’t, so you have to trim the padding after decrypting (and add one before you encrypt.)

One more thing. If you only use doFinal(...) from the Java crypto library, it considers the whole message as one chunk and therefore, ivec must always stay the same (0) if you are encrypting something intended to be decrypted by Java. Otherwise, use multiple update(...) and a final doFinal(...).

3 Responses to “Encryption for Java and C++ Interoperability”

  1. Er. You mean memset(ivec…) not memset(&ivec…) certainly.

  2. If I look at the header file…

    typedef unsigned char DES_cblock[8];

    So, you are correct, one must call memset(ivec…) and not memset(&ivec…)

    Thanks for letting me know.

  3. Hi,

    Thanks for writing about this. I have the same dilemma (doing DES encryption in Java, and decryption in C/C++)

    Would it be possible to get a copy of the C++ file? :)

    Also for the SECRET_KEY in « char *keyString = SECRET_KEY ». Is that the « at least 24 characters long » string (like a passphrase) ? Or is it the one generated by javax.crypto.SecretKeyFactory?

    Elaine

Leave a Reply




Suivez-moi !