2015-10-04 4 views
10

Sto usando questo codice per generare stringhe casuali con data lunghezzautilizzando RNGCryptoServiceProvider per generare stringa casuale

public string RandomString(int length) 
{ 
    const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 
    StringBuilder res = new StringBuilder(); 
    Random rnd = new Random(); 
    while (0 < length--) 
    { 
     res.Append(valid[rnd.Next(valid.Length)]); 
    } 
    return res.ToString(); 
} 

Tuttavia, ho letto che RNGCryptoServiceProvider è più sicuro di Random classe. Come posso implementare RNGCryptoServiceProvider in questa funzione. Dovrebbe usare la stringa valid proprio come questa funzione.

risposta

2

I RNGCryptoServiceProvider rendimenti numeri casuali in forma di byte, quindi è necessario un modo per ottenere un numero casuale più conveniente da esso:

public static int GetInt(RNGCryptoServiceProvider rnd, int max) { 
    byte[] r = new byte[4]; 
    int value; 
    do { 
    rnd.GetBytes(r); 
    value = BitConverter.ToInt32(r, 0) & Int32.MaxValue; 
    } while (value >= max * (Int32.MaxValue/max)); 
    return value % max; 
} 

Quindi è possibile utilizzare che nel metodo:

public static string RandomString(int length) { 
    const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 
    StringBuilder res = new StringBuilder(); 
    using (RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider()) { 
    while (length-- > 0) { 
     res.Append(valid[GetInt(rnd, valid.Length)]); 
    } 
    } 
    return res.ToString(); 
} 

(ho fatto il metodo statico, in quanto non utilizza alcun dati di istanza.)

14

Dal RNGRandomNumberGenerator restituisce solo array di byte, quello che dovete fare è l ike questo:

static string RandomString(int length) 
{ 
    const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 
    StringBuilder res = new StringBuilder(); 
    using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) 
    { 
     byte[] uintBuffer = new byte[sizeof(uint)]; 

     while (length-- > 0) 
     { 
      rng.GetBytes(uintBuffer); 
      uint num = BitConverter.ToUInt32(uintBuffer, 0); 
      res.Append(valid[(int)(num % (uint)valid.Length)]); 
     } 
    } 

    return res.ToString(); 
} 

nota tuttavia che questa ha un difetto, 62 caratteri validi è pari a 5,9541963103868752088061235991756 bit (log (62)/log (2)), in modo da non dividere equamente su 32 numero di bit (uint).

Quali conseguenze ha? Come risultato, l'output casuale non sarà uniforme. I caratteri che sono più bassi nella validità si verificano più probabilmente (solo di una piccola frazione, ma succede ancora).

Per essere più precisi, i primi 4 caratteri dell'array valido sono maggiori di 0,00000144354999199840239435286%.

