3

I have to decrypt text in JAVA, encrypted in C# (AES, RijndaelManaged).

After several days of reading and searching solutions, and a lot of stackoverflow solutions tested, i still have a problem unsolved.

I apply C# code here (which is working) and the java code (which not fully working).

Here is the exception from the java code:

Exception in thread "main" javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at test.Decrypt.main(Decrypt.java:39)

JAVA code

import java.nio.charset.StandardCharsets;
import java.security.spec.KeySpec;
import java.util.Base64;

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

public class Decrypt {

public static void main(String[] args) throws Exception {

    String encText = "EAAAADE2ODA2NjQya2JNN2M1ISShgi+Oi5tbsPgOz5KsCHj0";
    final String password = "KJH#$@kds32@!kjhdkftt";
    final String iv = "16806642kbM7c5!$";
    byte[] salt = new byte[] { 34, (byte) 134, (byte) 145, 12, 7, 6, (byte) 243, 63, 43, 54, 75, 65, 53, 2, 34, 54,
            45, 67, 64, 64, 32, (byte) 213 };

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256);
    SecretKey tmp = factory.generateSecret(spec);

    SecretKeySpec secret = new SecretKeySpec(tmp.getEncoded(), "AES");
    IvParameterSpec ivs = new IvParameterSpec(iv.getBytes(StandardCharsets.US_ASCII));

    System.out.println("Key:" + Base64.getEncoder().encodeToString(secret.getEncoded()));
    System.out.println("IV:" + Base64.getEncoder().encodeToString(ivs.getIV()));

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, ivs);

    byte [] decodedText = Base64.getDecoder().decode(encText);

    String plaintext = new String(cipher.doFinal(decodedText), "UTF-8");
    System.out.println(plaintext);
  }
}

C# code

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

namespace Test
{
public class EncriptionHelper
{   
        public static void Main()
        {
            var cleanTxt = "8212093345";
            var encTxt = "EAAAADE2ODA2NjQya2JNN2M1ISShgi+Oi5tbsPgOz5KsCHj0";
            var secret = "KJH#$@kds32@!kjhdkftt";

            var xtxt =  EncryptStringAES(cleanTxt, secret);             
            var txt =   DecryptStringAES(encTxt, secret);

            Console.WriteLine(txt);
            Console.WriteLine(xtxt);
        }

    private static byte[] vector = Encoding.ASCII.GetBytes("16806642kbM7c5!$");
    private static byte[] salt = new byte[] { 34, 134, 145, 12, 7, 6, 243, 63, 43, 54, 75, 65, 53, 2, 34, 54, 45, 67, 64, 64, 32, 213 };

    /// <summary>
    /// Encrypt the given string using AES.  The string can be decrypted using 
    /// DecryptStringAES().  The sharedSecret parameters must match.
    public static string EncryptStringAES(string text, string sharedSecret)
    {
        if (string.IsNullOrEmpty(text))
        {
            throw new ArgumentNullException("Text is null or empty");
        }

        if (string.IsNullOrEmpty(sharedSecret))
        {
            throw new ArgumentNullException("Secret is null or empty");
        }

        if (salt == null || salt.Length == 0)
        {
            throw new ArgumentNullException("Salt is null or empty");
        }

        string outStr = null;
        RijndaelManaged aesAlg = null;
        try
        {
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, salt);

            aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
            aesAlg.IV = vector;

            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
                msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(text);
                    }
                }
                outStr = Convert.ToBase64String(msEncrypt.ToArray());
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        // Return the encrypted bytes from the memory stream.
        return outStr;
    }

    /// <summary>
    /// Decrypt the given string.  Assumes the string was encrypted using 
    /// EncryptStringAES(), using an identical sharedSecret.
    /// </summary>
    public static string DecryptStringAES(string cipherText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(cipherText))
        {
            throw new ArgumentNullException("Text is null or empty");
        }

        if (string.IsNullOrEmpty(sharedSecret))
        {
            throw new ArgumentNullException("Secret is null or empty");
        }

        if (salt == null || salt.Length == 0)
        {
            throw new ArgumentNullException("Salt is null or empty");
        }

        RijndaelManaged aesAlg = null;
        string result = null;

        try
        {
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, salt);
            byte[] bytes = Convert.FromBase64String(cipherText);
            using (MemoryStream msDecrypt = new MemoryStream(bytes))
            {
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                aesAlg.IV = ReadByteArray(msDecrypt);

                Console.WriteLine("KEY: "+ Convert.ToBase64String(aesAlg.Key));
                Console.WriteLine("IV: "+ Convert.ToBase64String(aesAlg.IV));


                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        result = srDecrypt.ReadToEnd();
                    }
                }
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        return result;
    }

    private static byte[] ReadByteArray(Stream s)
    {
        byte[] rawLength = new byte[sizeof(int)];
        if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
        {
            throw new SystemException("Stream did not contain properly formatted byte array");
        }

        byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
        if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
        {
            throw new SystemException("Did not read byte array properly");
        }
        return buffer;
    }
  }
 }
