AES расшифровка нетекстовых файлов приводит к повреждению данных

#c# #encryption #utf-8 #aes #rncryptor

#c# #шифрование #utf-8 #aes #rncryptor

Вопрос:

Я пишу приложение для Windows на C #, которое должно взаимодействовать с приложением Mac (написанным на Cocoa). Это приложение шифрует файлы в AES с помощью CBC (IV, ключ, соль, HMAC). Я не очень разбираюсь в шифровании, но я думаю, что это то, что он делает. Используемая нами библиотека Cocoa — это RNCryptor. У них есть версия C #, которую я использую на стороне Windows (с несколькими изменениями, в основном для использования byte[] вместо строк).

Теперь текстовые файлы расшифрованы правильно, но другие файлы (например, файл PNG) в конечном итоге повреждены (правильный файл справа и поврежденный слева, с использованием кодировки UTF8, вы можете видеть, что там много бриллиантов с ?s):

различия

Я считаю, что это связано с кодировкой файла, но я пробовал UTF8, Default, Unicode, ASCII … и выходные файлы всегда повреждены с разными размерами файлов, поскольку ASCII и кодировка по умолчанию (UTF16, я полагаю) являются самыми близкими по размеру.

Это модифицированный код RNCryptor, который я использовал:

 public byte[] Decrypt (byte[] encryptedBase64, string password)
    {
        PayloadComponents components = this.unpackEncryptedBase64Data (encryptedBase64);

        if (!this.hmacIsValid (components, password)) {
            return null;
        }

        byte[] key = this.generateKey (components.salt, password);

        byte[] plaintextBytes = new byte[0];

        switch (this.aesMode) {
            case AesMode.CTR:
                // Yes, we are "encrypting" here.  CTR uses the same code in both directions.
                plaintextBytes = this.encryptAesCtrLittleEndianNoPadding(components.ciphertext, key, components.iv);
                break;

            case AesMode.CBC:
                plaintextBytes = this.decryptAesCbcPkcs7(components.ciphertext, key, components.iv);
                break;
        }

        return plaintextBytes;
    }

    private byte[] decryptAesCbcPkcs7 (byte[] encrypted, byte[] key, byte[] iv)
    {
        var aes = Aes.Create();
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        var decryptor = aes.CreateDecryptor(key, iv);

        string plaintext;
        using (MemoryStream msDecrypt = new MemoryStream(encrypted))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                {
                    plaintext = srDecrypt.ReadToEnd();
                }
            }
        }

        return Encoding.UTF8.GetBytes(plaintext);
    }

    private PayloadComponents unpackEncryptedBase64Data (byte[] encryptedBase64)
    {
        List<byte> binaryBytes = new List<byte>();
        binaryBytes.AddRange (encryptedBase64);

        PayloadComponents components;
        int offset = 0;

        components.schema = binaryBytes.GetRange(0, 1).ToArray();
        offset  ;

        this.configureSettings ((Schema)binaryBytes [0]);

        components.options = binaryBytes.GetRange (1, 1).ToArray();
        offset  ;

        components.salt = binaryBytes.GetRange (offset, Cryptor.saltLength).ToArray();
        offset  = components.salt.Length;

        components.hmacSalt = binaryBytes.GetRange(offset, Cryptor.saltLength).ToArray();
        offset  = components.hmacSalt.Length;

        components.iv = binaryBytes.GetRange(offset, Cryptor.ivLength).ToArray();
        offset  = components.iv.Length;

        components.headerLength = offset;

        components.ciphertext = binaryBytes.GetRange (offset, binaryBytes.Count - Cryptor.hmac_length - components.headerLength).ToArray();
        offset  = components.ciphertext.Length;

        components.hmac = binaryBytes.GetRange (offset, Cryptor.hmac_length).ToArray();

        return components;

    }

    private bool hmacIsValid (PayloadComponents components, string password)
    {
        byte[] generatedHmac = this.generateHmac (components, password);

        if (generatedHmac.Length != components.hmac.Length) {
            return false;
        }

        for (int i = 0; i < components.hmac.Length; i  ) {
            if (generatedHmac[i] != components.hmac[i]) {
                return false;
            }
        }
        return true;
    }
  

И это мой код для расшифровки и записи файла:

  byte[] decryptedFile = this.decryptor.Decrypt(File.ReadAllBytes(filePath), password);
 File.WriteAllBytes(filePath, decryptedFile);
  

Что здесь может быть не так? Заранее спасибо.

Ответ №1:

Проблема заключается в использовании StreamReader при расшифровке. StreamReader читает текст (здесь UTF-8), не произвольные двоичные данные. Одним из решений было бы считывание данных в MemoryStream и использование его ToArray() метода для получения результата byte[] .

Комментарии:

1. Привет, спасибо за ответ. К сожалению, если я не сделал что-то неправильно, это не работает. Это модифицированный метод: pastebin.com/DkXFndbC . Файл заканчивается как множество китайских символов.

2. @pmerino Выглядит так, что вы неправильно Read() вводите из msDecrypt , который содержит зашифрованные байты, а не из csDecrypt , которые будут расшифрованными байтами.

3. Спасибо! Попытался переписать код, имея в виду то, что вы сказали, и теперь это сработало!