ではRfc2898DeriveBytesクラスを利用してSaltと暗号鍵を生成しています。
Rfc2898DeriveBytes (string password, byte[] salt);
saltを指定すると同じ暗号鍵をつくります。また
Rfc2898DeriveBytes (string password, int saltSize);
// saltSizeは8以上を指定すること
このようにサイズを指定するとランダムsaltを生成します。
さて、本当にランダムなのでしょうか? 何回も同じことをくり替えていると同じsaltが生成されるということはないのでしょうか?
1 2 3 4 5 6 7 8 9 10 11 12 |
int len = 1 * 10000; List<string> ret = new List<string>(); for (int i = 0; i < len; i++) { Rfc2898DeriveBytes o1 = new Rfc2898DeriveBytes("abc", 10); ret.Add(Convert.ToBase64String(o1.Salt)); if (i % 100 == 0) progressBar1.Value++; } int count1 = ret.Count; int count2 = ret.Distinct().Count(); MessageBox.Show((count1-count2).ToString()); |
このようにすると生成されたソルトを集めて重複している数を知ることができます。
lenの値を大きくしても重複することはないようです。
それもそのはずsaltSize=8を指定しても生成されるsaltは256の8乗あります。256の8乗を計算すると18,446,744,073,709,551,616。約1844京もある巨大な数です。1000万回や1億回繰り返したところでダブることはまずありません。
また生成される暗号鍵は256bitの場合、2の256乗であり、約1.15×10の77乗です。異なるsaltやパスワードから同じ暗号鍵が生成される可能性もほとんどありません。
RandomクラスのNextBytesメソッドを使えば同じようなことができますが、暗号強度が高いランダムな数値を生成するのには適してしません。ためしに以下のような処理をさせてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Random r = new Random(); int len = 10000 * 500; int size = 5; List<string> ret = new List<string>(); for (int i = 0; i < len; i++) { byte[] vs = new byte[size]; r.NextBytes(vs); ret.Add(Convert.ToBase64String(vs)); } int count1 = ret.Count; int count2 = ret.Distinct().Count(); MessageBox.Show((count1 - count2).ToString()); |
sizeに8以上の値を設定したときは基本的に重複はしません。しかし小さな値を設定すると重複しやすくなります。
ループ10万回
4バイト以上 重複 0
3バイト 重複 約300
ループ100万回
5バイト以上 重複 0
4バイト 重複 約100
ループ300万回
6バイト以上 重複 0
5バイト 重複 約10
このような結果になりました。変なことはしないでRfc2898DeriveBytes クラスのようなものを使ったほうが無難です。