2010-07-09 12 views
13

Mi sto cazzando intorno all'API Bouncy Castle di C# per trovare come eseguire una derivazione della chiave PBKDF2.PBKDF2 nel castello gonfiabile C#

Sono davvero senza tracce in questo momento.

Ho provato a leggere i file Pkcs5S2ParametersGenerator.cs e PBKDF2Params.cs ma non riesco davvero a capire come farlo.

Secondo la ricerca che ho fatto finora, PBKDF2 richiede una stringa (o char []) che è la password, un numero di sale e un numero di iterazioni.

Finora il più promettente e più ovvio che ho fatto finora è PBKDF2Params e Pkcs5S2ParametersGenerator.

Nessuno di questi sembra accettare una stringa o un carattere [].

Qualcuno ha fatto questo in C# o ha qualche indizio su questo? O forse qualcuno che ha implementato BouncyCastle in Java e può aiutare?

Grazie mille in anticipo :)

UPDATE: ho trovato come fare questo nel castello gonfiabile. Guarda sotto per la risposta :)

risposta

13

Dopo ore e ore di lettura del codice, ho trovato che il modo più semplice per farlo è quello di prendere alcune parti del codice in Pkcs5S2ParametersGenerator.cs e creare la mia classe che ovviamente usa altre API di BouncyCastle. Funziona perfettamente con Dot Net Compact Framework (Windows Mobile). Questo è l'equivalente della classe Rfc2898DeriveBytes che non è presente in DotNet Compact Framework 2.0/3.5. Beh, forse non l'esatto equivalente ma non il lavoro :)

Questo è PKCS5/PKCS # 5

Il PRF (Funzione Pseudo casuale) che viene utilizzato sarà HMAC-SHA1

Prima le cose, primo. Scarica il gruppo compilato da Bouncy Castle dal http://www.bouncycastle.org/csharp/, aggiungi BouncyCastle.Crypto.dll come riferimento al tuo progetto.

Successivamente creare un nuovo file di classe con il seguente codice.

using System; 
using Org.BouncyCastle.Crypto; 
using Org.BouncyCastle.Crypto.Parameters; 
using Org.BouncyCastle.Crypto.Digests; 
using Org.BouncyCastle.Crypto.Macs; 
using Org.BouncyCastle.Math; 
using Org.BouncyCastle.Security; 

namespace PBKDF2_PKCS5 
{ 
    class PBKDF2 
    { 

     private readonly IMac hMac = new HMac(new Sha1Digest()); 

     private void F(
      byte[] P, 
      byte[] S, 
      int c, 
      byte[] iBuf, 
      byte[] outBytes, 
      int outOff) 
     { 
      byte[] state = new byte[hMac.GetMacSize()]; 
      ICipherParameters param = new KeyParameter(P); 

      hMac.Init(param); 

      if (S != null) 
      { 
       hMac.BlockUpdate(S, 0, S.Length); 
      } 

      hMac.BlockUpdate(iBuf, 0, iBuf.Length); 

      hMac.DoFinal(state, 0); 

      Array.Copy(state, 0, outBytes, outOff, state.Length); 

      for (int count = 1; count != c; count++) 
      { 
       hMac.Init(param); 
       hMac.BlockUpdate(state, 0, state.Length); 
       hMac.DoFinal(state, 0); 

       for (int j = 0; j != state.Length; j++) 
       { 
        outBytes[outOff + j] ^= state[j]; 
       } 
      } 
     } 

     private void IntToOctet(
      byte[] Buffer, 
      int i) 
     { 
      Buffer[0] = (byte)((uint)i >> 24); 
      Buffer[1] = (byte)((uint)i >> 16); 
      Buffer[2] = (byte)((uint)i >> 8); 
      Buffer[3] = (byte)i; 
     } 

     // Use this function to retrieve a derived key. 
     // dkLen is in octets, how much bytes you want when the function to return. 
     // mPassword is the password converted to bytes. 
     // mSalt is the salt converted to bytes 
     // mIterationCount is the how much iterations you want to perform. 


