6

Hello I've Encrypted Hex string and Key that has been encrypted using standard AES Algorithm. Code:

        final String key = "=abcd!#Axd*G!pxP";
        final javax.crypto.spec.SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
        final javax.crypto.Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte [] encryptedValue = cipher.doFinal(input.getBytes());
        return new String(org.apache.commons.codec.binary.Hex.encodeHex(encryptedValue));

Now I try to decrypt it using C# Code:

            RijndaelManaged rijndaelCipher = new RijndaelManaged();
            // Assumed Mode and padding values.
            rijndaelCipher.Mode = CipherMode.ECB;
            rijndaelCipher.Padding = PaddingMode.None;

            // AssumedKeySize and BlockSize values.
            rijndaelCipher.KeySize = 0x80;
            rijndaelCipher.BlockSize = 0x80;
            // Convert Hex keys to byte Array.
            byte[] encryptedData = hexStringToByteArray(textToDecrypt);

            byte[] pwdBytes = Encoding.Unicode.GetBytes(key);
            byte[] keyBytes = new byte[0x10];
            int len = pwdBytes.Length;
            if (len > keyBytes.Length)
            {
                len = keyBytes.Length;
            }
            Array.Copy(pwdBytes, keyBytes, len);
            rijndaelCipher.Key = keyBytes;
            rijndaelCipher.IV = keyBytes;
            // Decrypt data
            byte[] plainText = rijndaelCipher.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);
            str = Encoding.UTF8.GetString(plainText);

and

    static private byte[] HexToBytes(string str)
    {
        if (str.Length == 0 || str.Length % 2 != 0)
            return new byte[0];
        byte[] buffer = new byte[str.Length / 2];
        char c;
        for (int bx = 0, sx = 0; bx < buffer.Length; ++bx, ++sx)
        {
            // Convert first half of byte   
            c = str[sx];
            buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')) << 4);
            // Convert second half of byte    
            c = str[++sx];
            buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0'));
        }
        return buffer;
    } 

but the output is not as expected. Please point out where I'm going wrong?

4 Answers 4

8

Your code has one big problem: It is mixing the character encodings!

In Java you are calling key.getBytes(), without arguments. This method returns the UTF-8 or CP1252/ISO 8859-1 encoded data depending on your operating system and the default charset in Java.

On C# side you are using Encoding.Unicode.GetBytes(key) - "Unicode" in .Net is a synonym for double byte characters alias UTF-16 (Little-Endian). Therefore you are using a different key in C#.

You should be able to see the difference by comparing the number of bytes in Java and C#:

Java: "=abcd!#Axd*G!pxP".getBytes().length = 16

C#: Encoding.Unicode.GetBytes("=abcd!#Axd*G!pxP").Length = 32

I strongly recommend you to use byte arrays instead of Strings for defining a cryptographic key.

Update: Another difference is that you are setting an initialization vector (IV) in C# which you don't do in Java. As you are using ECB the IV should not be used but if you change to CBC for example this makes a big difference.

Sign up to request clarification or add additional context in comments.

3 Comments

I got to know that Encoding used is: "ISO-8859-1" So I changed the key value in decryption logic to: byte[] pwdBytes = System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(key); but it still didn't work. I get "Padding is invalid and can't be removed" exception.
Have you changed your Java code to Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); as Chris Thompson proposed?
ECB should only be used for encrypting keys and other data that can be considered random, please advise to use CBC and a different IV for each piece of data encrypted with the same key. It's probably more usefull to specify PKCS#5 padding as well; no padding means that the length of the encoded data must be implicitly known.
4

I needed something not only for C# but also Silverlight and Windows Phone 7 compatible. And I'm definitely sick of the lack full examples of something acceptable both in Java and C# (and Base64 based).

Code isn't anything fancy, but works. Please feel free to improve upon it, as I marked this as community wiki, but make sure to test it before submitting any changes to it.

Here's the C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
//Author: Doguhan Uluca
//Website: www.deceivingarts.com

namespace DeceivingArts.Common
{
    public class Encryptor
    {
        private string _seed = "";

        public Encryptor(string seed)
        {
        _seed = seed;
        }

        public string Encrypt<TSymmetricAlgorithm>(string input) where TSymmetricAlgorithm : SymmetricAlgorithm, new()
        {
            var pwdBytes = Encoding.UTF8.GetBytes(_seed);
            using(TSymmetricAlgorithm sa = new TSymmetricAlgorithm())
            {
                ICryptoTransform saEnc = sa.CreateEncryptor(pwdBytes, pwdBytes);

                var encBytes = Encoding.UTF8.GetBytes(input);

                var resultBytes = saEnc.TransformFinalBlock(encBytes, 0, encBytes.Length);

                return Convert.ToBase64String(resultBytes);
            }
        }

