Почему я не могу зашифровать несколько строк, используя один шифратор в MonoTouch? - PullRequest
5 голосов
/ 11 августа 2011

Я использую следующий (урезанный) класс для шифрования некоторых данных перед отправкой их из приложения iPad в веб-службу WCF.

public class FlawedAlgorithm
{
    protected static byte[] key = { 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
    protected static byte[] vector = { 13, 37, 13, 37, 13, 37, 13, 37, 13, 37, 13, 37, 13, 37, 13, 37 };
    protected ICryptoTransform encryptor, decryptor;
    protected UTF8Encoding encoder;

    public FlawedAlgorithm()
    {
        using (var rijndael = new RijndaelManaged())
        {
            this.encryptor = rijndael.CreateEncryptor(key, vector);
            this.decryptor = rijndael.CreateDecryptor(key, vector);
        }

        this.encoder = new UTF8Encoding();
    }

    public string Encrypt(string unencrypted)
    {
        var buffer = this.encoder.GetBytes(unencrypted);

        return Convert.ToBase64String(Encrypt(buffer));
    }

    public string Decrypt(string encrypted)
    {
        var buffer = Convert.FromBase64String(encrypted);

        return this.encoder.GetString(Decrypt(buffer));
    }

    private byte[] Encrypt(byte[] buffer)
    {
        var encryptStream = new MemoryStream();

        using (var cryptoStream = new CryptoStream(encryptStream, this.encryptor, CryptoStreamMode.Write))
        {
            cryptoStream.Write(buffer, 0, buffer.Length);
        }

        return encryptStream.ToArray();
    }

    private byte[] Decrypt(byte[] buffer)
    {
        var decryptStream = new MemoryStream();

        using (var cryptoStream = new CryptoStream(decryptStream, this.decryptor, CryptoStreamMode.Write))
        {
            cryptoStream.Write(buffer, 0, buffer.Length);
        }

        return decryptStream.ToArray();
    }
}

Когда я запускаю следующий код на сервереи iPad, оба печатают одну и ту же зашифрованную строку.

var algorithm = new FlawedAlgorithm();

Console.WriteLine(algorithm.Encrypt("Some string"));

Однако, когда я пытаюсь зашифровать второе значение, результаты на сервере и iPad различаются.

var algorithm = new FlawedAlgorithm();

// The first encryption still functions correctly.
Console.WriteLine(algorithm.Encrypt("Some string"));

// This second encryption produces a different value on the iPad.
Console.WriteLine(algorithm.Encrypt("This text is a bit longer"));

Когда я расшифровываю отклоняющийся результат iPad на сервере, часть расшифрованной строки - это бред .Зашифрованные результаты с сервера расшифровываются правильно.

Проблема не проявляется, если я создаю новый экземпляр FlawedAlgorithm для каждого вызова, например:

// These statements produce the correct results on the iPad.
Console.WriteLine(new FlawedAlgorithm().Encrypt("Some string"));
Console.WriteLine(new FlawedAlgorithm().Encrypt("This text is a bit longer"));

Это заставляет меня задуматьсячто проблема лежит где-то в состоянии вовлеченных объектов.Я проверил переменную buffer в методе Encrypt(string) и значения, полученные экземпляром UTF8Encoding, верны.Это подразумевает, что поле encryptor (или его базовая реализация) является виновником .

Когда я начинаю изменять размер первого зашифрованного значения, я вижу изменения в результатевторой вызов шифрования.Это, вероятно, будет означать, что некоторая часть потока не очищается или не перезаписывается должным образом.Но потоки, используемые классом FlawedAlgorithm, не являются частью его состояния;они воссоздаются при каждом вызове метода.И объект encryptor не похож на тип, который управляет своими собственными потоками.

Кто-нибудь еще сталкивался с проблемой, подобной этой?Класс RijndaelManaged имеет недостатки?Или здесь есть какие-то подводные камни в управлении потоками и памятью, не связанные с этим примером криптографии?

PS: я проверил это на iPad и iPad Simulator;оба показывают это странное поведение.

1 Ответ

7 голосов
/ 11 августа 2011

При использовании криптографии .NET вы всегда должны проверять ICryptoTransform.CanReuseTransform (или предполагать, что он вернет false).Если он возвращает false, то вы не можете повторно использовать один и тот же шифратор / дешифровщик и должны создавать новые экземпляры.

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

Вы можете использовать что-то вроде:

 ICryptoTransform Decryptor {
    get {
       if (decryptor == null || !decryptor.CanReuseTransform)
          decryptor = rijndael.CreateDecryptor (key, vector);
        return decryptor;
    }
 }

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

...