二进制格式化程序反序列化时CryptoStream问题

发布于 2024-11-29 05:37:01 字数 4040 浏览 1 评论 0原文

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

更新: 我将代码重构为:

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。可能的原因是序列化和反序列化之间无效的流或对象版本更改。

当我将加密方法排除在外时,一切正常。

I'm getting a bit desperate here. I'm trying to write an encrypted file with a serialized object to disk and later retrieve the file, decrypt it and deserialize the object back.

UPDATE:
I refactored the code to this:

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

Now I'm getting a cryptographic exception on deserialize: Bad Data

ORIGINAL:

I'm doing this:

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

Encrypting and writing works fine, the file appears on the disk and the content is there (encrypted of course). But when I call the retrieve method I always get a SerializationException

Binary stream '30' does not contain a valid BinaryHeader. Possible causes are invalid stream or object version change between serialization and deserialization.

When I leave the cryptographic methods out everything works fine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

听,心雨的声音 2024-12-06 05:37:01

所以,

您意识到在这段代码中

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

So,

You realize that in this code

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

you will ALWAYS dispose of the provider meaning you are always using a random key and iv

青春有你 2024-12-06 05:37:01

你很接近。但是,您传递到 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。

You are close. However, the stream you pass into the creation of CryptoStream is always, always, always the buffer that will hold your end result. It is not the stream that holds the data you want to encrypt or decrypt. I put the emphasis in there because I remember learning this for the first time and I did exactly what you were doing. So here:

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

Notice in both cases, CryptoStream is being initialized with a blank output stream. Your stream does not enter into the picture until later. So, during a write, you will do the following:

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

And during the read, you will:

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

So, to save you some trouble, here's a nice simple Symmetric method that will perform both encryption and decryption. The only difference between this and yours is that I am working directly on byte arrays, but perhaps that augmentation can be an exercise:

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

And to call it, you might do something like this:

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

Again, you'll do something slightly different to work with streams, but hopefully you get the gist. Instead of MemoryStream, it will be whatever stream you need to feed into your serializer.

山人契 2024-12-06 05:37:01

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

如何使用 RijndaelManaged 和 PKCS5 加密 vb.net 中的字符串填充?

BinaryFormatter是否应用任何压缩?

稍后,您可以看到我如何将压缩、加密和序列化叠加起来。它有效。

Some previous posts that can be of use:

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

Does BinaryFormatter apply any compression?

In later, you can see how I stacked compression with encryption with serialization. And it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文