        public string Decrypt<TSymmetricAlgorithm>(string input) where TSymmetricAlgorithm : SymmetricAlgorithm, new()
        {
            var pwdBytes = Encoding.UTF8.GetBytes(_seed);
            using(TSymmetricAlgorithm sa = new TSymmetricAlgorithm())
            {
                ICryptoTransform saDec = sa.CreateDecryptor(pwdBytes, pwdBytes);

                var encBytes = Convert.FromBase64String(input);

                var resultBytes = saDec.TransformFinalBlock(encBytes, 0, encBytes.Length);
                return Encoding.UTF8.GetString(resultBytes);
            }
        }
    }
}

Here's the Android compatible Java code:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * Usage:
 * <pre>
 * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext)
 * ...
 * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto)
 * </pre>
 * @author ferenc.hechler
 * @author Doguhan Uluca
 */
public class Encryptor {

    public static String encrypt(String seed, String cleartext) throws Exception {
            byte[] rawKey = getRawKey(seed.getBytes());
            byte[] result = encrypt(rawKey, cleartext.getBytes());
            return toBase64(result);
    }

    public static String decrypt(String seed, String encrypted) throws Exception {
            byte[] rawKey = getRawKey(seed.getBytes());
            byte[] enc = fromBase64(encrypted);
            byte[] result = decrypt(rawKey, enc);
            return new String(result);
    }

    private static byte[] getRawKey(byte[] seed) throws Exception {
        SecretKey skey = new SecretKeySpec(seed, "AES");

        byte[] raw = skey.getEncoded();

        return raw;
    }

    private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(raw);

        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
        byte[] encrypted = cipher.doFinal(clear);
        return encrypted;
    }

    private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(raw);

        cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
    }

    public static String toBase64(byte[] buf)
    {
        return Base64.encodeBytes(buf);
    }

    public static byte[] fromBase64(String str) throws Exception
    {
        return Base64.decode(str);
    }
}

For the Base64 conversion please see the excellent implementation at http://iharder.net/base64.

I hope this saves people hours.

4 Comments

-1 for being bad crypto 1) It's using weird terminology, seed for password 2) it uses the password as IV. Don't do that. You need a new random IV per encryption. 3) It uses a password directly as key, without appropriate KDF 4) It lacks authentication => vulnerable to padding oracles
@CodesInChaos Thanks for the great feedback. I'm not a crypto expert. You're most welcome to edit the answer and update the code to fit the standards of good crypto - I'm sure that'd help the community a lot.
@CodesInChaos How exactly did you use/call the Encrypt<TSymmetricAlgorithm>(string input)..... method in C#. I tried using Decrypt5<TSymmetricAlgorithm>("myEncryptedStringxyzabcblahblah"); but it gave me an error. Saying I can't use an abstract class. Am I calling it wrong? What is a non-abstract class that I can use? Thank you.
@BenAkin TSymmetricAlgorithm must be of type SymmetricAlgorithm and a concrete implementation. Here's the documentation page for it: msdn.microsoft.com/en-us/library/… Here are the concrete types that implement it: System.Security.Cryptography.Aes, System.Security.Cryptography.DES, System.Security.Cryptography.RC2, System.Security.Cryptography.Rijndael, System.Security.Cryptography.TripleDES. So a sample call would be like Encrypt<System.Security.Cryptography.TripleDES>('your input').
2

Try this combination:

aesAlg.Mode = CipherMode.ECB;   
aesAlg.Padding = PaddingMode.PKCS7;   
//aesAlg.IV; - use default (not assign)   

Comments

0

I suspect the error is that you aren't specifying a padding or mode of operation in the Java side of this equation. I'm not sure what Java's AES implementation defaults to, but I would start by specifying both when you obtain the cipher. For instance:

Cipher.getInstance("<algorithm>/<mode of operation>/<padding>");

You'll need to look up the supported padding schemes and modes of operation for AES in Java and then make sure you configure your C# code to use the exact same configuration.

1 Comment

I tried using all possible combinations of Mode and padding. but it not working. Also tried to call Security.getProvider("SunJCE); to know default mode and padding, not sure in which kery will I get this info related to default mode and padding used in Java AES. however encryption and decryption logic written in java works with this encryption also. But when I try to decrypt in C# with given code provided, I'm unable to decrypt the string.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.