C# System.Security.Cryptography - Исключение, возникающее при дешифровании с неверным ключом - Как работает заполнение? - PullRequest
0 голосов
/ 05 февраля 2020

Я пытался узнать больше о System.Security.Cryptography в c# с онлайн-документами и примерами. До сих пор я был в состоянии успешно зашифровать и расшифровать как текст, так и файлы; Тем не менее, я получил исключение при попытке расшифровать с неправильным ключом.

Blockquote System.Security.Cryptography.CryptographicException: 'Заполнение является недопустимым и не может быть удалено.'

Шифрование

private static byte[] EncryptData(SymmetricAlgorithms symmetricAlgorithm, byte[] inputBytes, byte[] key)
    {
        byte[] output;

        using (SymmetricAlgorithm algorithm = SymmetricAlgorithmFactory(symmetricAlgorithm))
        {
            byte[] encrypted;
            byte[] salt = new byte[PBKDF2_SaltBytes];
            int maxKeySize = GetLegalKeySizes(algorithm).Max();

            _rng.GetBytes(salt); //
            using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(key, salt, PBKDF2_Iterations))
            {
                algorithm.Key = pbkdf2.GetBytes(maxKeySize);
            }

            //algorithm.Padding = PaddingMode.PKCS7;

            using (ICryptoTransform cryptoTransform = algorithm.CreateEncryptor())
            {
                using (MemoryStream inputStream = new MemoryStream(inputBytes), transformedStream = new MemoryStream())
                {
                    using (CryptoStream cryptoStream = new CryptoStream(inputStream, cryptoTransform, CryptoStreamMode.Read))
                    {
                        cryptoStream.CopyTo(transformedStream);
                    }

                    encrypted = transformedStream.ToArray();
                }
            }

            output = new byte[salt.Length + algorithm.IV.Length + encrypted.Length];
            Buffer.BlockCopy(salt, 0, output, 0, salt.Length);
            Buffer.BlockCopy(algorithm.IV, 0, output, salt.Length, algorithm.IV.Length);
            Buffer.BlockCopy(encrypted, 0, output, salt.Length + algorithm.IV.Length, encrypted.Length);
        }

        return output;
    }

Дешифрование

private static byte[] DecryptData(SymmetricAlgorithms symmetricAlgorithm, byte[] inputBytes, byte[] key)
    {
        using (SymmetricAlgorithm algorithm = SymmetricAlgorithmFactory(symmetricAlgorithm))
        {
            byte[] salt = new byte[PBKDF2_SaltBytes];
            byte[] iv = new byte[algorithm.IV.Length];
            byte[] encryptedData = new byte[inputBytes.Length - salt.Length - iv.Length];

            int maxKeySize = GetLegalKeySizes(algorithm).Max();
            Buffer.BlockCopy(inputBytes, 0, salt, 0, salt.Length);
            Buffer.BlockCopy(inputBytes, salt.Length, iv, 0, iv.Length);
            Buffer.BlockCopy(inputBytes, salt.Length + iv.Length, encryptedData, 0, encryptedData.Length);

            algorithm.IV = iv;
            //algorithm.Padding = PaddingMode.PKCS7;

            using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(key, salt, PBKDF2_Iterations))
            {
                algorithm.Key = pbkdf2.GetBytes(maxKeySize);
            }

            using(ICryptoTransform cryptoTransform = algorithm.CreateDecryptor())
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(encryptedData, 0, encryptedData.Length);
                        cryptoStream.FlushFinalBlock();                         
                    }
                    return memoryStream.ToArray();
                }
            }
        }
    }

SymmetricAlgorithmFactory возвращает экземпляр SymmetricAlgorithm

private static SymmetricAlgorithm SymmetricAlgorithmFactory(SymmetricAlgorithms symmetricAlgorithm)
    {
        switch (symmetricAlgorithm)
        {
            case SymmetricAlgorithms.AES:
                return new AesCryptoServiceProvider();
            case SymmetricAlgorithms.DES:
                return new DESCryptoServiceProvider();
            case SymmetricAlgorithms.RC2:
                return new RC2CryptoServiceProvider();
            case SymmetricAlgorithms.Rijndael:
                return new RijndaelManaged();
            case SymmetricAlgorithms.TripleDES:
                return new TripleDESCryptoServiceProvider();
            default:
                throw new Exception("The provided Symmetric algorithm is not supported.");
        }
    }

public enum SymmetricAlgorithms
{
    AES,
    DES,
    RC2,
    Rijndael,
    TripleDES
}

Дополнительные свойства класса

private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
    private const int PBKDF2_SaltBytes = 8; //PBKDF2 recommends 64 bits minimum. 64 / 8 = 8 bytes
    private const int PBKDF2_Iterations = 10000;
    private static Encoding encoding = Encoding.UTF32;

Используя AES для тестирования, я затем попытался изменить свойство padding и получил несколько разных сообщений

PaddingMode.None - Метод шифрования вызвал исключение

Blockquote System.Security.Cryptography.CryptographicException: 'Входные данные не являются полным блоком.'

PaddingMode.PKCS7 + PaddingMode.ANSIX923 + PaddingMode.ISO10126 - метод дешифрования сгенерировал исключение

Blockquote System.Security.Cryptography.CryptographicException: 'Заполнение недействительно и не может быть удален. '

PaddingMode.Zeros - Кажется, это работает с правильным и неправильным ключом

Кто-нибудь сможет Помогите понять, как работают отступы и почему некоторые режимы не работают? Будет ли это ситуация, когда для каждого из разных алгоритмов Symmetri c потребуется разное заполнение?

1 Ответ

1 голос
/ 05 февраля 2020

В Википедии есть статья о Cryptographi c padding , которая может помочь.

Блочные шифры шифруют данные в виде блоков размером с блок. Большинство сообщений не являются точным количеством блоков, поэтому для заполнения последнего блока до нужного размера необходимо заполнение. При расшифровке отступ автоматически удаляется.

В вашем случае расшифровка не распознает заполнение и жалуется.

Возможных причин много. Неправильный ключ будет искажать все, включая отступы. В самом деле, любая ошибка дешифрования может привести к изменению заполнения, вызывая эту проблему.

Очевидно, что если функция шифрования применяет один тип заполнения, а функция дешифрования ожидает другой тип, то вы получите эту проблему. Убедитесь, что обе стороны используют одинаковую прокладку. Если у вас нет веских причин, используйте PKCS # 7.

Когда вы устанавливаете PaddingMode.None, метод шифрования не добавляет заполнение, поэтому последний блок был не дополнен и, следовательно, имеет неправильный размер. Таким образом, сторона шифрования пометила его. Этот режим полезен только для сообщений фиксированного размера, которые уже составляют целое число блоков.

PaddingMode.Zeros может работать, если в последнем блоке имеются завершающие нули. Проблема в том, что он потеряет все завершающие нули в исходном сообщении, если оно присутствует. Не рекомендуется.

Использование нескольких режимов заполнения очень необычно; одного достаточно. Я подозреваю, что метод расшифровки выбрал неправильный режим из трех, чтобы попытаться первым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...