2014-09-17 31 views
6

Sono completamente nuovo alla crittografia, ma sto imparando. Ho messo insieme molti suggerimenti diversi dalla mia ricerca online, e ho creato la mia classe per gestire l'hash, il sale, lo stretching chiave e il confronto/conversione dei dati associati.Come utilizzare SHA-512 con Rfc2898DeriveBytes nel mio codice salina e hash?

Dopo aver ricercato la libreria .NET integrata per la crittografia, ho scoperto che quello che ho è ancora solo SHA-1. Ma sto arrivando alla conclusione che non è male visto che sto usando più iterazioni del processo di hash. È corretto?

Ma se volessi iniziare con il più robusto SHA-512, come potrei implementarlo nel mio codice qui sotto? Grazie in anticipo.

using System; 
using System.Runtime.InteropServices; 
using System.Security; 
using System.Security.Cryptography; 

public class CryptoSaltAndHash 
{ 
    private string strHash; 
    private string strSalt; 
    public const int SaltSizeInBytes = 128; 
    public const int HashSizeInBytes = 1024; 
    public const int Iterations = 3000; 

    public string Hash { get { return strHash; } } 
    public string Salt { get { return strSalt; } } 

    public CryptoSaltAndHash(SecureString ThisPassword) 
    { 
     byte[] bytesSalt = new byte[SaltSizeInBytes]; 
     using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider()) 
     { 
      crypto.GetBytes(bytesSalt); 
     } 
     strSalt = Convert.ToBase64String(bytesSalt); 
     strHash = ComputeHash(strSalt, ThisPassword); 
    } 

    public static string ComputeHash(string ThisSalt, SecureString ThisPassword) 
    { 
     byte[] bytesSalt = Convert.FromBase64String(ThisSalt); 
     Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(
      convertSecureStringToString(ThisPassword), bytesSalt, Iterations); 
     using (pbkdf2) 
     { 
      return Convert.ToBase64String(pbkdf2.GetBytes(HashSizeInBytes)); 
     } 
    } 

    public static bool Verify(string ThisSalt, string ThisHash, SecureString ThisPassword) 
    { 
     if (slowEquals(getBytes(ThisHash), getBytes(ComputeHash(ThisSalt, ThisPassword)))) 
     { 
      return true; 
     } 
     return false; 
    } 

    private static string convertSecureStringToString(SecureString MySecureString) 
    { 
     IntPtr ptr = IntPtr.Zero; 
     try 
     { 
      ptr = Marshal.SecureStringToGlobalAllocUnicode(MySecureString); 
      return Marshal.PtrToStringUni(ptr); 
     } 
     finally 
     { 
      Marshal.ZeroFreeGlobalAllocUnicode(ptr); 
     } 
    } 

    private static bool slowEquals(byte[] A, byte[] B) 
    { 
     int intDiff = A.Length^B.Length; 
     for (int i = 0; i < A.Length && i < B.Length; i++) 
     { 
      intDiff |= A[i]^B[i]; 
     } 
     return intDiff == 0; 
    } 

    private static byte[] getBytes(string MyString) 
    { 
     byte[] b = new byte[MyString.Length * sizeof(char)]; 
     System.Buffer.BlockCopy(MyString.ToCharArray(), 0, b, 0, b.Length); 
     return b; 
    } 
} 

Note: ho fatto riferimento un sacco di pratiche da https://crackstation.net/hashing-security.htm. Il metodo di confronto slowEquals consiste nel normalizzare i tempi di esecuzione prevenendo la ramificazione. L'uso di SecureString consiste nel disporre di una forma crittografata della password che passa tra questa classe e altre classi e pagine all'interno della mia applicazione web. Mentre questo sito sarà su HTTPS, è sempre bello fare il possibile per assicurarsi che le cose siano il più sicure possibili pur rimanendo nei limiti della ragionevolezza.

Nel mio codice, ho impostato la stringa della chiave su 128 byte (anche se a volte diventa più grande, che va bene), la dimensione dell'hash su 1 KB e il numero di iterazioni su 3.000. È un po 'più grande del tipico sale da 64 byte, hash di 512 byte e 1.000 o 2.000 iterazioni, ma anche in questo caso la velocità di accesso e le prestazioni delle app sono una priorità estremamente bassa.

Pensieri?

+0

