2014-10-27 17 views
7

Ich sehe ein Problem mit einem von mir gepflegten Code. Der folgende Code hat ein private static SHA1 Mitglied (das ist ein IDisposable, aber da es static ist, sollte es nie finalisiert werden). Doch dieser Code unter Stress löst eine Ausnahme aus, dass es geschlossen wurde schon sagt:Warum scheitert SHA1.ComputeHash unter hoher Last mit vielen Threads?

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) 

Der Code in Frage:

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)); 
    } 

Meine Frage ist offensichtlich, was dieses Problem verursachen könnte. Kann der Aufruf an SHA1.Create im Hintergrund fehlschlagen (wie viele kryptografische Ressourcen sind verfügbar)? Könnte das durch das Herunterfahren der Appdomain verursacht werden?

Irgendwelche anderen Theorien?

+0

Was hat das mit Entsorgen Sie zu tun? Auch welche "SHA1" -Klasse ist das? –

+1

Sind Sie sicher, dass die Klasse SHA1 threadsafe ist? Können Sie das Passwort abrufen, wenn es fehlschlägt? – Rob

+0

@John Saunders, Entschuldigung, Sie haben Recht. Dies hat nichts mit Dispose zu tun. Ich dachte, dass der Finalizer auf dem System.Security.Cryptography.SHA1CryptoServiceProvider irgendwie ausgelöst worden sein könnte. http://msdn.microsoft.com/en-us/library/e7hyyd4e(v=vs.110).aspx – MvdD

Antwort

23

Wie pro the documentation für die HashAlgorithm Basisklasse

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

Sie sollen nicht diese Klassen zwischen Threads gemeinsam nutzen, wo verschiedene Threads versuchen und ComputeHash auf derselben Instanz zur gleichen Zeit nennen.

BEARBEITEN Dies ist, was Ihren Fehler verursacht. Der folgende Stresstest führt zu einer Reihe von Fehlern, da mehrere Threads ComputeHash für dieselbe Hashalgorithmusinstanz aufrufen. Dein Fehler ist einer von ihnen.

  • System.Security.Cryptography.CryptographicException: Hash nicht gültig in bestimmten Zustand für die Verwendung

    Insbesondere habe ich die folgenden Fehler mit diesem Stresstest gesehen.

  • System.ObjectDisposedException: Sicherer Griff hat

Stresstest Codebeispiel geschlossen worden:

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

Bemerkenswertes Missgeschick, der sichere Griff sieht gut aus. Aber unbestreitbar, gute Antwort. –

+1

@HansPassant. Vielen Dank. Ich würde vermuten, dass es die "SHA1CryptoServiceProvider.Initialize" -Methode sein könnte, die anscheinend ein nicht threadsicheres "Dispose" macht und dann im '_safeHashHandle'-Feld neu erstellt. –

+0

Hehe, bizarro, Initialize() wird * nach * genannt, um den Hash zu berechnen. Muss eine Art Sicherheitssache sein. Du hast es. –