Per evitare ciò, è necessario utilizzare lunghezze degli array che dividono uniformemente come 64 (Considerare l'uso Convert.ToBase64String sull'uscita invece, dato che è possibile pulito abbinare 64 bit a 6 byte.

+1

È necessario fare attenzione quando si convertono i valori di byte in un numero in un intervallo specifico. Se si usa '%', allora produrrà numeri con una distribuzione non uniforme, e l'intero scopo di usare un generatore casuale migliore è inutile. – Guffa

+1

È vero, volevo aggiungere che dopo aver postato la mia risposta. La risposta di Tamir Vered è piuttosto interessante anche se orribilmente inefficiente dal momento che non usa StringBuilder. Lascia fuori i byte non accettati come input, che è una soluzione interessante e avrà una distribuzione corretta, come pure – hl3mukkel

2

È necessario generare casuali byte s utilizzando RNGCryptoServiceProvider e aggiungere solo quelli validi alla tornata string:.

const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 

static string GetRandomString(int length) 
{ 
    string s = ""; 
    using (RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider()) 
    { 
     while (s.Length != length) 
     { 
      byte[] oneByte = new byte[1]; 
      provider.GetBytes(oneByte); 
      char character = (char)oneByte[0]; 
      if (valid.Contains(character)) 
      { 
       s += character; 
      } 
     } 
    } 
    return s; 
} 

Si potrebbe anche usare modulo al fine di non saltare i validi byte valori, ma che le possibilità per ogni personaggio non sarà nemmeno

+0

The 's.Length! = Valid.Length' dovrebbe essere' s.Length! = Length', altrimenti crea una stringa con la stessa lunghezza della stringa 'valid' (e il parametro' length' è necessario). – Guffa

+0

Sì, non ho notato che vuole fornire la lunghezza del metodo, la modifica. –

2

Sono sicuro di aver già risposto prima con un'implementazione sicura, senza pregiudizi e buone prestazioni. Se è così, si prega di commentare.

Guardando la risposta di Tamir, ho pensato che sarebbe stato meglio usare l'operazione modulo, ma eliminare il resto incompleto dei valori di byte. Sto anche scrivendo questa risposta ora (forse di nuovo), perché ho bisogno di fare riferimento a questa soluzione per un peer.

Non è ancora testato, quindi potrebbero esserci un paio di bug.

const string lookup = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 

static string GetRandomString(int length) 
{ 
    if (lookup.Length > 256) 
     throw new Exception("Lookup must be <= 256 characters in length"); 

    var maxMultiples = (int)Math.Floor(256/lookup.Length); 
    var exclusiveLimit = (lookup.Length * maxMultiples); 

    var sb = new StringBuilder(); 
    using (var provider = new RNGCryptoServiceProvider()) 
    { 
     var buffer = new byte[length]; 
     while (true) 
     { 
      var remaining = length - b.Length; 
      if (remaining == 0) 
       break; 

      provider.GetBytes(oneByte, 0 remaining); 

      for (int i = 0; i < remaining) 
      { 
       if (buffer[i] >= exclusiveLimit) 
        continue; 

       var index = buffer[i] % lookup.Length; 
       sb.Append(lookup[index]); 
      } 
     } 
    } 
    return sb.ToString(); 
} 
+0

Ah, trovato: https://stackoverflow.com/a/1518495/887092. Ma questo potrebbe ancora essere migliore (quando è testato e confermato per funzionare). Per favore mi faccia sapere. – Todd

0
private string sifreuretimi(int sayı) //3 
    { 
     Random rastgele = new Random(); 
     StringBuilder sb = new StringBuilder(); 
     char karakter1 = ' ', karakter2 = ' ', karakter3 = ' '; 
     int ascii1, ascii2, ascii3 = 0; 

     for (int i = 0; i < sayı/3; i++) 
     { 
      ascii1 = rastgele.Next(48,58); 
      karakter1 = Convert.ToChar(ascii1); 

      ascii2 = rastgele.Next(65, 91); 
      karakter2 = Convert.ToChar(ascii2); 

      ascii3 = rastgele.Next(97, 123); 
      karakter3 = Convert.ToChar(ascii3); 

      sb.Append(karakter1); 
      sb.Append(karakter2); 
      sb.Append(karakter3); 
     } 
     return sb.ToString(); 
    } 
+0

Ciao, si prega di fornire un commento per spiegare la risposta; grazie ! – NatNgs

+0

L'utilizzo di un array di caratteri in tale software non è l'approccio corretto. –

0

nota: Sono a conoscenza del caso d'uso di deviazione del PO, ma penso che potrebbe aiutare gli altri che hanno un caso d'uso leggermente diverso

Molto può essere detto a proposito del motivi per convertire un array di byte crittografici in una stringa, ma di solito è per scopi di serializzazione; e quindi, in quel caso: il set di caratteri selezionato è arbitrario.

Quindi, se e solo se il primo è vero E la lunghezza della stringa non deve essere ottimizzata; è possibile utilizzare una semplice rappresentazione esadecimale della matrice di byte in questo modo:

//note: since the choice of characters [0..9a..zA...Z] is arbitrary, 
//limiting to [0..9,A..F] would seem to be a really big problem if it can be compensated 
//by the length. 

var rnd = new RNGCryptoServiceProvider(); 
var sb = new StringBuilder(); 
var buf = new byte[10]; //length: should be larger 
rnd.GetBytes(buf); 

//gives a "valid" range of: "ABCDEF" 
foreach (byte b in buf) 
    sb.AppendFormat("{0:x2}", b); 

//sb contains a RNGCryptoServiceProvider based "string" 

Ora direte: Ma aspetta: questi sono solo 16 caratteri in cui la sequenza di OP ha 62. La stringa sarà molto più a lungo.

"Sì", io risponderò, "e se questo è un problema, perché non si fa a scegliere 256 serrializable-caratteri di facile lettura e ... o forse 64"; -)

Come dichiarato da @Guffa; utilizzando % è vietato a meno che non altera la distribuzione. Per fare in modo che ciò accada, dato un insieme uniformemente distribuito, il sottoinsieme deve corrispondere esattamente a x volte nel set originale.

Così, espandendo il set iniziale valido con 2 si ottiene un risultato valido (perché: 256/64 = 4).

Il codice sarebbe:

//note: added + and/chars. could be any of them 
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/"; 

var rnd = new RNGCryptoServiceProvider(); 
var sb = new StringBuilder(); 
var buf = new byte[10]; //length: should be larger 
rnd.GetBytes(buf); //get the bytes 

foreach (byte b in buf) 
    sb.Append(valid[b%64]); 

Si prega di notare: in tutte le risposte, compreso questo, il sub-set è più piccolo di 256 possibilità del byte. Questo significa che ci sono meno informazioni disponibili. Ciò significa che se hai la tua stringa con 4 caratteri, è più facile decifrare il risultato originale a 4 byte di RNGCryptoServiceProvider.

Quindi ... ora si dice: "Perché non utilizzare la codifica di base 64?", bene, se si tratta di vi si addice, ma stare attenti con il trailing =, vedere Base64 on Wikipedia:

var rnd = new RNGCryptoServiceProvider(); 
var buf = new byte[60]; //length not randomly picked, see 64Base, wikipedia 
rnd.GetBytes(buf); 
string result = Convert.ToBase64String(buf); 

prega note²: Un tipico caso d'uso è qualche segno url. Si noti che il segno + non è valido come tale carattere.