Domanda simile sulla revisione del codice: [Secure password hashing] (http://codereview.stackexchange.com/questions/32856/secure-password-hashing) – CodesInChaos

risposta

5

Rispondere alla domanda: scaricare gli esempi di codice libero dal libro "SecurityDriven.NET". Trova la classe PBKDF2 che prende uno stabilimento HMAC. HMACSHA512 in fabbrica è disponibile, tra gli altri.

Dato che sei nuovo della crittografia, ti consiglio vivamente di leggere il libro (ad esempio per comprendere appieno i punti fatti da CodesInChaos).

+0

Grazie. Mentre quegli esempi usano .NET 4.5, sono su .NET 4.0. Tuttavia dovrebbero funzionare per entrambi con modifiche minime. Questi esempi fanno riferimento alla classe di crittografia di cui avevo bisogno: http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha512%28v=vs.100%29.aspx –

8
  1. 3000 iterazioni è piuttosto basso. Anche 10000 è basso. Ma è necessario ponderare il guadagno di sicurezza di ulteriori iterazioni contro il rischio che un utente malintenzionato esegua il proprio server tentando di accedere spesso, il che innesca un hash costoso per ogni tentativo.
  2. Non c'è punto in un sale più grande di 128 bit/16 byte. Un sale dovrebbe essere unico, niente di più.
  3. Una dimensione hash maggiore della dimensione nativa (20 byte per SHA-1) riduce le prestazioni per il difensore ma non per l'attaccante. Poiché ciò significa che puoi permetterti un minor numero di iterazioni, in realtà indebolisce la sicurezza.

    Ad esempio allo stesso costo dell'hash da 1024 byte con iterazioni da 3000, è possibile acquistare un hash da 20 byte con 156000 iterazioni, che è 52 volte più costoso da decifrare.

  4. Per utilizzare SHA-2 è necessaria un'implementazione PBKDF2 completamente diversa, quella inclusa in .net è codificata per l'uso di SHA-1.

    Se si prende la briga di utilizzare una libreria di terze parti, preferisco usare una libreria bcrypt poiché è molto più forte contro gli aggressori basati su GPU.

  5. L'API è scomoda da utilizzare, poiché si spinge Salt Management sul chiamante invece di gestirlo all'interno delle funzioni Create/Verify.

  6. È stupido usare SecureString e quindi convertirlo in String. Questo contrasta l'intero punto di utilizzo di un SecureString in primo luogo.

    Personalmente non mi dilungherò con SecureString in un'applicazione tipica.Vale la pena solo se lo abbini a una revisione completa della sicurezza a livello intero che verifica che la password non venga mai memorizzata in un String e che venga sempre cancellata dallo spazio di archiviazione mutevole quando non è più necessaria.

  7. Non memorizzare le password/sali nelle variabili di istanza. Basta tenerli locali per le funzioni pertinenti. Memorizzerei solo la configurazione nelle variabili di istanza (come il conteggio delle iterazioni).

  8. Mentre SHA-1 è indebolito crittograficamente, gli attacchi producono collisioni. Per le collisioni con hash delle password non pertinenti, ciò che ti interessa sono i primi attacchi pre-immagine. SHA-1 è ancora abbastanza forte in questo senso.

    Il vantaggio principale di SHA-512 non è che è crittograficamente più forte (anche se lo è), è che l'aritmetica a 64 bit costa l'attaccante più del difensore, dal momento che il defender utilizzerà probabilmente una CPU Intel a 64 bit che offre velocemente Aritmetica a 64 bit.

+0

Consiglio eccellente, grazie mille! Mi ricorderò di usare solo SecureString se non lo converto mai in una stringa di codice. Per quanto la mia API sia scomoda da usare, sembra funzionare bene. Istanziare la classe per creare un salt e ha, o chiamarlo staticamente per verificare un hash creato in precedenza. Nessuna gestione ha spinto il chiamante lì. Comunque la mia domanda ruota attorno al tuo punto # 4. Sareste in grado di elaborare di più su tale suggerimento e su come sarebbe implementato in un ambiente .NET (ad esempio: scegliere una libreria SHA-2 che si consiglia). Grazie ancora! –

+0

Non mi sono mai preoccupato di esaminare le implementazioni in C# di PBKDF2-HMAC-SHA-2, quindi non lo so davvero. Immagino che il bouncycastle lo sopporti. Personalmente preferirei valutare bcrypt.net e altre implementazioni di bcrypt. – CodesInChaos