1

We have a use case where we are looking to decrypt data in java which was previously encrypted using .NET

Here is the Crypt class used to Encrypt and Decrypt Data in .NET :

https://gist.github.com/epinapala/9400064

I separated the Decrypt method and executed, and It works fine in .NET, I am looking to port the code below:

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

class Program
{
    static void Main()
    {
        {
            string line = "f5EBWYipPKG1FpyTEP7pyPLLJNpqrvwYJFs8iMw9mOY$";

            line = line.Replace('-', '+').Replace('_', '/').Replace('$', '=');

            while (line.Length % 4 != 0){
                    line = line.PadRight(line.Length + (4 - line.Length % 4), '=');
            }
             Console.WriteLine(line);


            byte[] inputBuffer = Convert.FromBase64String(line);
             Console.WriteLine(inputBuffer.Length);
            byte[] numArray = new byte[16];
            byte[] key = new PasswordDeriveBytes("ThisIsMyKey", new byte[13]
      {
        (byte) 73,
        (byte) 118,
        (byte) 97,
        (byte) 110,
        (byte) 32,
        (byte) 77,
        (byte) 101,
        (byte) 100,
        (byte) 118,
        (byte) 101,
        (byte) 100,
        (byte) 101,
        (byte) 118
      }).GetBytes(16);

      Rijndael rijndael = GetRijndael(key);

      for (int index = 0; index < 16; ++index){
          numArray[index] = inputBuffer[index];
      }
           rijndael.IV = numArray;

            string decodedString = Encoding.UTF8.GetString(rijndael.CreateDecryptor().TransformFinalBlock(inputBuffer, 16, inputBuffer.Length - 16));
            Console.WriteLine(decodedString);
        }
    }


    private static Rijndael GetRijndael(byte[] key)
    {
      Rijndael rijndael = Rijndael.Create();
      rijndael.Mode = CipherMode.CBC;
      rijndael.KeySize = key.Length * 8;
      rijndael.Key = key;
      rijndael.Padding = PaddingMode.PKCS7;
      return rijndael;
    }
}

Here is what I tried so far:

   public static void main() {
     ecnryptedData = "f5EBWYipPKG1FpyTEP7pyPLLJNpqrvwYJFs8iMw9mOY$";
     ecnryptedData = ecnryptedData.replace('-', '+')
         .replace('_', '/').replace('$', '=');

     while (ecnryptedData.length() % 4 != 0) {
         ecnryptedData = StringUtils.rightPad(ecnryptedData, ecnryptedData.length() + (4 - ecnryptedData.length() % 4),
             "=");
     }
     System.out.println(Decrypt(ecnryptedData, "ThisIsMyKey"));
 }

 public static String Decrypt(String text, String key) throws Exception {
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
     byte[] keyBytes = new byte[16];
     byte[] b = key.getBytes("UTF-8");
     int len = b.length;
     if (len > keyBytes.length) len = keyBytes.length;
     System.arraycopy(b, 0, keyBytes, 0, len);
     SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
     AlgorithmParameterSpec spec = new IvParameterSpec(keyBytes);
     cipher.init(Cipher.DECRYPT_MODE, keySpec, spec);

     BASE64Decoder decoder = new BASE64Decoder();
     byte[] results = cipher.doFinal(decoder.decodeBuffer(text));
     return new String(results, "UTF-8");
 }

I am getting the exception : "Given final block not properly padded". Not sure what I did wrong. I appreciate any help on this issue.

[[EDIT]]:

I figured it out. The thing I was missing was that I was using a wrong salt. I decoded the constant salt used(as bytes) in the .NET code into its String counter part, and used it as Salt in my Java code.

Also Thanks to the PasswordDerivedBytes class found here :

https://github.com/getkksingh/TimesSSO/blob/fc78e4b30d5fd347341757c02eea6c9271575515/src/java/com/timesgroup/sso/hibernate/apis/PasswordDeriveBytes.java

private static byte[] decryptData(byte[] data, String password,
            String paddingMode, String salt) throws Exception {
        if (data == null || data.length == 0)
            throw new IllegalArgumentException("data is empty");
        if (password == null || password == "")
            throw new IllegalArgumentException("password is empty");
        if (salt == null || salt == "")
            salt = ".";
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        byte[] saltBytes = salt.getBytes("UTF8");
        byte[] passBytes = password.getBytes("UTF8");

        PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(
                new SHA1Digest());
        generator.init(passBytes, saltBytes, 100);

        byte[] key = ((KeyParameter) generator.generateDerivedParameters(256))
                .getKey();
        passBytes = new byte[16];
        saltBytes = new byte[16];
        System.arraycopy(key, 0, passBytes, 0, 16);
        System.arraycopy(key, 16, saltBytes, 0, 16);

        Cipher cipher = Cipher.getInstance("AES/CBC/" + paddingMode, "BC");
        SecretKeySpec keySpec = new SecretKeySpec(passBytes, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec,
                new IvParameterSpec(saltBytes));

        byte[] original = cipher.doFinal(data);
        return original;
    }

1 Answer 1

1

You are using a custom Base64 variant in the C# code: _ and - as characters 62 and 63 and $ as padding. The C# code converts these to normal Base64 before attempting decoding.

Your java code passes invalid data to the Base64 decoder. This should throw an IllegalArgumentException before the code even reaches the crypto.

Another issue is that your C# code uses PBKDF1 to derive the key from the password, the java code directly uses the password as key bytes.

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

6 Comments

I Updated my question, I am replacing the characters before passing them to Base64 decoder? Can you please let me know If that's wrong?
How can I get the Java code to use PBKDF1 instead of using the password bytes directly?
@EswarRajeshPinapala The way you turn the password into a key differs. The C# code uses PBKDF1 with a fixed salt (a fixed salt is a bad idea BTW), the java code uses the password directly (an even worse idea)
So Are you saying, there is no way to port the decryption code that uses PBKDF1?
Just implement it from spec. It's something simple like x = concat(pw, salt); for(i=0; i<100; i++){x = sha1(x)}. Or google for a java library which implements PBKDF1 with SHA1 and 100 iterations.
|

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.