Расшифровка Rijndael в Java, зашифрованная с использованием .NET

У нас есть вариант использования, когда мы хотим расшифровать данные в java, которые ранее были зашифрованы с использованием .NET.

Вот класс Crypt, используемый для шифрования и расшифровки данных в .NET:

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

Я отделил метод Decrypt и выполнил его, и он отлично работает в .NET, я хочу портировать код ниже:

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;
    }
}

Вот что я пробовал до сих пор:

   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");
 }

Я получаю исключение: «Данный последний блок не заполнен должным образом». Не знаю, что я сделал не так. Я ценю любую помощь в этом вопросе.

[[РЕДАКТИРОВАТЬ]]:

Я понял. Чего мне не хватало, так это того, что я использовал неправильную соль. Я декодировал постоянную соль, используемую (в виде байтов) в коде .NET, в ее счетчик String и использовал ее как соль в моем коде Java.

Также благодаря классу PasswordDerivedBytes, найденному здесь:

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;
    }

person Eswar Rajesh Pinapala    schedule 06.03.2014    source источник


Ответы (1)


Вы используете пользовательский вариант Base64 в коде C#: _ и - в качестве символов 62 и 63 и $ в качестве заполнения. Код C# преобразует их в обычный формат Base64 перед попыткой декодирования.

Ваш Java-код передает неверные данные в декодер Base64. Это должно вызвать IllegalArgumentException еще до того, как код достигнет криптографии.

Другая проблема заключается в том, что ваш код С# использует PBKDF1 для получения ключа из пароля, код Java напрямую использует пароль в качестве байтов ключа.

person Loki    schedule 06.03.2014
comment
Я обновил свой вопрос, я заменяю символы перед передачей их в декодер Base64? Не могли бы вы сообщить мне, если это не так? - person Eswar Rajesh Pinapala; 07.03.2014
comment
Как я могу заставить код Java использовать PBKDF1 вместо прямого использования байтов пароля? - person Eswar Rajesh Pinapala; 07.03.2014
comment
@EswarRajeshPinapala Способ превращения пароля в ключ отличается. Код C # использует PBKDF1 с фиксированной солью (фиксированная соль - плохая идея, кстати), код Java использует пароль напрямую (еще худшая идея) - person Loki; 07.03.2014
comment
Итак, вы говорите, что нет способа портировать код дешифрования, использующий PBKDF1? - person Eswar Rajesh Pinapala; 07.03.2014
comment
Просто реализуйте его из спецификации. Это что-то простое, например x = concat(pw, salt); for(i=0; i<100; i++){x = sha1(x)}. Или поищите в Google java-библиотеку, которая реализует PBKDF1 с SHA1 и 100 итерациями. - person Loki; 07.03.2014
comment
На самом деле я понял это. Отредактировал ответ. Большое спасибо за ваш вклад. - person Eswar Rajesh Pinapala; 07.03.2014