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?

Quintin Robinson
  • 81,193
  • 14
  • 123
  • 132
Test
  • 61
  • 1
  • 1
  • 2

4 Answers4

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.

Steve Knight
  • 923
  • 7
  • 12
Robert
  • 39,162
  • 17
  • 99
  • 152
  • 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. – Test Mar 15 '11 at 13:01
  • Have you changed your Java code to `Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");` as Chris Thompson proposed? – Robert Mar 16 '11 at 08:36
  • 2
    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. – Maarten Bodewes Aug 17 '11 at 17:39
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.

Doguhan Uluca
  • 6,933
  • 4
  • 38
  • 51
  • -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 Feb 12 '13 at 11:09
  • @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. – Doguhan Uluca Feb 12 '13 at 15:06
  • @CodesInChaos How exactly did you use/call the `Encrypt(string input).....` method in C#. I tried using `Decrypt5("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. – Ben Akin Jan 17 '18 at 16:32
  • @BenAkin `TSymmetricAlgorithm` must be of type `SymmetricAlgorithm` and a concrete implementation. Here's the documentation page for it: https://msdn.microsoft.com/en-us/library/system.security.cryptography.symmetricalgorithm%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396 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('your input')`. – Doguhan Uluca Jan 17 '18 at 21:09
2

Try this combination:

aesAlg.Mode = CipherMode.ECB;   
aesAlg.Padding = PaddingMode.PKCS7;   
//aesAlg.IV; - use default (not assign)   
Artem Shmatkov
  • 1,434
  • 5
  • 22
  • 41
Alf
  • 21
  • 1
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.

Chris Thompson
  • 35,167
  • 12
  • 80
  • 109
  • 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. – Test Mar 14 '11 at 07:22