web-dev-qa-db-ja.com

対称暗号化(AES):暗号化されたデータと一緒にIVとSaltを保存することは安全で適切ですか?

対称暗号化アルゴリズム(この場合はAES)を使用してデータを暗号化および復号化するときに、初期化ベクトルとソルト(該当する場合)を処理および管理する方法を理解しようとしています。

さまざまなSOスレッドやその他のさまざまなWebサイトから、IVもソルトも秘密にする必要はなく、ブルートフォース攻撃などの暗号解読攻撃から防御するためにのみ一意であると推測しました。これにより、疑似ランダムIVを暗号化されたデータと一緒に保存することが実行可能であると考え、使用している方法が適切かどうか、さらに、現在ハードコードされているソルトを同じように処理する必要があるかどうかを尋ねています。 IVと一緒にメモリストリームに書き込む

私のコード:

private const ushort ITERATIONS = 300;
private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22,  0x3c };

private static byte[] CreateKey(string password, int keySize)
{
    DeriveBytes derivedKey = new Rfc2898DeriveBytes(password, SALT, ITERATIONS);
    return derivedKey.GetBytes(keySize >> 3);
}

public static byte[] Encrypt(byte[] data, string password)
{
    byte[] encryptedData = null;
    using (AesCryptoServiceProvider provider = new AesCryptoServiceProvider())
    {
        provider.GenerateIV();
        provider.Key = CreateKey(password, provider.KeySize);
        provider.Mode = CipherMode.CBC;
        provider.Padding = PaddingMode.PKCS7;

        using (MemoryStream memStream = new MemoryStream(data.Length))
        {
            memStream.Write(provider.IV, 0, 16);
            using (ICryptoTransform encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
            {
                using (CryptoStream cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
                {
                    cryptoStream.Write(data, 0, data.Length);
                    cryptoStream.FlushFinalBlock();
                }
            }
            encryptedData = memStream.ToArray();
        }
    }
    return encryptedData;
}

public static byte[] Decrypt(byte[] data, string password)
{
    byte[] decryptedData = new byte[data.Length];
    using (AesCryptoServiceProvider provider = new AesCryptoServiceProvider())
    {
        provider.Key = CreateKey(password, provider.KeySize);
        provider.Mode = CipherMode.CBC;
        provider.Padding = PaddingMode.PKCS7;
        using (MemoryStream memStream = new MemoryStream(data))
        {
            byte[] iv = new byte[16];
            memStream.Read(iv, 0, 16);
            using (ICryptoTransform decryptor = provider.CreateDecryptor(provider.Key, iv))
            {
                using (CryptoStream cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Read))
                {
                    cryptoStream.Read(decryptedData, 0, decryptedData.Length);
                }
            }
        }
    }
    return decryptedData;
}

また、適切な方法に関する対称暗号化に関するその他の情報も受け付けています。

22
Caster Troy

IVとSaltを暗号文と一緒に保存することは適切であり、ベストプラクティスです。ソルトをハードコーディングすることは役に立ちません。ランダムであることは重要です。反復をハードコーディングすることは完全に問題ありませんが、通常は300よりはるかに高くなります(実際には少なくとも1000であり、マシン/使用法が10秒のように処理できる場合は通常はるかに高くなります数千)。

スタックオーバーフローのカットアンドペーストからオープンソースコードへのc#暗号化の悪い(または古い)例をたくさん見たので、カットアンドペースト暗号化コードを少し書きました の対称認証付き暗号化の最新の例string。 私は最新の状態に保ち、レビューしようとします。 ivとsaltを暗号文とともに保存し、暗号文と暗号文に含まれる値を認証します。

理想的には、ivのようなベストプラクティスを処理する高レベルの暗号化ライブラリを使用することをお勧めしますが、それらは通常、csharpには存在しません。私はグーグルの keyczar ライブラリのネイティブ csharpバージョン に取り組んできました。機能的には使用できる状態になっていますが、最初の公式の安定版リリースの前に、コードにもっと目を向けたいと思っていました。

26
jbtule

はい、IVと塩の両方が公的な価値です。さらに重要なのは、これらが各暗号化操作のランダムな値であることを確認することです。

実例を示すために、 rncryptorデータ形式 を見てください。ここで、saltとIVは、暗号文とMAC値とともにデータ形式にパッケージ化されています。 (注:これはObjective-cの例です)。

13
Duncan Jones