2014-10-27 72 views
7

我看到我维护的一些代码存在问题。下面的代码有一个private static SHA1成员(这是一个IDisposable,但因为它是static,它永远不会被最终确定)。然而,在压力下这段代码抛出表明它已经关闭了异常:为什么SHA1.ComputeHash在高负载下有很多线程会失败?

Caught exception. Safe handle has been closed" 
Stack trace: Call stack where exception was thrown 
at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success) 
at System.Security.Cryptography.Utils.HashData(SafeHashHandle hHash, Byte[] data, Int32 cbData, Int32 ibStart, Int32 cbSize) 
at System.Security.Cryptography.Utils.HashData(SafeHashHandle hHash, Byte[] data, Int32 ibStart, Int32 cbSize) 
at System.Security.Cryptography.HashAlgorithm.ComputeHash(Byte[] buffer) 

的代码中的问题是:

internal class TokenCache 
{ 
    private static SHA1 _sha1 = SHA1.Create(); 

    private string ComputeHash(string password) 
    { 
     byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(password); 
     return UTF8Encoding.UTF8.GetString(_sha1.ComputeHash(passwordBytes)); 
    } 

我的问题是明显,是什么引发了这个问题。对SHA1.Create的调用是否可以无提示失败(有多少加密资源可用)?这可能是由应用程序域名下降造成的吗?

其他理论?

+0

这是什么都与处置呢?另外,哪个“SHA1”类是那个? – 2014-10-27 16:51:27

+1

你确定类SHA1是线程安全吗?当它失败时你能够获取被哈希处理的密码吗? – Rob 2014-10-27 16:51:56

+0

@约翰桑德斯,对不起,你是对的。这与Dispose无关。我认为System.Security.Cryptography.SHA1CryptoServiceProvider上的终结器可能以某种方式被触发。 http://msdn.microsoft.com/en-us/library/e7hyyd4e(v=vs.110).aspx – MvdD 2014-10-27 16:53:50

回答

23

按照the documentationHashAlgorithm基类

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

你不应该在那里的线程不同的线程试图在同一时间在同一个实例调用ComputeHash之间共享这些类。

编辑 这是什么导致你的错误。下面的压力测试会由于多个线程在同一个哈希算法实例中调用ComputeHash而产生各种错误。你的错误就是其中之一。

具体而言,我已经看到了这种应力测试如下错误:

  • System.Security.Cryptography.CryptographicException:哈希不适于在指定状态下使用。
  • System.ObjectDisposedException:安全句柄已关闭

压力测试的代码示例:

const int threadCount = 2; 
var sha1 = SHA1.Create(); 
var b = new Barrier(threadCount); 
Action start =() => { 
        b.SignalAndWait(); 
        for (int i = 0; i < 10000; i++) 
        { 
         var pwd = Guid.NewGuid().ToString(); 
         var bytes = Encoding.UTF8.GetBytes(pwd); 
         sha1.ComputeHash(bytes); 
        } 
       }; 
var threads = Enumerable.Range(0, threadCount) 
         .Select(_ => new ThreadStart(start)) 
         .Select(x => new Thread(x)) 
         .ToList(); 
foreach (var t in threads) t.Start(); 
foreach (var t in threads) t.Join(); 
+0

显着的事故,安全处理看起来不错。但不可否认,很好的答案。 – 2014-10-27 17:25:55

+1

@HansPassant。谢谢。我猜想它可能是'SHA1CryptoServiceProvider.Initialize'方法,它似乎做了一个非线程安全的'Dispose',然后在'_safeHashHandle'字段上重新创建。 – 2014-10-27 17:33:46

+0

hehe,bizarro,Initialize()在*计算散列之后调用*。必须是某种安全的东西。你说对了。 – 2014-10-27 17:42:44