     public byte[] GenerateDerivedKey(
      int dkLen, 
      byte[] mPassword, 
      byte[] mSalt, 
      int mIterationCount 
      ) 
     { 
      int hLen = hMac.GetMacSize(); 
      int l = (dkLen + hLen - 1)/hLen; 
      byte[] iBuf = new byte[4]; 
      byte[] outBytes = new byte[l * hLen]; 

      for (int i = 1; i <= l; i++) 
      { 
       IntToOctet(iBuf, i); 

       F(mPassword, mSalt, mIterationCount, iBuf, outBytes, (i - 1) * hLen); 
      } 

     //By this time outBytes will contain the derived key + more bytes. 
     // According to the PKCS #5 v2.0: Password-Based Cryptography Standard (www.truecrypt.org/docs/pkcs5v2-0.pdf) 
     // we have to "extract the first dkLen octets to produce a derived key". 

     //I am creating a byte array with the size of dkLen and then using 
     //Buffer.BlockCopy to copy ONLY the dkLen amount of bytes to it 
     // And finally returning it :D 

     byte[] output = new byte[dkLen]; 

     Buffer.BlockCopy(outBytes, 0, output, 0, dkLen); 

     return output; 
     } 


    } 
} 

Quindi, come utilizzare questa funzione? Semplice! :) Questo è un esempio molto semplice in cui la password e il sale sono forniti dall'utente.

private void cmdDeriveKey_Click(object sender, EventArgs e) 
     { 
      byte[] salt = ASCIIEncoding.UTF8.GetBytes(txtSalt.Text); 

      PBKDF2 passwordDerive = new PBKDF2(); 


     // I want the key to be used for AES-128, thus I want the derived key to be 
     // 128 bits. Thus I will be using 128/8 = 16 for dkLen (Derived Key Length) . 
     //Similarly if you wanted a 256 bit key, dkLen would be 256/8 = 32. 

      byte[] result = passwordDerive.GenerateDerivedKey(16, ASCIIEncoding.UTF8.GetBytes(txtPassword.Text), salt, 1000); 

      //result would now contain the derived key. Use it for whatever cryptographic purpose now :) 
      //The following code is ONLY to show the derived key in a Textbox. 

      string x = ""; 

      for (int i = 0; i < result.Length; i++) 
      { 
       x += result[i].ToString("X"); 
      } 

      txtResult.Text = x; 

     } 

Come verificare se questo è corretto? V'è una linea javascript implementazione di PBKDF2 http://anandam.name/pbkdf2/

ho ottenuto risultati coerenti :) Si prega di segnalare se qualcuno sta ottenendo un risultato errato :)

Spero che questo aiuti qualcuno :)

UPDATE: Confermato lavorando con vettori di test disponibile qui

http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-00

UPDAT E: In alternativa, per il sale possiamo usare uno RNGCryptoServiceProvider. Assicurarsi di fare riferimento allo spazio dei nomi System.Security.Cryptography.

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();   

byte[] salt = new byte[16]; 

rng.GetBytes(salt); 
+1

Rispondete alle vostre domande abbastanza velocemente! –

+1

Mi hai appena salvato un sacco di tempo. Grazie! – John

+0

@ John: Felice di essere d'aiuto :) –

1

Ho avuto questo problema da solo e ho trovato un approccio più diretto. Come di almeno Bouncy Castle 1.7 si può fare in questo modo (in VB utilizzando Org.BouncyCastle.Crypto):

Dim bcKeyDer As New Generators.Pkcs5S2ParametersGenerator() 
bcKeyDer.Init(password, salt, keyIterations) 
Dim bcparam As Parameters.KeyParameter = bcKeyDer.GenerateDerivedParameters("aes256", 256) 
Dim key1() As Byte = bcparam.GetKey() 

Ho testato questo contro System.Security.Cryptography di .Net, e funziona!