2013-10-11 3 views
4

У меня очень любопытная проблема при расшифровке строк с использованием алгоритма AES. Приложение C# отправляет зашифрованные данные (строки) в приложение Java. Даже я использую ту же строку ключа, дешифрования приводит в Exception:Любопытное поведение с помощью C# для шифрования/дешифрования Java с использованием AES

javax.crypto.BadPaddingException: Учитывая последний блок не правильно проложенный на com.sun.crypto.provider.CipherCore.doFinal (CipherCore.java:810)

Однако, только если длина открытого текста ввода для шифрования (на стороне C#) превышает 1393 символа ... Но если длина равна 1393 символам или составляет менее 1393 символов, она работает нормально.

Вот C# codefor шифрования:

private static string Encrypt(string textToEncrypt, string key) 
    { 
     try 
     { 
      RijndaelManaged rijndaelCipher = new RijndaelManaged(); 
      rijndaelCipher.Mode = CipherMode.CBC; 
      rijndaelCipher.Padding = PaddingMode.PKCS7; 

      rijndaelCipher.KeySize = 0x80; // 256bit key 
      rijndaelCipher.BlockSize = 0x80; 
      byte[] pwdBytes = Encoding.UTF8.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; 
      ICryptoTransform transform = rijndaelCipher.CreateEncryptor(); 
      byte[] plainText = Encoding.UTF8.GetBytes(textToEncrypt); 
      return Convert.ToBase64String(transform.TransformFinalBlock(plainText, 0, plainText.Length)); 
     } 
     catch (Exception ex) 
     { 
      throw ex; 
     } 
    } 

и код Java для расшифровки:

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"); 
    IvParameterSpec ivSpec = new IvParameterSpec(keyBytes); 
    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); 

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

Я попытался заменить BASE64Decoder на Base64 кодек от апача, но результат был тот же ... Буду благодарен за любые советы или идеи. Благодарю.

+0

Получает ли Java-код зашифрованную строку через веб-запрос? Если это так, убедитесь, что вы выполняете декодирование html, если оно закодировано между сообщением. –

+0

В .net вы указали PaddingMode.PKCS7 для заполнения, а в вашем Java-коде вы указали PKCS5Padding. Неужели? –

+0

Прокладка PKCS5 и дополнение PKCS7 эквивалентны в этом контексте. –

ответ

1

Если ваш передающий шифрованный текст по сети, вы должны обеспечить его целостность с помощью message authentication code. Перед попыткой расшифровки зашифрованного текста должен быть проверен MAC. Это будет защищать как от непреднамеренного развращения, так и от вредоносного вмешательства в сообщение. В этом случае он должен помочь вам убедиться, что зашифрованный текст, который вы передаете, точно такой же, как и ваш прием.

Я переписал сторону расшифровки java, чтобы выполнить проверку MAC. Он предполагает, что MAC генерируется с использованием алгоритма HMACSHA1, который был вставлен с тем же ключевым материалом, который используется для шифрования AES (ПРИМЕЧАНИЕ: это только пример, ключ шифрования и ключ HMAC должны быть разными в реальной системе) и добавлены к шифротекста. Первые 20 байтов сообщения должны быть MAC, после чего следует зашифрованный текст сразу же после этого.

public static String Decrypt(String message, 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"); 
    IvParameterSpec ivSpec = new IvParameterSpec(keyBytes); 

    byte[] messageBytes = DatatypeConverter.parseBase64Binary(message); 
    byte[] macBytes = new byte[20]; 
    byte[] ciphertext = new byte[messageBytes.length - 20]; 

    System.arraycopy(messageBytes, 0, macBytes, 0, macBytes.length); 
    System.arraycopy(messageBytes, 20, ciphertext, 0, ciphertext.length); 

    Mac mac = Mac.getInstance("HMACSHA1"); 
    mac.init(keySpec); 

    verifyMac(mac.doFinal(ciphertext), macBytes); 

    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); 

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

private static void verifyMac(byte[] mac1, byte[] mac2) throws Exception { 
    MessageDigest sha = MessageDigest.getInstance("SHA1"); 
    byte[] mac1_hash = sha.digest(mac1); 
    sha.reset(); 
    byte[] mac2_hash = sha.digest(mac2); 

    if(!Arrays.equals(mac1_hash, mac2_hash)){ 
     throw new RuntimeException("Invalid MAC"); 
    } 

} 

Только некоторые замечания по практике безопасности. Использование байтов UTF-8 типизированного пароля не дает вам достаточной энтропии в вашем секретном ключе. Если вы собираетесь использовать пароль в качестве криптового ключа, вы должны обработать его с помощью алгоритма растягивания ключей. PBKDF2 - популярный выбор для этого. IV для шифрования AES должен генерироваться случайным образом (используя криптографически безопасный источник случайности) и передаваться в ясности с помощью зашифрованного текста. Ключ, используемый для инициализации шифрования AES, должен отличаться от ключа, используемого для MAC. Вы можете использовать PBKDF2 для создания ключа в два раза дольше, чем вам нужно, из вашего поставленного пароля и использовать первую половину для AES, а вторую половину для HMAC-SHA1. В заключение, при проверке MAC-адресов вы должны проверить хэш MAC вместо MAC (как показано в методе выше). Проверка MAs напрямую предоставляет возможный вектор временной атаки.

+0

Большое спасибо за исправление. Здесь у меня есть дополнительный вопрос: Мое сообщение, переданное по сети, состоит из оболочки, которая инкапсулирует содержимое и хэш содержимого. Эта оболочка шифруется и передается по сети. После дешифрования я снова вычисляю хэш содержимого и сравниваю с поставленным хешем (инкапсулированным в обертку). Итак, правильно ли этот подход? – Joseph

+0

@ Целостность текста должна быть обеспечена целостность сообщения Joseph. Предоставление вам возможности расшифровать непроверенное сообщение может привести к уязвимостям, например, прописному оракулу. Стандартный способ достижения целостности сообщения - использовать MAC на зашифрованном тексте. – Dev

+0

@Joseph Разработка правильных систем crytpo - это сложная задача, чтобы получить правильные результаты, и даже эксперты все еще ошибаются. Если в вашем проекте существует реальная потребность в безопасности, я настоятельно рекомендую вам использовать SSL/TLS вместо того, чтобы пытаться перевернуть собственную криптосистему. TLS заботится о конфиденциальности, целостности и аутентификации в одном, и вы можете выбирать из множества наборов шифров. – Dev

Смежные вопросы