CNG,CryptoServiceProvider和HashAlgorithm的托管实现

所以我想知道哈希算法的各种实现之间是否存在任何重大差异,例如以SHA系列算法为例。 它们每个都有3个实现,1个托管代码和2个围绕不同本机加密API的包装器,但使用它们之间有什么主要区别吗? 我可以想象包装器版本可以具有更高的性能,因为它在本机代码中执行,但是他们都需要执行完全相同的计算,从而提供相同的输出,即嘿是可互换的。 它是否正确?

例如,SHA512CNG无法用于XP SP2(文档错误),但SHA512MANAGED可以。


@Maxim – 谢谢,但不是我要求的。 我问的是,使用给定哈希算法的Managed / CryptoServiceProvider / CNG实现,除了可能的性能之外是否存在任何差异。 使用.NET 3.5,您可以获得具有三种实现的所有哈希算法,因此

SHA512Managed SHA512CryptoServiceProvider SHA512Cng

后两者是本机API的包装器。 例如,对于所有SHAxxx实现都是如此。

一个区别是本机版本(至少其中一些)是FIPS认证的(即,由美国政府批准),而管理版本则不是。 如果您的代码恰好在已配置为“仅FIPS”的Windows计算机上运行,​​则尝试使用托管版本将失败。

大多数Windows机器都没有以这种方式配置,但如果您部署到政府或面向国防(或其他高度安全)的环境,您可能会遇到这种情况。

请参见http://blogs.msdn.com/shawnfa/archive/2005/05/16/417975.aspx 。

Cng版本应该更快一点,但我刚写了一个比较每个版本速度的小程序。 (我有一个客户询问MD5与SHA1的性能特征)

我很惊讶地发现MD5和SHA1之间几乎没有区别,但也很惊讶Cng和CryptoServiceProvider之间存在细微差别。

源代码非常直接,我添加了代表多次执行相同的迭代,所以我可以在其中一次运行期间在我的机器上进行平均以防万一。

通过这样的调用调用以下内容:

CalculateHash(1, 1024, new SHA1CryptoServiceProvider()); static long CalculateHash(UInt64 repetitions, UInt64 size, HashAlgorithm engine) { RandomNumberGenerator rng = RandomNumberGenerator.Create(); byte[][] goo = new byte[repetitions][]; for (UInt64 i = 0; i < repetitions; i++) { goo[i] = new byte[size]; rng.GetBytes(goo[i]); } DateTime start = DateTime.Now; for (UInt64 i = 0; i < repetitions; i++) { engine.ComputeHash(goo[i]); } return DateTime.Now.Subtract(start).Ticks; } 

我在一个越来越大的循环中运行它,以确定在使用大或小输入时是否有人倒下。 这是循环,数据如下(我的计算机在2 ^ 28时用完了ram):

 int loops = 32; UInt64 reps = 1; int width = 20; Console.WriteLine("Loop#".PadRight(6) + "MD5".PadRight(width) + "SHA1".PadRight(width) + "SHA1Cng".PadRight(width) + "SHA256".PadRight(width) + "SHA256Cng".PadRight(width)); for (int i = 0; i < loops; i++) { UInt64 size = (UInt64)Math.Pow((double)2, (double)i); Console.WriteLine((i + 1).ToString().PadRight(6) + CalculateHash(reps, size, new MD5CryptoServiceProvider()).ToString().PadRight(width) + CalculateHash(reps, size, new SHA1CryptoServiceProvider()).ToString().PadRight(width) + CalculateHash(reps, size, new SHA1Cng() ).ToString().PadRight(width) + CalculateHash(reps, size, new SHA256CryptoServiceProvider()).ToString().PadRight(width) + CalculateHash(reps, size, new SHA256Cng()).ToString().PadRight(width)); } Loop# MD5 SHA1 SHA1Cng SHA256 SHA256Cng 1 50210 0 0 0 0 2 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 0 5 0 0 0 0 0 6 0 0 0 0 0 7 0 0 0 0 0 8 0 0 0 0 0 9 0 0 0 0 0 10 0 0 10042 0 0 11 0 0 0 0 0 12 0 0 0 0 0 13 0 0 0 0 0 14 0 0 0 0 0 15 10042 0 0 10042 10042 16 10042 0 0 0 0 17 0 0 0 10042 10042 18 0 10042 10042 20084 10042 19 0 10042 10042 30126 40168 20 20084 20084 20084 70294 70294 21 30126 40168 40168 140588 140588 22 60252 70294 80336 291218 281176 23 120504 140588 180756 572394 612562 24 241008 281176 361512 1144788 1215082 25 482016 572394 723024 2289576 2420122 26 953990 1134746 1456090 4538984 4830202 27 1907980 2259450 2982474 9118136 9660404 28 3805918 4508858 5804276 18336692 19581900 

我在CNG和SHA512上进行了快速而肮脏的比较,这是使用下面代码的所有SHA算法AFAIK中最慢的。

  static void Main(string[] args) { int loops = 10000000; var data = Encoding.ASCII.GetBytes("123"); var hashLoop = new Action((HashAlgorithm ha) => { for (int i = 0; i < loops; i++) ha.ComputeHash(data); }); var t1 = Task.Factory.StartNew(() => { Time(hashLoop, new SHA512Managed()); }); var t2 = Task.Factory.StartNew(() => { Time(hashLoop, new SHA512Cng()); }); Task.WaitAll(t1, t2); Console.WriteLine("Benchmark done!"); Console.ReadKey(); } static void Time(Action action, HashAlgorithm ha) { var sw = new Stopwatch(); sw.Start(); action(ha); sw.Stop(); Console.WriteLine("{1} done in {0}ms", sw.ElapsedMilliseconds, ha.ToString()); } 

经过几次运行,我发现不同之处在于CNG比算法的托管版本要快得多,大约21.7% to 49.5%

Managed和CNG版本之间的另一个区别是支持的.Net Framework版本:例如

  • AES管理版本从3.5开始,而CNG从4.6.2开始
  • SHA512, Managed从1.1开始, Cng从3.5开始。

但是,我相信如果我们不受框架版本的限制或支持传统操作系统版本,我们应该使用CNG版本

  • 后缀为Cng的散列算法是唯一使用bcrypt的算法
  • 它可能需要更长时间的事实实际上是一个优势,因为它可以防止暴力攻击:在用户端300ms或3ms没有区别,而对于攻击者来说它是100级的命令!