Проблема BinaryFormatter и CryptoStream при десериализации - PullRequest
3 голосов
/ 11 августа 2011

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

UPDATE: Я переделал код на это:

using (Stream innerStream = File.Create(this.GetFullFileNameForUser(securityContext.User, applicationName)))
            {
                using (Stream cryptoStream = new CryptoStream(innerStream, GetCryptoProvider().CreateEncryptor(), CryptoStreamMode.Write))
                {
                    // 3. write to the cryptoStream 
                    //BinaryFormatter bf = new BinaryFormatter();
                    //bf.Serialize(cryptoStream, securityContext);
                    XmlSerializer xs = new XmlSerializer(typeof(SecurityContextDTO));
                    xs.Serialize(cryptoStream, securityContext);
                }
            }


 using (Stream innerStream = File.Open(this.GetFullFileNameForUser(user, applicationName), FileMode.Open))
        {
            using (Stream cryptoStream = new CryptoStream(innerStream, GetCryptoProvider().CreateDecryptor(), CryptoStreamMode.Read))
            {
                //BinaryFormatter bf = new BinaryFormatter();
                //return (SecurityContextDTO)bf.Deserialize(cryptoStream);
                XmlSerializer xs = new XmlSerializer(typeof(SecurityContextDTO));
                //CryptographicException here
                return (SecurityContextDTO)xs.Deserialize(cryptoStream);
            }
        }

Теперь я получаю криптографическое исключение при десериализации: неверные данные

ОРИГИНАЛ:

Я делаю это:

public void StoreToFile(SecurityContextDTO securityContext, string applicationName)
    {
        if (securityContext.LoginResult.IsOfflineMode == false)
        {
            Stream stream = null;
            CryptoStream crStream = null;
            try
            {
                TripleDESCryptoServiceProvider cryptic = GetCryptoProvider();

                stream = File.Open(this.GetFullFileNameForUser(securityContext.User, applicationName), FileMode.Create);
                crStream = new CryptoStream(stream,
                   cryptic.CreateEncryptor(), CryptoStreamMode.Write);

                BinaryFormatter bFormatter = new BinaryFormatter();
                bFormatter.Serialize(crStream, securityContext);
            }
            catch(Exception)
            {
                throw;
            }
            finally
            {
                if (crStream != null)
                    crStream.Close();
            }
        }
    }



public SecurityContextDTO RetrieveFromFile(UserDTO user,string applicationName)
    {
        SecurityContextDTO objectToSerialize;
        Stream stream = null;
        CryptoStream crStream=null;
        try
        {
            stream = File.Open(this.GetFullFileNameForUser(user, applicationName), FileMode.Open);
             crStream= new CryptoStream(stream,
                GetCryptoProvider().CreateDecryptor(), CryptoStreamMode.Read);
            BinaryFormatter bFormatter = new BinaryFormatter();
            //Exception here
            objectToSerialize = (SecurityContextDTO)bFormatter.Deserialize(crStream); 
        }
        catch (Exception)
        {
            objectToSerialize = null;
        }
        finally
        {
            if (crStream!=null)
                crStream.Close();
        }
        return objectToSerialize;
    }


private static TripleDESCryptoServiceProvider GetCryptoProvider()
    {
        TripleDESCryptoServiceProvider cryptic = new TripleDESCryptoServiceProvider();
        try
        {
            cryptic.Key = ASCIIEncoding.ASCII.GetBytes(CrypKey);

            Rfc2898DeriveBytes db = new Rfc2898DeriveBytes("sdddsdsd", 8);
            cryptic.IV = db.GetBytes(8);
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            cryptic.Dispose();
        }
        return cryptic;
    }

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

Двоичный поток '30' не содержит допустимого BinaryHeader. Возможные причины: неверное изменение потока или версии объекта между сериализацией и десериализацией.

Когда я оставляю криптографические методы, все работает нормально.

Ответы [ 3 ]

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

Итак,

Вы понимаете, что в этом коде

private static TripleDESCryptoServiceProvider GetCryptoProvider()
{
    TripleDESCryptoServiceProvider cryptic = new TripleDESCryptoServiceProvider();
    try
    {
        cryptic.Key = ASCIIEncoding.ASCII.GetBytes(CrypKey);

        Rfc2898DeriveBytes db = new Rfc2898DeriveBytes("sdddsdsd", 8);
        cryptic.IV = db.GetBytes(8);
    }
    catch (Exception)
    {
        throw;
    }
    finally
    {
        cryptic.Dispose(); // <------- Don't do this until you are done decrypting.
    }
    return cryptic;
}

