Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java equivalent of C# AES encryption

I have a following code in C#. It does encoding an array of bytes with an AES symmetric algorithm. I need to write Java equivalent of this code.

class Program
{
    static void Main(string[] args)
    {
        string a = "ABCDEFGHIJKLMNOP";
        byte[] bytes = Encoding.ASCII.GetBytes(a);
        byte[] cipher = encode(bytes, "1111111122222222111111112222222211111111222222221111111122222222", "66666666555555556666666655555555");
    }

    private static byte[] encode(byte[] toEncrypt, string sKey, string sIV)
    {
        byte[] IV = new byte[16];
        byte[] key = new byte[32];
        byte[] array = new byte[toEncrypt.Length];
        string s;

        for (int i = 0; i < IV.Length; ++i)
        {
            s = sIV.Substring(i * 2, 2);
            IV[i] = Convert.ToByte(s, 16);
        }

        for (int i = 0; i < key.Length; ++i)
        {
            s = sKey.Substring(i * 2, 2);
            key[i] = Convert.ToByte(s, 16);
        }

        MemoryStream filecrypt = new MemoryStream(array);

        AesManaged encrypt = new AesManaged();
        encrypt.Mode = CipherMode.CBC;
        encrypt.Padding = PaddingMode.None;
        encrypt.BlockSize = 128;
        encrypt.KeySize = 256;

        CryptoStream cs = new CryptoStream(filecrypt, encrypt.CreateEncryptor(key, IV), CryptoStreamMode.Write);
        cs.Write(toEncrypt, 0, toEncrypt.Length);
        cs.Close();

        return array;
    }
}

This is my attempt of writing this in Java. The code looks fine, but the output is different, something must be wrong.

public class Main {

    public static void main(String [] args) {
        byte [] code = encode("ABCDEFGHIJKLMNOP".getBytes(), "1111111122222222111111112222222211111111222222221111111122222222", "66666666555555556666666655555555");
    }

    private static byte[] toByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        int a;
        int b;
        for (int i = 0; i < len; i += 2) {
            a = (Character.digit(s.charAt(i), 16) << 4);
            b = Character.digit(s.charAt(i+1), 16);
            int n = (Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16);
                data[i / 2] = (byte) (n);
        }
        return data;
    }

    private static byte[] encode(byte[] toEncrypt, String skey, String siv)
    {
        byte[] key = toByteArray(skey);
        byte[] iv = toByteArray(siv);

        byte[] array = new byte[toEncrypt.length];

        Cipher cipher;

        try {
            cipher = Cipher.getInstance("AES/CBC/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE,  new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
            array = cipher.doFinal(array);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return array;
    }
}

Any clues and ideas will be very appreciated.

like image 359
Someone Avatar asked Nov 27 '15 09:11

Someone


1 Answers

I don't know C# pretty well but in general you want multiple consecutive encryption results to be different. This is why you specify an initial IV for the AES algorithm. An encryption code could look like the following:

  public String encrypt( String stringToEncrypt, IvParameterSpec ivSpec ) {
    if ( stringToEncrypt == null ) {
      return null;
    }
    try {
      Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding");
      SecretKeySpec keySpec = new SecretKeySpec( key, "AES" );
      cipher.init( Cipher.ENCRYPT_MODE, keySpec, ivSpec );
      byte[] data = cipher.doFinal( stringToEncrypt.getBytes( "UTF-8" ) );
      return String.format( "%s:%s", Base64.encode( ivSpec.getIV() ), Base64.encode( data ) );
    } catch ( Exception e ) {
      throw new RuntimeException( "Unable to encrypt the string", e );
    }
  }

Your key and your IV should be generated using SecureRandom as this provides the best entropy in java:

byte[] iv = new byte[32];
random.nextBytes( iv );
byte[] key = new byte[32];
random.nextBytes( key );

Furthermore, you might want to calculate an HMAC afterwards - java also supports multiple solutions here. By checking the HMAC on the receiver side you can prevent a padding oracle attack.

To compare different encryption results I would compare them base64 encoded.

Note: It is ok to save the IV next to the ciphertext - it is just there to protect against pre computation attacks.

like image 87
Florian Biesinger Avatar answered Oct 06 '22 02:10

Florian Biesinger