Sali Manaf
  • 166
  • 1
  • 7
  • Your question should be self-contained, so please include the relevant C# code as well; don't link to it. – Mark Rotteveel Dec 15 '18 at 08:16
  • A good method to go about debugging this is to write both encrypt and decrypt in both languages and then compare the outputs (or intermediary steps) to see where the problem occurs – Joe Phillips Dec 16 '18 at 22:05

1 Answers1

2

Here is Java code to decode encoded data from C#:

public static void main(String[] args) throws Exception {

    String encText = "EAAAADE2ODA2NjQya2JNN2M1ISShgi+Oi5tbsPgOz5KsCHj0";
    final String password = "KJH#$@kds32@!kjhdkftt";
    byte[] salt = new byte[] { 34, (byte) 134, (byte) 145, 12, 7, 6, (byte) 243, 63, 43, 54, 75, 65, 53, 2, 34, 54,
            45, 67, 64, 64, 32, (byte) 213 };

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256);
    SecretKey tmp = factory.generateSecret(spec);

    SecretKeySpec secret = new SecretKeySpec(tmp.getEncoded(), "AES");

    byte[] data = Base64.getDecoder().decode("EAAAADE2ODA2NjQya2JNN2M1ISShgi+Oi5tbsPgOz5KsCHj0");
    // skip first 4 bytes (the length of IV) and get IV byte array
    byte[] iv = Arrays.copyOfRange(data, 4, 20);

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
    // skip IV length (4 bytes) and IV (16 bytes)
    cipher.update(data, 20, data.length - 20);
    String plaintext = new String(cipher.doFinal(), "UTF-8");
    System.out.println(plaintext);
}

Below is simplified example, just in case if someone has access to both sources:

C#:

private static byte[] salt = new byte[] { 34, 134, 145, 12, 7, 6, 243, 63, 43, 54, 75, 65, 53, 2, 34, 54, 45, 67, 64, 64, 32, 213 };
private static byte[] vector = Encoding.ASCII.GetBytes("16806642kbM7c5!$");
private static string cleartext = "8212093345";

static void Main(string[] args)
{
    var kdf = new Rfc2898DeriveBytes("KJH#$@kds32@!kjhdkftt", salt);
    using (var aes = new RijndaelManaged())
    {
        aes.Key = kdf.GetBytes(aes.KeySize / 8);
        aes.IV = vector;
        using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
        using (var ms = new MemoryStream())
        {
            using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
            {
                // don't use StreamWriter, it just makes things more complicated
                var bytes = Encoding.ASCII.GetBytes(cleartext);
                cs.Write(bytes, 0, bytes.Length);
            }
            Console.WriteLine(Convert.ToBase64String(ms.ToArray()));
            // outputs: oYIvjoubW7D4Ds+SrAh49A==
        }
    }
}

Java:

public static void main(String[] args) throws Exception {

    String encText = "EAAAADE2ODA2NjQya2JNN2M1ISShgi+Oi5tbsPgOz5KsCHj0";
    final String password = "KJH#$@kds32@!kjhdkftt";
    final String iv = "16806642kbM7c5!$";
    byte[] salt = new byte[] { 34, (byte) 134, (byte) 145, 12, 7, 6, (byte) 243, 63, 43, 54, 75, 65, 53, 2, 34, 54,
            45, 67, 64, 64, 32, (byte) 213 };

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 256);
    SecretKey tmp = factory.generateSecret(spec);

    SecretKeySpec secret = new SecretKeySpec(tmp.getEncoded(), "AES");
    IvParameterSpec ivs = new IvParameterSpec(iv.getBytes(StandardCharsets.US_ASCII));

     // your code use PKCS7, but I use PKCS5 because it shows exception in my case
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, ivs);

    // base64 string from C# output
    String plaintext = new String(cipher.doFinal(Base64.getDecoder().decode("oYIvjoubW7D4Ds+SrAh49A==")), "UTF-8");
    System.out.println(plaintext);
}
Zergatul
  • 1,481
  • 13
  • 22
  • Hello, thank you for the provided solution, but i don't have controll over the C# code (it is only for reference how they to that in c#). I use PKCS7 because this is the default value for RijndaelManaged (what i get when i print it in the console). For PKCS7 you need JCE provider (Bouncy Castle for example). I need a solution to decrypt the provided in the sample encrypted text. – Sali Manaf Dec 16 '18 at 19:00
  • 1
    @NewspapersApps I added Java code just for your case. If I am not wrong, PKCS5 and PKCS7 paddings are the same. – Zergatul Dec 16 '18 at 22:03
  • Thank you a lot for the solution, you helped me a lot :) – Sali Manaf Dec 17 '18 at 08:25
  • This solution helped me a lot. Thanks for this :) – Aimkiller Dec 08 '20 at 18:22