вы ВСЕГДА избавитесь от провайдера, то есть вы всегда используете случайный ключ, а iv

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

Некоторые предыдущие сообщения, которые могут быть полезны:

Как зашифровать строку в vb.net с помощью RijndaelManaged и с использованием отступов PKCS5?

Применяет ли BinaryFormatter какое-либо сжатие?

В дальнейшем вы увидите, как я сложил сжатие с шифрованием с сериализацией.И это работает.

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

Вы рядом.Однако поток, который вы передаете в создание CryptoStream, является всегда, всегда, всегда буфером, в котором будет храниться ваш конечный результат.Это , а не поток, содержащий данные, которые вы хотите зашифровать или расшифровать.Я акцентировал внимание на этом, потому что помню, как впервые изучил это, и я сделал именно то, что делал ты.Итак, вот:

// this is for encryption
var memStreamEncryptedData = new MemoryStream();
var encryptStream = new CryptoStream(memStreamEncryptedData, 
   transform, CryptoStreamMode.Write);

// this is for decryption
var memStreamDecryptedData = new MemoryStream();
var decryptStream = new CryptoStream(memStreamDecryptedData, 
   transform, CryptoStreamMode.Write);

Обратите внимание, что в обоих случаях CryptoStream инициализируется с пустым потоком вывода.Ваш поток не входит в картину до позже.Итак, во время записи вы будете делать следующее:

encryptStream.Write(dataToBeEncrypted);
encryptStream.FlushFinalBlock();
encryptStream.Close();
// memStreamEncryptedData now safely holds your encrypted data

А во время чтения вы будете:

decryptStream.Write(dataToBeDecrypted);
encryptStream.FlushFinalBlock();
encryptStream.Close();
// memStreamDecryptedData now safely holds your decrypted data

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

public static byte[] Symmetric(bool encrypt, byte[] plaintext, string ikey)
{
    if (plaintext.Length == 0) return plaintext;

    // setting up the services can be very expensive, so I'll cache them
    // into a static dictionary.
    SymmetricSetup setup;
    if (!_dictSymmetricSetup.TryGetValue(ikey, out setup))
    {
        setup = new SymmetricSetup();
        setup.des = new DESCryptoServiceProvider { Mode = CipherMode.CBC, 
            Padding = PaddingMode.Zeros };
        setup.hash = Hash(Encoding.ASCII.GetBytes(ikey));
        setup.key = setup.hash.ForceLength(8, 0);
        setup.IV = Encoding.ASCII.GetBytes("init vec");
        setup.des.Key = setup.key;
        setup.des.IV = setup.IV;

        setup.encrypt = setup.des.CreateEncryptor(setup.des.Key, setup.des.IV);
        setup.decrypt = setup.des.CreateDecryptor(setup.des.Key, setup.des.IV);
        _dictSymmetricSetup[ikey] = setup;
    }

    var transform = encrypt ? setup.encrypt : setup.decrypt;

    var memStreamEncryptedData = new MemoryStream();

    var encStream = new CryptoStream(memStreamEncryptedData, transform, CryptoStreamMode.Write);

    if (encrypt)
        encStream.Write(new[] {(byte) ((8 - (plaintext.Length + 1)%8)%8)}, 0, 1);

    encStream.Write(plaintext, 0, plaintext.Length);
    encStream.FlushFinalBlock();
    encStream.Close();

    memStreamEncryptedData.Flush();

    var ciphertext = memStreamEncryptedData.ToArray();

    byte b;

    if (!encrypt)
        if (byte.TryParse("" + ciphertext[0], out b))
            ciphertext = ciphertext.Skip(1).Take(ciphertext.Length - b - 1).ToArray();

    return ciphertext;
}

И, если это так, вы можете сделать что-то вроде этого:

static public byte[] DecryptData(this byte[] source, string password) {
    return Symmetric(false, source, password);
}

static public byte[] EncryptData(this byte[] source, string password) {
    return Symmetric(true, source, password);
}

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

...