web-dev-qa-db-ja.com

.NETでファイルを暗号化し、元のファイルと同じファイルサイズにするにはどうすればよいですか?

私は.NETを使用しており、AES256 CBCを使用してファイルを暗号化および復号化する素晴らしいルーチン(xD)を作成しました。完全に機能しますが、暗号化されたファイルは復号化されたファイルと同じサイズである必要があると今彼らは私に言いました(他のAPIには問題がありますが、私のせいではありません。私は誓います。)

だから、私は見つけることができるすべてのものを試しましたが、何もうまくいきません。 .NET Frameworkには、必要なものとまったく同じに見えるCipherMode.CTSがありますが、残念ながら現在はサポートされていません。

パディングをなしに設定しようとしましたが、ブロックサイズが想定よりも小さいため、もちろんエラーが発生します。

アイデア?

編集:私は2つの方法でこの問題を解決することができました:


.NET APIを使用する場合SymmetricAlgorithm alg = new RijndaelManaged(); alg.Mode = CipherMode.CFB; alg.Padding = PaddingMode.None; alg.FeedbackSize = 8;


BouncyCastle APIのIBufferedCipher暗号= new CtsBlockCipher(new CbcBlockCipher(new AesFastEngine()));を使用します。

これが同じ問題を持つ他の人を助けることができることを願っています:)

10
Fabio

CTSを使用する場合でも、必要なのは 初期化ベクトル(IV) である必要があります(私は主張します[〜#〜]必須[〜#〜])暗号学的に強力な疑似乱数ジェネレータを使用して、ファイルごとに新しく生成されます。そのため、サイズを大きくせずにすべてに対応することはできません。 「通常の」ブロック暗号を使用している限り、これは避けられません。

また、暗号化が必要な場合は、おそらく整合性、つまり [〜#〜] mac [〜#〜] が必要です。これは、本質的に少し余分なスペースを必要とします。

あなたが試すことができるのは、暗号化する前にファイルを圧縮することです(System.IO.Compression.DeflateStream);これにより、必要な追加の呼吸スペースが得られます(で保証されていない方法ですが、特に暗号化する必要があるファイルがXMLファイルなど、多くの構造を持つファイル)。

10
Thomas Pornin

Thomasが書いたように、ファイルを安全に暗号化するには、暗号化されたファイルに加えてIVが必要です。 IVをセキュリティで保護するには、次の2つの条件を満たす必要があります。

  1. 同じIVとキーのペアは、2つの異なるメッセージには使用されません。
  2. 攻撃者は、自分が選択したメッセージに使用されているIVを、攻撃者が解読しようとしているメッセージと同じキーで暗号化して予測することはできません。

通常、これらの2つの条件は、メッセージごとに異なるランダムIVを使用することで満たされます。この場合、IVを暗号化ファイルに追加する必要があるため、ファイルサイズが大きくなります。ただし、特定の条件に該当する場合は、ファイルのプロパティ(パス、名前、日付と時刻)をIVとして使用できます(暗号化されたファイルにIVを追加する必要がないようにするため)。条件は次のとおりです。

  1. 暗号化キーはマシンごとに一意です。これにより、同じファイルプロパティを持つ同じマシン上に2つのファイルを置くことができないため、同じ暗号化キーで同じIVが使用されることはありません。
  2. 暗号化キーはユーザーごとに一意です。これにより、キーが異なるため、攻撃者が 選択された平文攻撃 をマウントして他のユーザーのファイルを復号化できないようになります。

これら2つの条件が満たされている場合は、ストリーム暗号モードの操作(CTR、OFB、CFBなど)を使用し、ファイルプロパティをIVとして使用できます。

5

いくつかのオプションがあります:

  1. TripleDESCryptoServiceProviderがCTSをサポートしているかどうかを確認します。
  2. CBCモードのAESをストリーム暗号に変換します。

後者の場合は、ゼロのブロックとxorのプレーンテキストを結果の暗号ストリームで暗号化するだけです。

// work out what the padding length needs to be
int paddingSize = cipher.BlockSize - (message.Length % cipher.BlockSize);
int paddedSize = message.Length + paddingSize;

// build a block of zeros
byte[] zeros = new byte[paddedSize];

// create a keystream from the key and IV, using the zeros as a plaintext
byte[] keystream = EncryptAES(zeros, key, iv);

// produce the ciphertext by xoring the plaintext and the keystream
byte[] ciphertext = new byte[message.Length];
for (int i = 0; i < message.Length; i++)
    ciphertext[i] = message[i] ^ keystream[i];

これは、暗号化アルゴリズムと復号化アルゴリズムの両方として機能します。同じキーとIVを使用して暗号テキストをフィードバックするだけで、元のプレーンテキストを取得できます。ただし、Thomas Porninが指摘するように、メッセージごとに一意のIVを送信する必要があります。

2
Polynomial