java – Encryption compatible between Android and C#-ThrowExceptions

Exception or error:

I’ve found plenty of examples how to do encryption in C#, and a couple for Android, but I’m particularly looking for a way to handle encrypting (using something like AES, TripleDES, etc.) from Android, and eventually wind up being decrypted in C#. I found an example for encoding AES in Android and encoding/decoding AES in C# but am not sure if these are compatible (C# requires an IV, nothing is specified for this in the Android example). Also, a recommendation on a good way of encoding the encrypted string for transmission over HTTP (Base64?) would be helpful. Thanks.

How to solve:

Got some help from http://oogifu.blogspot.com/2009/01/aes-in-java-and-c.html.

Here is my Java class:

package com.neocodenetworks.smsfwd;

import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import android.util.Log;

public class Crypto {
    public static final String TAG = "smsfwd";

    private static Cipher aesCipher;
    private static SecretKey secretKey;
    private static IvParameterSpec ivParameterSpec;

    private static String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static String CIPHER_ALGORITHM = "AES";
    // Replace me with a 16-byte key, share between Java and C#
    private static byte[] rawSecretKey = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    private static String MESSAGEDIGEST_ALGORITHM = "MD5";

    public Crypto(String passphrase) {
        byte[] passwordKey = encodeDigest(passphrase);

        try {
            aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "No such algorithm " + CIPHER_ALGORITHM, e);
        } catch (NoSuchPaddingException e) {
            Log.e(TAG, "No such padding PKCS5", e);
        }

        secretKey = new SecretKeySpec(passwordKey, CIPHER_ALGORITHM);
        ivParameterSpec = new IvParameterSpec(rawSecretKey);
    }

    public String encryptAsBase64(byte[] clearData) {
        byte[] encryptedData = encrypt(clearData);
        return net.iharder.base64.Base64.encodeBytes(encryptedData);
    }

    public byte[] encrypt(byte[] clearData) {
        try {
            aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        } catch (InvalidKeyException e) {
            Log.e(TAG, "Invalid key", e);
            return null;
        } catch (InvalidAlgorithmParameterException e) {
            Log.e(TAG, "Invalid algorithm " + CIPHER_ALGORITHM, e);
            return null;
        }

        byte[] encryptedData;
        try {
            encryptedData = aesCipher.doFinal(clearData);
        } catch (IllegalBlockSizeException e) {
            Log.e(TAG, "Illegal block size", e);
            return null;
        } catch (BadPaddingException e) {
            Log.e(TAG, "Bad padding", e);
            return null;
        }
        return encryptedData;
    }

    private byte[] encodeDigest(String text) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance(MESSAGEDIGEST_ALGORITHM);
            return digest.digest(text.getBytes());
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "No such algorithm " + MESSAGEDIGEST_ALGORITHM, e);
        }

        return null;
    }
}

I used http://iharder.sourceforge.net/current/java/base64/ for the base64 encoding.

Here’s my C# class:

using System;
using System.Text;
using System.Security.Cryptography;

namespace smsfwdClient
{
    public class Crypto
    {
        private ICryptoTransform rijndaelDecryptor;
        // Replace me with a 16-byte key, share between Java and C#
        private static byte[] rawSecretKey = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

        public Crypto(string passphrase)
        {
            byte[] passwordKey = encodeDigest(passphrase);
            RijndaelManaged rijndael = new RijndaelManaged();
            rijndaelDecryptor = rijndael.CreateDecryptor(passwordKey, rawSecretKey);
        }

        public string Decrypt(byte[] encryptedData)
        {
            byte[] newClearData = rijndaelDecryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
            return Encoding.ASCII.GetString(newClearData);
        }

        public string DecryptFromBase64(string encryptedBase64)
        {
            return Decrypt(Convert.FromBase64String(encryptedBase64));
        }

        private byte[] encodeDigest(string text)
        {
            MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] data = Encoding.ASCII.GetBytes(text);
            return x.ComputeHash(data);
        }
    }
}

I really hope this helps someone else!

###

In the provided c# source code sample, watch out this line:

Encoding.ASCII.GetString(newClearData);

UTF-8 is the default encoding for Android, so encrypted string (especially non-ASCII chars such as Chinese) will be passed to C# assuming UTF-8. The text becomes scrambled if decoded back to string using ASCII encoding. Here is a better one,

Encoding.UTF8.GetString(newClearData);

Thanks!

###

Yes it should be fine, as long we the keysize is the same – 128 bit AES and the correct block cipher mode (CBC). You might run into issues with padding, but that should be fairly easy to sort out. I ran into these issues with Java and Python recently, but got everything working in the end. Base64 for encoding should be fine over HTTP. Good luck!

###

If you correctly implement the same cipher (like AES) and mode (like CTR, CFB, CCM, etc) on both ends, the ciphertext from one end can be decrypted by the other end regardless of platform.

The Android example you linked to appears to use the ECB mode, and thus is not secure for your purposes. It’s critically important that you understand the implications of the block mode you select. It’s very easy to get crypto wrong at this level, resulting in a system that’s not as secure as you think it is.

EDIT: I take that back, it’s not using ECB, but the way it generates the IV is not practical. In any case, my point about understanding the implications of the block modes stands.

You can start with this wikipedia article. Bruce Schneier’s book ‘Practical Cryptography’ is also hugely valuable to anyone implementing cryptographic security.

As to encoding the string, if you must convert the string into ASCII text Base64 is as good a way as any, but I would suggest you investigate use of HTTP PUT or POST to spare you this additional step.

###

Most of the examples on internet are weak implementation of AES. For an implementation to be strong, random IV should be used all the time and key should be hashed.

For more secure(random IV + hashed key) cross platform (android, ios, c#) implementation of AES see my answer here – https://stackoverflow.com/a/24561148/2480840

Leave a Reply

Your email address will not be published. Required fields are marked *