61572


お世話になります。  
C#のBinaryFormatterでのシリアル化についての質問です。  
[前回の質問](https://ja.stackoverflow.com/questions/61543/c%e3%81%aedeflatestream%e3%81%ab%e3%81%a4%e3%81%84%e3%81%a6)を参考に暗号化したバイナリ配列をDeflate圧縮させることはできたのですが、巨大なクラスをシリアル化しようとすると、OutOfMemoryExceptionが発生してしまいます。  
あまりきれいではないかつ長いですが、ソースコードを貼り付けますので、どのあたりを直したらよいか、アドバイスをいただけますと幸いです。

[code]

    //パスワードをあらかじめ指定しておく。
    private string encrypt_key = "testpassword";
    
    public save(){
    //とりあえず、クラスをバイト型配列に変換しておく。
    //今回はtest_dataに対象のデータが入っているものとする。
    byte[] bytes;
    using (MemoryStream ms = new MemoryStream()){
    BinaryFormatter formatter = new     BinaryFormatter();
    formatter.Serialize(ms, test_data);
    bytes = ms.ToArray();
    }
    //できたデータを暗号化する。
    bytes = Encrypt(bytes);
    //あとはDeflateで圧縮して、ファイルに書き込む。
    using (Stream stream = new FileStream("test_data.bin", FileMode.Create, FileAccess.Write)){
    using (DeflateStream ds = new DeflateStream(stream, CompressionLevel.Optimal)){
    ds.Write(bytes, 0, bytes.Length);
    }
    }
    bytes = null;
    }
    
    private byte[] Encrypt(byte[] bytes){
    //byte[] bytes;
    SymmetricAlgorithm algorithm = new AesManaged();
    algorithm.BlockSize = 128;
    algorithm.KeySize = 128;                // KeySize = 16bytes
    algorithm.Mode = CipherMode.CBC;        // CBC mode
    algorithm.Padding = PaddingMode.PKCS7;    // Padding mode is "PKCS7".
    
    //ソルト、キーおよびIVを生成。
    Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(encrypt_key, 16);
    byte[] salt = new byte[16];
    salt = deriveBytes.Salt;
    byte[] key = deriveBytes.GetBytes(16);
    algorithm.Key = key;
    algorithm.GenerateIV();
    using (MemoryStream ms = new MemoryStream()){
    //復号化の際に利用できるよう、先ほど生成したソルトとIVを書き込む。
    ms.Write(salt, 0, 16);
    ms.Write(algorithm.IV, 0, 16);
    //暗号化を行う。
    using (CryptoStream cs = new CryptoStream(ms, algorithm.CreateEncryptor(algorithm.Key, algorithm.IV), CryptoStreamMode.Write)){
    cs.Write(bytes, 0, bytes.Length);
    }
    //MemoryStreamの内容をバイト型配列に変換。
    bytes = ms.ToArray();
    }
    //暗号化結果を返す。
    return bytes;
    }
    
[/code]

一応エラー内容も貼り付けておきます。

[code]

    種類 'System.OutOfMemoryException' の例外がスローされました。
       場所 System.IO.MemoryStream.set_Capacity(Int32 value)
       場所 System.IO.MemoryStream.EnsureCapacity(Int32 value)
       場所 System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
       場所 System.Security.Cryptography.CryptoStream.Write(Byte[] buffer, Int32 offset, Int32 count)
       場所 Encrypt(Byte[] bytes)
       場所 save()
    
[/code]

以上、よろしくお願いいたします。


CC BY-SA 4.0
2019-12-18T23:42:19.077
61559
2019-12-19T06:32:27.030
2019-12-19T02:59:00.750
29034
29034
question
c#
BinaryFormatterでクラスのシリアライズ時に暗号化とDeflate圧縮をしたい


`FileStream`も`DeflateStream`も`CryptoStream`、そして`MemoryStream`もすべて`Stream`で統一されたインターフェース（正確には基本クラス）が用意されています。  
`MemoryStream`で一旦バイト配列を経由するのではなく、必要な相手に直接書き込んではどうでしょうか？

[code]

    using (var fs = new FileStream("test_data.bin", FileMode.Create, FileAccess.Write))
    using (var ds = new DeflateStream(fs, CompressionLevel.Optimal))
    using (var deriveBytes = new Rfc2898DeriveBytes(encrypt_key, 16))
    using (var aes = Aes.Create()) {
        aes.BlockSize = 128;
        aes.KeySize = 128;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        aes.Key = deriveBytes.GetBytes(16);
        aes.GenerateIV();
        ds.Write(deriveBytes.Salt, 0, 16);
        ds.Write(aes.IV, 0, 16);
        using (var encryptor = aes.CreateEncryptor())
        using (var cs = new CryptoStream(ds, encryptor, CryptoStreamMode.Write))
            cs.Write(bytes, 0, bytes.Length);
    }
    
[/code]


CC BY-SA 4.0
2019-12-19T06:32:27.030
61572
2019-12-19T06:32:27.030
4236
61559
answer