William Cézar
William Cézar

Reputation: 177

Decrypt C# AES encrypted text in Java

I'm implementing in Java a third party application, but some part of the application I'm get a encrypt string like this: eGlhV2xNbmdqSFBkbEhkZDNpZ3gwQT09 and have to decrypt.

The guy who code this application is no longer here, so I need some help make a decrypt code in Java. this is the password :CB=Z8#P@0!N2/8$%3K-9C(5S9*FDH+0Z

public static class SystemCriptografia
{
    #region Atributos        
    private static string chave = "CB=Z8#P@0!N2/8$%3K-9C(5S9*FDH+0Z";
    private static SymmetricAlgorithm algoritmo = new RijndaelManaged();

    #endregion

    #region Métodos

    #region Métodos privados

    private static string base64Encode(string data)
    {
        byte[] encData_byte = new byte[data.Length];
        encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
        string encodedData = Convert.ToBase64String(encData_byte);
        return encodedData;
    }

    private static string base64Decode(string data)
    {
        UTF8Encoding encoder = new UTF8Encoding();
        Decoder utf8Decode = encoder.GetDecoder();

        byte[] todecode_byte = Convert.FromBase64String(data);
        int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
        char[] decoded_char = new char[charCount];
        utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
        string result = new String(decoded_char);
        return result;
    }

    private static string Criptografa(string valor, string chave)
    {
        byte[] ByteValor = Encoding.UTF8.GetBytes(valor);

        // Seta a chave privada
        algoritmo.Mode = CipherMode.CBC;
        algoritmo.Key = Encoding.Default.GetBytes(chave);
        algoritmo.IV = Encoding.Default.GetBytes("brasilshopsoft07");

        // Interface de criptografia / Cria objeto de criptografia
        ICryptoTransform cryptoTransform = algoritmo.CreateEncryptor();

        MemoryStream _memoryStream = new MemoryStream();
        CryptoStream _cryptoStream = new CryptoStream(_memoryStream, cryptoTransform, CryptoStreamMode.Write);

        // Grava os dados criptografados no MemoryStream
        _cryptoStream.Write(ByteValor, 0, ByteValor.Length);
        _cryptoStream.FlushFinalBlock();

        // Busca o tamanho dos bytes encriptados
        byte[] cryptoByte = _memoryStream.ToArray();

        // Converte para a base 64 string para uso posterior em um xml
        return Convert.ToBase64String(cryptoByte, 0, cryptoByte.GetLength(0));
    }

    private static string Descriptografa(string valor, string chave)
    {
        // Converte a base 64 string em num array de bytes
        byte[] cryptoByte = Convert.FromBase64String(valor);

        // Seta a chave privada
        algoritmo.Mode = CipherMode.CBC;
        algoritmo.Key = Encoding.Default.GetBytes(chave);
        algoritmo.IV = Encoding.Default.GetBytes("brasilshopsoft07");

        // Interface de criptografia / Cria objeto de descriptografia
        ICryptoTransform cryptoTransform = algoritmo.CreateDecryptor();

        MemoryStream _memoryStream = new MemoryStream(cryptoByte, 0, cryptoByte.Length);
        CryptoStream _cryptoStream = new CryptoStream(_memoryStream, cryptoTransform, CryptoStreamMode.Read);

        // Busca resultado do CryptoStream
        StreamReader _streamReader = new StreamReader(_cryptoStream);
        return _streamReader.ReadToEnd();
    }

    #endregion

    public static string ToCriptografa(this string valor)
    {
        return Criptografa(valor, chave);
    }

    public static string ToDescriptografa(this string valor)
    {
        return Descriptografa(valor, chave);
    }

    public static string ToCriptografaQueryString(this string valor)
    {
        return base64Encode(Criptografa(valor, chave));
    }

    public static string ToDescriptografaQueryString(this string valor)
    {
        return Descriptografa(base64Decode(valor), chave);
    }

    #endregion
}

and this is the java code that I'm trying to do :

public class Criptografia {

    private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING";
    private static final int KEY_SIZE = 256;

    public static void main(final String[] args) throws Exception {
        System.out.println(decryptAuthorizationString(
                "eGlhV2xNbmdqSFBkbEhkZDNpZ3gwQT09", "CB=Z8#P@0!N2/8$%3K-9C(5S9*FDH+0Z"));
    }

