BinaryFormatter&反序列化时出现CryptoStream问题

时间:2011-08-11 13:27:34

标签: c# .net file-io encryption-symmetric

我在这里有点绝望。我正在尝试将带有序列化对象的加密文件写入磁盘,然后检索文件,解密并反序列化该对象。

更新: 我重构了这个代码:

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);
            }
        }

现在我在反序列化时收到加密异常:错误数据

ORIGINAL:

我这样做:

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;
    }

加密和写入工作正常,文件出现在磁盘上,内容就在那里(当然是加密的)。但是当我调用retrieve方法时,我总是得到一个SerializationException

  

二进制流'30'不包含有效的BinaryHeader。可能的原因是序列化和反序列化之间的无效流或对象版本更改。

当我离开加密方法时,一切正常。

3 个答案:

答案 0 :(得分:1)

所以,

你在这段代码中意识到了

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

答案 1 :(得分:0)

你很亲密。但是,传递给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,它将是您需要输入序列化器的任何流。

答案 2 :(得分:0)

以前的一些帖子可以使用:

How do I encrypt a string in vb.net using RijndaelManaged, and using PKCS5 padding?

Does BinaryFormatter apply any compression?

稍后,您可以看到我如何通过序列化加密压缩。它有效。