    private static String decryptAuthorizationString(final String authString,
            final String password) {
        try {
            // --- check if AES-256 is available
            if (Cipher.getMaxAllowedKeyLength(AES_CBC_PKCS5PADDING) < KEY_SIZE) {
                throw new IllegalStateException("Unlimited crypto files not present in this JRE");
            }

            // --- create cipher
            final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);

            // --- create the key and initial vector bytes
            final byte[] passwordEncoded = password.getBytes(UTF_16LE);
            final byte[] keyData = Arrays.copyOf(passwordEncoded, KEY_SIZE
                    / Byte.SIZE);
            final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());

            // --- init cipher
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"),
                    new IvParameterSpec(ivBytes));

            // --- decode & decrypt authentication string
            final byte[] authBytes = Base64.decode(authString);
            final byte[] decryptedData = cipher.doFinal(authBytes);

            // WARNING: may still decrypt to wrong string if
            // authString or password are incorrect - 
            // BadPaddingException may *not* be thrown
            return new String(decryptedData, UTF_16LE);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            // failure to authenticate
            return null;
        } catch (final GeneralSecurityException e) {
            throw new IllegalStateException(
                    "Algorithms or unlimited crypto files not available", e);
        }
    }
}

Upvotes: 1

Views: 855

Answers (1)

Artjom B.
Artjom B.

Reputation: 61892

The issues of your code:

  • Wrong character set chosen. If Encoding.Default is UTF-8 in C#, then the "password" and IV encodings must also be "UTF-8" in Java.

  • The IV is not derived from the key, but also a fixed value.

  • The ciphertext is actually doubly Base64 encoded. I guess somebody took "two is better than one" too literal.

Full code:

private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING";
private static final int KEY_SIZE = 256;
private static String UTF_8 = "UTF-8";

public static void main(final String[] args) throws Exception {
    System.out.println(decryptAuthorizationString(
            "eGlhV2xNbmdqSFBkbEhkZDNpZ3gwQT09", "CB=Z8#P@0!N2/8$%3K-9C(5S9*FDH+0Z"));
}

private static String decryptAuthorizationString(final String authString,
                                                 final String password) throws UnsupportedEncodingException {
    try {
        // --- check if AES-256 is available
        if (Cipher.getMaxAllowedKeyLength(AES_CBC_PKCS5PADDING) < KEY_SIZE) {
            throw new IllegalStateException("Unlimited crypto files not present in this JRE");
        }

        // --- create cipher
        final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);

        // --- create the key and initial vector bytes
        final byte[] passwordEncoded = password.getBytes(UTF_8);
        final byte[] ivBytes = "brasilshopsoft07".getBytes(UTF_8);

        // --- init cipher
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(passwordEncoded, "AES"),
                new IvParameterSpec(ivBytes));

        // --- decode & decrypt authentication string
        final byte[] authBytes = Base64.decode(authString);
        final byte[] authBytes2 = Base64.decode(authBytes);
        final byte[] decryptedData = cipher.doFinal(authBytes2);

        // WARNING: may still decrypt to wrong string if
        // authString or password are incorrect -
        // BadPaddingException may *not* be thrown
        return new String(decryptedData, UTF_8);
    } catch (BadPaddingException | IllegalBlockSizeException e) {
        // failure to authenticate
        return null;
    } catch (final GeneralSecurityException e) {
        throw new IllegalStateException(
                "Algorithms or unlimited crypto files not available", e);
    }
}

Output:

1

Notes:

  • Using a fixed IV is insecure. The IV must be chosen randomly in order reach semantic security. It doesn't have to be secret, so it can be sent along with the ciphertext. A common way is to prepend it to the ciphertext and slice it off before decryption.

  • Lose the second Base64 encoding. It just takes away space, but doesn't provide any useful feature.

  • Always use a specific encoding. Encoding.Default is nice for testing, but it may change depending on the machine it's running on and you will lose compatibility between multiple clients/servers.

  • Authenticate ciphertexts! If you don't add a message authentication code or use an authenticated mode like GCM or EAX, then an attacker may manipulate the ciphertext and you will not be able to determine this. This can go so far as to completely recover the plaintext of specific encrypted messages (padding oracle attack).

Upvotes: 1

Related Questions