Pamiętaj, że NIE powinieneś używać żadnej metody opartej na Randomklasie do generowania haseł. Siew Randomma bardzo niską entropię, więc nie jest tak naprawdę bezpieczny. Użyj hasła kryptograficznego PRNG.
CodesInChaos
2
Byłoby miło dołączyć do tego pytania lokalizację języka. Zwłaszcza jeśli twoje gui musi obsługiwać język chiński lub bułgarski!
Peter Jamsmenson,
15
Coś z tyloma głosami i tak wieloma wysokiej jakości odpowiedziami nie zasługiwało na oznaczenie jako zamknięte. Głosuję za ponownym otwarciem.
John Coleman
Odpowiedzi:
1684
Słyszałem, że LINQ to nowa czerń, więc oto moja próba użycia LINQ:
privatestaticRandom random =newRandom();publicstaticstringRandomString(int length){conststring chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";returnnewstring(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());}
(Uwaga: użycie tej Randomklasy sprawia, że nie nadaje się do jakichkolwiek kwestii związanych z bezpieczeństwem , takich jak tworzenie haseł lub tokenów. Użyj RNGCryptoServiceProviderklasy, jeśli potrzebujesz silnego generatora liczb losowych.)
@Alex: Przeprowadziłem kilka szybkich testów i wydaje się, że skaluje się dość liniowo podczas generowania dłuższych ciągów (o ile faktycznie jest wystarczająca ilość dostępnej pamięci). Powiedziawszy to, odpowiedź Dana Rigby'ego była prawie dwa razy szybsza niż ta w każdym teście.
Łukasza
6
Dobrze. Jeśli twoim kryterium jest to, że używa linq i że ma kiepską narrację kodu, to zdecydowanie są to kolana pszczoły. Zarówno narracja kodu, jak i rzeczywista ścieżka wykonania są raczej nieefektywne i pośrednie. Nie zrozumcie mnie źle, jestem ogromnym hipsterem kodu (uwielbiam pytona), ale jest to właściwie maszyna Rube Goldberg.
eremzeit
5
Chociaż technicznie odpowiada to na pytanie, jego wyniki są bardzo mylące. Generowanie 8 losowych znaków wydaje się, że może być bardzo wiele wyników, a co najwyżej daje 2 miliardy różnych wyników. A w praktyce jeszcze mniej. Powinieneś także dodać ostrzeżenie BIG FAT, aby nie używać go do żadnych rzeczy związanych z bezpieczeństwem.
CodesInChaos
41
@xaisoft: Małe litery są ćwiczeniem dla czytelnika.
dtb
15
Następująca linia jest bardziej wydajna pod względem pamięci (a tym samym czasu) niż podanareturn new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
Tyson Williams,
374
var chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";var stringChars =newchar[8];var random =newRandom();for(int i =0; i < stringChars.Length; i++){
stringChars[i]= chars[random.Next(chars.Length)];}var finalString =newString(stringChars);
Nie tak elegancki jak rozwiązanie Linq.
(Uwaga: Randomczyni to klasanie nadaje się do jakichkolwiek kwestii związanych z bezpieczeństwem , takich jak tworzenie haseł lub tokenów. Użyj RNGCryptoServiceProviderklasy, jeśli potrzebujesz silnego generatora liczb losowych.)
@Alex: To nie jest absolutnie najszybsza odpowiedź, ale jest to najszybsza „prawdziwa” odpowiedź (tj. Tych, które pozwalają kontrolować użyte znaki i długość łańcucha).
Łukasza
2
@Alex: Rozwiązanie Adama Porada GetRandomFileNamejest szybsze, ale nie pozwala na kontrolę używanych znaków, a maksymalna możliwa długość to 11 znaków. GuidRozwiązanie Douglasa jest błyskawiczne, ale postacie są ograniczone do A-F0-9, a maksymalna możliwa długość to 32 znaki.
Łukasza
1
@Adam: Tak, możesz powiązać wynik wielu wywołań, GetRandomFileNameale wtedy (a) stracisz przewagę wydajności i (b) kod stanie się bardziej skomplikowany.
Łukasza
1
@xaisoft utwórz instancję obiektu Random () poza pętlą. Jeśli utworzysz wiele wystąpień funkcji Random () w krótkim odstępie czasu, wywołanie funkcji .Next () zwróci tę samą wartość, co funkcja Random () wykorzystująca ziarno oparte na czasie.
Dan Rigby
2
@xaisoft Nie używaj tej odpowiedzi do celów krytycznych dla bezpieczeństwa, takich jak hasła. System.Randomnie nadaje się do bezpieczeństwa.
CodesInChaos
333
AKTUALIZACJA na podstawie komentarzy. Oryginalna implementacja generowała ah ~ 1,95% czasu, a pozostałe znaki ~ 1,56% czasu. Aktualizacja generuje wszystkie znaki ~ 1,61% czasu.
WSPARCIE RAMOWE - .NET Core 3 (i przyszłe platformy obsługujące .NET Standard 2.1 lub nowszy) zapewnia kryptograficznie solidną metodę RandomNumberGenerator.GetInt32 () w celu wygenerowania losowej liczby całkowitej w żądanym zakresie.
W przeciwieństwie do niektórych przedstawionych alternatyw, ta jest kryptograficznie solidna .
using System;
using System.Security.Cryptography;
using System.Text;
namespace UniqueKey{publicclassKeyGenerator{internalstaticreadonlychar[] chars ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();publicstaticstringGetUniqueKey(int size){byte[] data =newbyte[4*size];
using (RNGCryptoServiceProvider crypto =newRNGCryptoServiceProvider()){
crypto.GetBytes(data);}StringBuilder result =newStringBuilder(size);for(int i =0; i < size; i++){var rnd =BitConverter.ToUInt32(data, i *4);var idx = rnd % chars.Length;
result.Append(chars[idx]);}return result.ToString();}publicstaticstringGetUniqueKeyOriginal_BIASED(int size){char[] chars ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();byte[] data =newbyte[size];
using (RNGCryptoServiceProvider crypto =newRNGCryptoServiceProvider()){
crypto.GetBytes(data);}StringBuilder result =newStringBuilder(size);foreach(byte b in data){
result.Append(chars[b %(chars.Length)]);}return result.ToString();}}}
Na podstawie dyskusji o alternatywach tutaj i zaktualizowanej / zmodyfikowanej na podstawie poniższych komentarzy.
Oto mała uprząż testowa, która demonstruje rozkład znaków w starych i zaktualizowanych wynikach. W celu dogłębnej dyskusji na temat analizy losowości odwiedź random.org.
using System;
using System.Collections.Generic;
using System.Linq;
using UniqueKey;
namespace CryptoRNGDemo{classProgram{constint REPETITIONS =1000000;constint KEY_SIZE =32;staticvoidMain(string[] args){Console.WriteLine("Original BIASED implementation");PerformTest(REPETITIONS, KEY_SIZE,KeyGenerator.GetUniqueKeyOriginal_BIASED);Console.WriteLine("Updated implementation");PerformTest(REPETITIONS, KEY_SIZE,KeyGenerator.GetUniqueKey);Console.ReadKey();}staticvoidPerformTest(int repetitions,int keySize,Func<int,string> generator){Dictionary<char,int> counts =newDictionary<char,int>();foreach(var ch inUniqueKey.KeyGenerator.chars) counts.Add(ch,0);for(int i =0; i < REPETITIONS; i++){var key = generator(KEY_SIZE);foreach(var ch in key) counts[ch]++;}int totalChars = counts.Values.Sum();foreach(var ch inUniqueKey.KeyGenerator.chars){Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");}}}}
Wygląda to na prawidłowe podejście do mnie - losowe hasła, sole, entropia itp. Nie powinny być generowane za pomocą Random (), który jest zoptymalizowany pod kątem szybkości i generuje powtarzalne sekwencje liczb; Z drugiej strony RNGCryptoServiceProvider.GetNonZeroBytes () tworzy dzikie sekwencje liczb, które NIE są odtwarzalne.
mindplay.dk
25
Litery są lekko stronnicze (255% 62! = 0). Mimo tej drobnej wady jest to zdecydowanie najlepsze rozwiązanie.
CodesInChaos
13
Zauważ, że to nie brzmi, jeśli chcesz krypto-siły, obiektywnej losowości. (A jeśli tego nie chcesz, to po co używać RNGCSP?) Używanie mod do indeksowania w charstablicy oznacza, że otrzymasz stronnicze wyjście, chyba że chars.Lengthstanie się dzielnikiem 256.
LukeH
15
Jedną z możliwości znacznego zmniejszenia błędu systematycznego jest zażądanie 4*maxSizelosowych bajtów, a następnie użycie (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length. Użyłbym również GetByteszamiast GetNonZeroBytes. I w końcu możesz usunąć pierwsze połączenie z GetNonZeroBytes. Nie wykorzystujesz jego wyniku.
CodesInChaos
14
Ciekawostka: AZ az 0-9 ma 62 znaki. Ludzie zwracają uwagę na odchylenie liter, ponieważ 256% 62! = 0. Identyfikatory filmów w YouTube to AZ az 0-9, a także „-” i „_”, co daje 64 możliwe znaki, które dzielą się na 256 równomiernie. Zbieg okoliczności? Myślę, że nie! :)
qJake
200
Rozwiązanie 1 - największy „zasięg” o najbardziej elastycznej długości
To rozwiązanie ma większy zasięg niż używanie GUID, ponieważ GUID ma kilka stałych bitów, które są zawsze takie same i dlatego nie są losowe, na przykład 13 znaków w hex jest zawsze „4” - przynajmniej w GUID wersji 6.
To rozwiązanie pozwala również generować ciąg o dowolnej długości.
Rozwiązanie 2 - Jedna linia kodu - odpowiednia dla maksymalnie 22 znaków
Nie można generować ciągów, dopóki Rozwiązanie 1, a ciąg nie ma tego samego zakresu z powodu ustalonych bitów w identyfikatorach GUID, ale w wielu przypadkach to wystarczy.
Rozwiązanie 3 - Nieco mniej kodu
Guid.NewGuid().ToString("n").Substring(0,8);
Przeważnie trzymam to tutaj do celów historycznych. Zużywa nieco mniej kodu, co jest kosztem posiadania mniejszego zasięgu - ponieważ używa hexa zamiast base64, potrzeba więcej znaków do przedstawienia tego samego zakresu w porównaniu z innymi rozwiązaniami.
Co oznacza większą szansę na kolizję - przetestowanie go przy 100 000 iteracjach 8 ciągów znaków wygenerowało jeden duplikat.
@Alex: Skraca GUID do 8 znaków, więc prawdopodobieństwo kolizji jest znacznie wyższe niż GUID.
dtb
23
Nikt nie może tego docenić oprócz kujonów :) Tak, masz całkowitą rację, limit 8 znaków robi różnicę.
Alex
31
Guid.NewGuid (). ToString („n”) utrzyma kreski z daleka, nie jest wymagane wywołanie Replace (). Należy jednak wspomnieć, GUID to tylko 0–9 i AF. Liczba kombinacji jest „wystarczająco dobra”, ale nigdzie nie zbliżona do tego, na co pozwala prawdziwy losowy ciąg alfanumeryczny. Szanse na kolizję wynoszą 1: 4 294 967 296 - to samo co losowa 32-bitowa liczba całkowita.
richardtallent
32
1) GUID są zaprojektowane tak, aby były unikalne, a nie losowe. Chociaż obecne wersje systemu Windows generują identyfikatory GUID V4, które są rzeczywiście losowe, nie jest to gwarantowane. Na przykład starsze wersje systemu Windows używały identyfikatorów GUID V1, w których może wystąpić awaria. 2) Samo użycie znaków szesnastkowych znacznie obniża jakość losowego ciągu. Od 47 do 32 bitów. 3) Ludzie nie doceniają prawdopodobieństwa kolizji, ponieważ dają je dla poszczególnych par. Jeśli wygenerujesz 100k 32-bitowych wartości, prawdopodobnie masz między nimi jedną kolizję. Zobacz problem z urodzinami.
CodesInChaos
72
Oto przykład, który ukradłem z przykładu Sama Allena w Dot Net Perls
Jeśli potrzebujesz tylko 8 znaków, użyj Path.GetRandomFileName () w przestrzeni nazw System.IO. Sam mówi, że użycie „Path.GetRandomFileName tutaj jest czasem lepsze, ponieważ używa RNGCryptoServiceProvider dla lepszej losowości. Jednak jest ograniczone do 11 losowych znaków”.
GetRandomFileName zawsze zwraca ciąg 12 znaków z kropką na 9 znaku. Musisz więc usunąć kropkę (ponieważ nie jest to przypadek), a następnie pobrać 8 znaków z ciągu. Właściwie możesz wziąć pierwsze 8 znaków i nie martwić się o kropkę.
To działa dobrze. Przeprowadziłem go przez 100 000 iteracji i nigdy nie miałem zduplikowanej nazwy. Znalazłem jednak kilka wulgarnych słów (po angielsku). Nie pomyślałbym o tym, gdyby tylko jeden z pierwszych na liście miał F ***. Tylko jedna głowa, jeśli użyjesz tego do czegoś, co zobaczy użytkownik.
techturtle
3
@techturtle Dzięki za ostrzeżenie. Podejrzewam, że istnieje ryzyko wulgarnych słów w przypadku dowolnego generowania ciągów losowych, które wykorzystują wszystkie litery alfabetu.
Adam Porad,
ładne i proste, ale niezbyt dobre na długie
sznurki
Ta metoda wydaje się zwracać tylko małe ciągi alfanumeryczne.
jaybro
2
Od czasu do czasu pojawiają się wulgarne słowa, ale jeśli utrzymasz to wystarczająco długo, w końcu pisze Szekspir. (Zaledwie kilka wcieleń wszechświata. :)
Slothario
38
Głównymi celami mojego kodu są:
Rozkład ciągów jest prawie równomierny (nie przejmuj się drobnymi odchyleniami, o ile są małe)
Wyprowadza ponad kilka miliardów ciągów dla każdego zestawu argumentów. Generowanie ciągu 8 znaków (~ 47 bitów entropii) jest bez znaczenia, jeśli PRNG generuje tylko 2 miliardy (31 bitów entropii) różnych wartości.
Jest bezpieczny, ponieważ oczekuję, że ludzie będą go używać do haseł lub innych tokenów bezpieczeństwa.
Pierwszą właściwość uzyskuje się, biorąc 64-bitową wartość modulo wielkości alfabetu. W przypadku małych alfabetów (takich jak 62 znaki z pytania) prowadzi to do nieznacznego błędu. Drugą i trzecią właściwość uzyskuje się za pomocą RNGCryptoServiceProviderzamiast System.Random.
using System;
using System.Security.Cryptography;publicstaticstringGetRandomAlphanumericString(int length){conststring alphanumericCharacters ="ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz"+"0123456789";returnGetRandomString(length, alphanumericCharacters);}publicstaticstringGetRandomString(int length,IEnumerable<char> characterSet){if(length <0)thrownewArgumentException("length must not be negative","length");if(length >int.MaxValue/8)// 250 million chars ought to be enough for anybodythrownewArgumentException("length is too big","length");if(characterSet ==null)thrownewArgumentNullException("characterSet");var characterArray = characterSet.Distinct().ToArray();if(characterArray.Length==0)thrownewArgumentException("characterSet must not be empty","characterSet");var bytes =newbyte[length *8];var result =newchar[length];
using (var cryptoProvider =newRNGCryptoServiceProvider()){
cryptoProvider.GetBytes(bytes);}for(int i =0; i < length; i++){ulongvalue=BitConverter.ToUInt64(bytes, i *8);
result[i]= characterArray[value%(uint)characterArray.Length];}returnnewstring(result);}
Nie ma przecięcia z 64 x Z i Math.Pow (2, Y). Zatem zwiększenie liczby zmniejsza uprzedzenie, ale go nie eliminuje. Zaktualizowałem odpowiedź poniżej, moje podejście polegało na odrzuceniu losowych danych wejściowych i zastąpieniu ich inną wartością.
Todd
@Todd Wiem, że to nie eliminuje stronniczości, ale wybrałem prostotę tego rozwiązania zamiast eliminacji praktycznie nieistotnej stronniczości.
CodesInChaos
Zgadzam się, że w większości przypadków jest to praktycznie nieistotne. Ale teraz zaktualizowałem mój, aby był tak szybki jak Losowy i trochę bezpieczniejszy niż twój. Wszystkie otwarte źródła dla wszystkich do udostępnienia. Tak, zmarnowałem na to zdecydowanie zbyt dużo czasu ...
Todd
Jeśli korzystamy z usług dostawcy RNG, czy mamy jakiś sposób na uniknięcie stronniczości w teorii? Nie jestem pewien ... Jeśli Todd ma na myśli sposób, w jaki generuje dodatkową liczbę losową (gdy jesteśmy w strefie odchylenia), może to być błędne założenie. RNG ma prawie liniowy rozkład wszystkich generowanych wartości. Ale to nie znaczy, że nie będziemy mieli lokalnej korelacji między wygenerowanymi bajtami. Tak więc dodatkowy bajt tylko dla strefy stronniczości może nadal dawać pewne uprzedzenie, ale z innego powodu. Najprawdopodobniej ta tendencja będzie bardzo mała. ALE w tym przypadku zwiększenie całkowitej liczby wygenerowanych bajtów jest prostsze.
Maxim
1
@Maksym Możesz użyć odrzucenia, aby całkowicie wyeliminować błąd systematyczny (zakładając, że podstawowy generator jest całkowicie losowy). W zamian kod może działać dowolnie długo (z wykładniczo małym prawdopodobieństwem).
Jeśli kiedykolwiek będziesz się martwić, angielskie alfabety mogą się kiedyś zmienić i możesz stracić biznes, możesz uniknąć twardego kodowania, ale powinno działać nieco gorzej (porównywalne do Path.GetRandomFileNamepodejścia)
publicstaticstringGetRandomAlphaNumeric(){var chars ='a'.To('z').Concat('0'.To('9')).ToList();returnnewstring(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());}publicstaticIEnumerable<char>To(thischar start,char end){if(end < start)thrownewArgumentOutOfRangeException("the end char should not be less than start char", innerException:null);returnEnumerable.Range(start, end - start +1).Select(i =>(char)i);}
Dwa ostatnie podejścia wyglądają lepiej, jeśli można je na System.Randomprzykład ustawić jako metodę rozszerzenia .
Używanie chars.Selectjest bardzo brzydkie, ponieważ polega na tym, że rozmiar wyjściowy to co najwyżej rozmiar alfabetu.
CodesInChaos
@CodesInChaos Nie jestem pewien, czy cię rozumiem. Masz na myśli 'a'.To('z')podejście?
nawfal
1
1). chars.Select()Weź (n) `działa tylko, jeśli chars.Count >= n. Wybór sekwencji, której tak naprawdę nie używasz, jest nieco nieintuicyjny, szczególnie z tym ukrytym ograniczeniem długości. Wolałbym użyć Enumerable.Rangelub Enumerable.Repeat. 2) Komunikat o błędzie „Końcowy znak powinien być mniejszy niż początkowy znak” jest nieprawidłowy / nie ma go not.
CodesInChaos
@CodesInChaos, ale w moim przypadku chars.Countjest sposób > n. Nie dostaję też nieintuicyjnej części. To sprawia, że wszystkie zastosowania są Takenieintuicyjne, prawda? Nie wierzę w to. Dzięki za wskazanie literówki.
nawfal
4
Jest to opisywane na DailyDTW.com jako artykuł CodeSOD.
22
Tylko kilka porównań wydajności różnych odpowiedzi w tym wątku:
Testowane w LinqPad. Dla łańcucha o rozmiarze 10 generuje:
od Linq = chdgmevhcy [10]
z pętli = gtnoaryhxr [10]
z Select = rsndbztyby [10]
from GenerateRandomString = owyefjjakj [10]
from SecureFastRandom = VzougLYHYP [10]
z SecureFastRandom-NoCache = oVQXNGmO1S [10]
I numery wydajności wydają się nieznacznie różnić, bardzo rzadko NonOptimizedjest rzeczywiście szybciej, a czasem ForLoopi GenerateRandomStringprzełączyć kto jest w czołówce.
Byłoby ciekawie wiedzieć, które stworzyły duplikaty.
Rebecca
@Junto - aby dowiedzieć się, które skutkują duplikatami, coś w rodzaju var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());, gdzie zamieniasz na EachRandomizingMethod... każdą metodę
Myślałem o tym, ale nie mogłem się pozbyć znaków niealfanumerycznych, ponieważ drugi argument to MINIMUM znaków innych niż alfa
ozzy432836,
13
Kod napisany przez Erica J. jest dość niechlujny (jasne jest, że pochodzi on sprzed 6 lat ... prawdopodobnie nie napisałby dzisiaj tego kodu), a nawet są pewne problemy.
W przeciwieństwie do niektórych przedstawionych alternatyw, ta jest kryptograficznie solidna.
Nieprawdziwe ... Hasło zawiera błąd (jak napisano w komentarzu), bcdefghjest nieco bardziej prawdopodobny niż inne ( anie dlatego, że przez GetNonZeroBytesto nie generuje bajtów o wartości zero, więc błąd jest ponieważ ajest to zrównoważone), więc nie jest tak naprawdę kryptograficznie.
To powinno rozwiązać wszystkie problemy.
publicstaticstringGetUniqueKey(int size =6,string chars ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"){
using (var crypto =newRNGCryptoServiceProvider()){var data =newbyte[size];// If chars.Length isn't a power of 2 then there is a bias if// we simply use the modulus operator. The first characters of// chars will be more probable than the last ones.// buffer used if we encounter an unusable random byte. We will// regenerate it in this bufferbyte[] smallBuffer =null;// Maximum random number that can be used without introducing a// biasint maxRandom =byte.MaxValue-((byte.MaxValue+1)% chars.Length);
crypto.GetBytes(data);var result =newchar[size];for(int i =0; i < size; i++){byte v = data[i];while(v > maxRandom){if(smallBuffer ==null){
smallBuffer =newbyte[1];}
crypto.GetBytes(smallBuffer);
v = smallBuffer[0];}
result[i]= chars[v % chars.Length];}returnnewstring(result);}}
Pytanie: Dlaczego mam marnować czas Enumerable.Rangena pisanie zamiast pisania "ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789"?
using System;
using System.Collections.Generic;
using System.Linq;publicclassTest{publicstaticvoidMain(){var randomCharacters =GetRandomCharacters(8,true);Console.WriteLine(newstring(randomCharacters.ToArray()));}privatestaticList<char> getAvailableRandomCharacters(bool includeLowerCase){var integers =Enumerable.Empty<int>();
integers = integers.Concat(Enumerable.Range('A',26));
integers = integers.Concat(Enumerable.Range('0',10));if( includeLowerCase )
integers = integers.Concat(Enumerable.Range('a',26));return integers.Select(i =>(char)i).ToList();}publicstaticIEnumerable<char>GetRandomCharacters(int count,bool includeLowerCase){var characters = getAvailableRandomCharacters(includeLowerCase);var random =newRandom();var result =Enumerable.Range(0, count).Select(_ => characters[random.Next(characters.Count)]);return result;}}
Odpowiedź: Magiczne ciągi są ZŁE. Czy ktokolwiek zauważył, że nie ma „I w mojej strunie na górze ”? Moja matka nauczyła mnie nie używać magicznych sznurków z tego właśnie powodu ...
nb 1: Jak wiele osób mówiło @dtb, nie używaj System.Random jeśli potrzebujesz bezpieczeństwa kryptograficznego ...
Uwaga 2: Ta odpowiedź nie jest najskuteczniejsza ani najkrótsza, ale chciałem, aby przestrzeń oddzieliła odpowiedź od pytania. Celem mojej odpowiedzi jest bardziej ostrzeżenie przed magicznymi łańcuchami niż dostarczenie wymyślnej, innowacyjnej odpowiedzi.
Alfanumeryczny (ignorowanie wielkości liter) to [A-Z0-9]. Jeśli przypadkowo ciąg losowy obejmuje [A-HJ-Z0-9]tylko wynik, nie obejmuje on pełnego dopuszczalnego zakresu, co może być problematyczne.
Wai Ha Lee
Jak byłoby to problematyczne? Więc nie zawiera I. Czy to dlatego, że jest o jedną postać mniej, a to ułatwia złamanie? Jakie są statystyki haseł do zgryzania, które zawierają 35 znaków w zakresie niż 36. Myślę, że wolałbym zaryzykować ... lub po prostu sprawdzić zakres znaków ... niż zawrzeć wszystkie dodatkowe śmieci w moim kodzie. Ale to ja. To znaczy, żeby nie być dziurą w tyłku, mówię tylko. Czasami myślę, że programiści mają tendencję do wybierania bardzo skomplikowanej drogi ze względu na to, że są bardzo skomplikowani.
Christine
1
Zależy to od przypadku użycia. Bardzo często wyklucza się znaki takie jak Ii Oz tego typu losowych ciągów, aby uniknąć pomylenia ich przez ludzi z 1i 0. Jeśli nie zależy ci na łańcuchu czytelnym dla człowieka, dobrze, ale jeśli jest to coś, co ktoś może potrzebować wpisać, to naprawdę mądrze jest usunąć te znaki.
Chris Pratt
5
Nieco czystsza wersja rozwiązania DTB.
var chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";var random =newRandom();varlist=Enumerable.Repeat(0,8).Select(x=>chars[random.Next(chars.Length)]);returnstring.Join("",list);
Twoje preferencje dotyczące stylu mogą się różnić.
Po przejrzeniu innych odpowiedzi i rozważeniu komentarzy CodeInChaos, a także CodeInChaos wciąż stronniczej (choć mniejszej) odpowiedzi, pomyślałem, że konieczne jest ostateczne rozwiązanie cięcia i wklejania . Tak więc, aktualizując moją odpowiedź, postanowiłem wyjść na całość.
Odnosząc się do niektórych innych odpowiedzi - jeśli znasz długość wyniku, nie potrzebujesz StringBuilder, a podczas korzystania z ToCharArray tworzy i wypełnia tablicę (nie musisz najpierw tworzyć pustej tablicy)
Odnosząc się do kilku innych odpowiedzi - powinieneś używać NextBytes, zamiast uzyskiwać je pojedynczo dla wydajności
Technicznie rzecz biorąc, możesz przypiąć tablicę bajtów, aby uzyskać szybszy dostęp. Zazwyczaj warto, jeśli iterujesz więcej niż 6-8 razy w tablicy bajtów. (Nie zrobiono tutaj)
Zastosowanie RNGCryptoServiceProvider dla najlepszej losowości
Korzystanie z buforowania 1 MB bufora losowych danych - testy porównawcze pokazują, że buforowany pojedynczy bajt ma prędkość dostępu około 1000 razy szybszą - zajmuje 9 ms ponad 1 MB w porównaniu z 989 ms dla pamięci niebuforowanej.
Zoptymalizowane odrzucanie strefy stronniczości w mojej nowej klasie.
Zakończ rozwiązanie pytania:
staticchar[] charSet ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();staticint byteSize =256;//Labelling conveniencestaticint biasZone = byteSize -(byteSize % charSet.Length);publicstringGenerateRandomString(intLength)//Configurable output string length{byte[] rBytes =newbyte[Length];//Do as much before and after lock as possiblechar[] rName =newchar[Length];SecureFastRandom.GetNextBytesMax(rBytes, biasZone);for(var i =0; i <Length; i++){
rName[i]= charSet[rBytes[i]% charSet.Length];}returnnewstring(rName);}
Ale potrzebujesz mojej nowej (nieprzetestowanej) klasy:
/// <summary>/// My benchmarking showed that for RNGCryptoServiceProvider:/// 1. There is negligable benefit of sharing RNGCryptoServiceProvider object reference /// 2. Initial GetBytes takes 2ms, and an initial read of 1MB takes 3ms (starting to rise, but still negligable)/// 2. Cached is ~1000x faster for single byte at a time - taking 9ms over 1MB vs 989ms for uncached/// </summary>classSecureFastRandom{staticbyte[] byteCache =newbyte[1000000];//My benchmark showed that an initial read takes 2ms, and an initial read of this size takes 3ms (starting to raise)staticint lastPosition =0;staticint remaining =0;/// <summary>/// Static direct uncached access to the RNGCryptoServiceProvider GetBytes function/// </summary>/// <param name="buffer"></param>publicstaticvoidDirectGetBytes(byte[] buffer){
using (var r =newRNGCryptoServiceProvider()){
r.GetBytes(buffer);}}/// <summary>/// Main expected method to be called by user. Underlying random data is cached from RNGCryptoServiceProvider for best performance/// </summary>/// <param name="buffer"></param>publicstaticvoidGetBytes(byte[] buffer){if(buffer.Length> byteCache.Length){DirectGetBytes(buffer);return;}lock(byteCache){if(buffer.Length> remaining){DirectGetBytes(byteCache);
lastPosition =0;
remaining = byteCache.Length;}Buffer.BlockCopy(byteCache, lastPosition, buffer,0, buffer.Length);
lastPosition += buffer.Length;
remaining -= buffer.Length;}}/// <summary>/// Return a single byte from the cache of random data./// </summary>/// <returns></returns>publicstaticbyteGetByte(){lock(byteCache){returnUnsafeGetByte();}}/// <summary>/// Shared with public GetByte and GetBytesWithMax, and not locked to reduce lock/unlocking in loops. Must be called within lock of byteCache./// </summary>/// <returns></returns>staticbyteUnsafeGetByte(){if(1> remaining){DirectGetBytes(byteCache);
lastPosition =0;
remaining = byteCache.Length;}
lastPosition++;
remaining--;return byteCache[lastPosition -1];}/// <summary>/// Rejects bytes which are equal to or greater than max. This is useful for ensuring there is no bias when you are modulating with a non power of 2 number./// </summary>/// <param name="buffer"></param>/// <param name="max"></param>publicstaticvoidGetBytesWithMax(byte[] buffer,byte max){if(buffer.Length> byteCache.Length/2)//No point caching for larger sizes{DirectGetBytes(buffer);lock(byteCache){UnsafeCheckBytesMax(buffer, max);}}else{lock(byteCache){if(buffer.Length> remaining)//Recache if not enough remaining, discarding remaining - too much work to join two blocksDirectGetBytes(byteCache);Buffer.BlockCopy(byteCache, lastPosition, buffer,0, buffer.Length);
lastPosition += buffer.Length;
remaining -= buffer.Length;UnsafeCheckBytesMax(buffer, max);}}}/// <summary>/// Checks buffer for bytes equal and above max. Must be called within lock of byteCache./// </summary>/// <param name="buffer"></param>/// <param name="max"></param>staticvoidUnsafeCheckBytesMax(byte[] buffer,byte max){for(int i =0; i < buffer.Length; i++){while(buffer[i]>= max)
buffer[i]=UnsafeGetByte();//Replace all bytes which are equal or above max}}}
W przypadku historii - moje starsze rozwiązanie dla tej odpowiedzi wykorzystałem obiekt Random:
privatestaticchar[] charSet ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();static rGen =newRandom();//Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.staticint byteSize =256;//Labelling conveniencestaticint biasZone = byteSize -(byteSize % charSet.Length);staticboolSlightlyMoreSecurityNeeded=true;//Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.publicstringGenerateRandomString(intLength)//Configurable output string length{byte[] rBytes =newbyte[Length];//Do as much before and after lock as possiblechar[] rName =newchar[Length];lock(rGen)//~20-50ns{
rGen.NextBytes(rBytes);for(int i =0; i <Length; i++){while(SlightlyMoreSecurityNeeded&& rBytes[i]>= biasZone)//Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
rBytes[i]= rGen.NextByte();
rName[i]= charSet[rBytes[i]% charSet.Length];}}returnnewstring(rName);}
Wydajność:
SecureFastRandom - Pierwsze pojedyncze uruchomienie = ~ 9-33 ms . Niedostrzegalny. W toku : 5 ms (czasem dochodzi do 13 ms ) w ciągu 10 000 iteracji, z pojedynczą średnią iteracją = 1,5 mikrosekundy.. Uwaga: Wymaga ogólnie 2, ale czasami do 8 odświeżeń pamięci podręcznej - zależy od liczby pojedynczych bajtów przekraczających strefę odchylenia
Losowo - pierwszy pojedynczy przebieg = ~ 0-1 ms . Niedostrzegalny. W toku : 5 ms ponad 10 000 iteracji. Z pojedynczą średnią iteracją = 0,5 mikrosekundy. . O tej samej prędkości.
Te linki to inne podejście. Buforowanie można dodać do tej nowej bazy kodu, ale najważniejsze było zbadanie różnych podejść do usuwania stronniczości oraz porównanie prędkości i zalet / wad.
1) Dlaczego wszystkie te magiczne stałe? Podałeś długość wyjściową trzy razy. Po prostu zdefiniuj go jako stałą lub parametr. Możesz użyć charSet.Lengthzamiast 62. 2) Statyczny Randombez blokady oznacza, że ten kod nie jest bezpieczny dla wątków. 3) zmniejszenie 0-255 mod 62 wprowadza wykrywalne odchylenie. 4) Nie możesz używać ToStringna tablicy char, która zawsze zwraca "System.Char[]". Musisz użyć new String(rName)zamiast tego.
CodesInChaos
Dzięki @CodesInChaos, nigdy nie myślałem o tych rzeczach. Nadal używam tylko klasy Losowej, ale powinno być lepiej. Nie mogłem wymyślić lepszego sposobu na wykrycie i skorygowanie danych wyjściowych.
Todd,
Trochę głupio jest zacząć od słabej RNG ( System.Random), a następnie ostrożnie unikać stronniczości we własnym kodzie. Przychodzi na myśl wyrażenie „polerowanie gówna”.
CodesInChaos
@CodesInChaos A teraz uczeń przekroczył swojego mistrza
Todda
4
Okropne, wiem, ale po prostu nie mogłem się powstrzymać:
namespace ConsoleApplication2{
using System;
using System.Text.RegularExpressions;classProgram{staticvoidMain(string[] args){Random adomRng =newRandom();string rndString =string.Empty;char c;for(int i =0; i <8; i++){while(!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(),"[A-Za-z0-9]"));
rndString += c;}Console.WriteLine(rndString +Environment.NewLine);}}}
Szukałem bardziej szczegółowej odpowiedzi, w której chcę kontrolować format losowego ciągu i natknąłem się na ten post. Na przykład: tablice rejestracyjne (samochodów) mają określony format (w zależności od kraju) i chciałem stworzyć losowe tablice rejestracyjne.
Postanowiłem do tego napisać własną metodę rozszerzenia Random. (ma to na celu ponowne użycie tego samego obiektu losowego, ponieważ w scenariuszach wielowątkowych można mieć podwójne). Utworzyłem listę ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), ale skopiuję tutaj również klasę rozszerzenia:
voidMain(){Random rnd =newRandom();
rnd.GetString("1-###-000").Dump();}publicstaticclassRandomExtensions{publicstaticstringGetString(thisRandom random,string format){// Based on http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c// Added logic to specify the format of the random string (# will be random string, 0 will be random numeric, other characters remain)StringBuilder result =newStringBuilder();for(int formatIndex =0; formatIndex < format.Length; formatIndex++){switch(format.ToUpper()[formatIndex]){case'0': result.Append(getRandomNumeric(random));break;case'#': result.Append(getRandomCharacter(random));break;default: result.Append(format[formatIndex]);break;}}return result.ToString();}privatestaticchar getRandomCharacter(Random random){string chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZ";return chars[random.Next(chars.Length)];}privatestaticchar getRandomNumeric(Random random){string nums ="0123456789";return nums[random.Next(nums.Length)];}}
Używanie właściwości do czegoś, co zmienia się przy każdym dostępie, jest raczej wątpliwe. Zamiast tego poleciłbym metodę.
CodesInChaos
2
RNGCryptoServiceProviderpo użyciu należy je usunąć.
Tsahi Asher,
Naprawiłem problem IDisposable, ale nadal jest to bardzo wątpliwe, tworząc nowy RNGCryptoServiceProvider dla każdej litery.
piojo,
@CodesInChaos gotowe, teraz metoda.
Matas Vaitkevicius
3
Spróbuj połączyć dwie części: unikalną (sekwencję, licznik lub datę) i losową
publicclassRandomStringGenerator{publicstaticstringGen(){returnConvertToBase(DateTime.UtcNow.ToFileTimeUtc())+GenRandomStrings(5);//keep length fixed at least of one part}privatestaticstringGenRandomStrings(int strLen){var result =string.Empty;varGen=newRNGCryptoServiceProvider();var data =newbyte[1];while(result.Length< strLen){Gen.GetNonZeroBytes(data);int code = data[0];if(code >48&& code <57||// 0-9
code >65&& code <90||// A-Z
code >97&& code <122// a-z){
result +=Convert.ToChar(code);}}return result;}privatestaticstringConvertToBase(long num,int nbase =36){var chars ="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//if you wish make algorithm more secure - change order of letter here// check if we can convert to another baseif(nbase <2|| nbase > chars.Length)returnnull;int r;var newNumber =string.Empty;// in r we have the offset of the char that was converted to the new basewhile(num >= nbase){
r =(int)(num % nbase);
newNumber = chars[r]+ newNumber;
num = num / nbase;}// the last number to convert
newNumber = chars[(int)num]+ newNumber;return newNumber;}}
1) Możesz użyć literałów znakowych zamiast wartości ASCII powiązanych z tymi znakami. 2) Występuje błąd „jeden po drugim” w kodzie dopasowania przedziału czasu. Musisz użyć <=i >=zamiast <i >. 3) Dodałbym niepotrzebne nawiasy wokół &&wyrażeń, aby wyjaśnić, że mają one pierwszeństwo, ale oczywiście jest to tylko wybór stylistyczny.
CodesInChaos
+ 1 Dobra do usuwania stronniczości i dodawania testów. Nie jestem jednak pewien, dlaczego przygotowujesz swój losowy ciąg za pomocą łańcucha pochodzącego ze znacznika czasu? Ponadto nadal musisz pozbyć się swojego RNGCryptoServiceProvider
monty
2
Rozwiązanie bez użycia Random:
var chars =Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",8);var randomStr =newstring(chars.SelectMany(str => str).OrderBy(c =>Guid.NewGuid()).Take(8).ToArray());
NewGuid używa losowo wewnętrznie. Więc nadal używa losowego, po prostu to ukrywa.
Wedge
2
Oto wariant rozwiązania Erica J, tj. Dźwięk kryptograficzny, dla WinRT (aplikacja ze Sklepu Windows):
publicstaticstringGenerateRandomString(int length){var chars ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";var result =newStringBuilder(length);for(int i =0; i < length;++i){
result.Append(CryptographicBuffer.GenerateRandomNumber()% chars.Length);}return result.ToString();}
Jeśli wydajność ma znaczenie (szczególnie gdy długość jest wysoka):
publicstaticstringGenerateRandomString(int length){var chars ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";var result =newSystem.Text.StringBuilder(length);var bytes =CryptographicBuffer.GenerateRandom((uint)length *4).ToArray();for(int i =0; i < bytes.Length; i +=4){
result.Append(BitConverter.ToUInt32(bytes, i)% chars.Length);}return result.ToString();}
To nie jest dźwięk kryptograficzny. Istnieje niewielki błąd wynikający z działania modułu, który nie rozkłada równomiernie całej szerokości ulong na 62 znaki.
Lie Ryan
1
Wiem, że to nie jest najlepszy sposób. Ale możesz spróbować.
string str =Path.GetRandomFileName();//This method returns a random file name of 11 characters
str = str.Replace(".","");Console.WriteLine("Random string: "+ str);
Jak tam ta jedna linia? Console.WriteLine ($ "Losowy ciąg: {Path.GetRandomFileName (). Zastąp (". "," ")}"); jest jedna linia.
PmanAce
1
Nie wiem, jak kryptograficznie to brzmi, ale jest on bardziej czytelny i zwięzły niż bardziej skomplikowane rozwiązania (imo) i powinien być bardziej „losowy” niż System.Randomoparty na rozwiązaniach.
To prawda, że nie jest zoptymalizowany pod kątem szybkości, więc jeśli misja ma generować miliony losowych ciągów co sekundę, spróbuj innego!
UWAGA: To rozwiązanie nie pozwala na powtarzanie symboli w alfabecie, a alfabet MUSI mieć taki sam lub większy rozmiar niż ciąg wyjściowy, co sprawia, że takie podejście jest mniej pożądane w niektórych okolicznościach, wszystko zależy od przypadku użycia.
Jeśli twoje wartości nie są całkowicie losowe, ale w rzeczywistości mogą zależeć od czegoś - możesz obliczyć skrót md5 lub sha1 tego „czegoś”, a następnie skrócić go do dowolnej długości.
1) metody statyczne powinny być bezpieczne dla wątków. 2) Po co zwiększać indeks zamiast korzystać random.Nextbezpośrednio z wyniku ? Komplikuje kod i nie osiąga niczego użytecznego.
CodesInChaos
0
Oto mechanizm generowania losowego ciągu alfanumerycznego (używam go do generowania haseł i danych testowych) bez definiowania alfabetu i liczb,
CleanupBase64 usunie niezbędne części ciągu i będzie rekurencyjnie dodawał losowe litery alfanumeryczne.
publicstaticstringGenerateRandomString(int length){var numArray =newbyte[length];newRNGCryptoServiceProvider().GetBytes(numArray);returnCleanUpBase64String(Convert.ToBase64String(numArray), length);}privatestaticstringCleanUpBase64String(string input,int maxLength){
input = input.Replace("-","");
input = input.Replace("=","");
input = input.Replace("/","");
input = input.Replace("+","");
input = input.Replace(" ","");while(input.Length< maxLength)
input = input +GenerateRandomString(maxLength);return input.Length<= maxLength ?
input.ToUpper()://In my case I want capital letters
input.ToUpper().Substring(0, maxLength);}
Zadeklarowałeś GenerateRandomStringi wykonałeś połączenie GetRandomStringz wewnątrz SanitiseBase64String. Również zostały uznane SanitiseBase64String i wywołanie CleanUpBase64Stringw GenerateRandomString.
Wai Ha Lee
0
Jest jeden z niesamowitych pakietów nugetów , które sprawiają, że jest to takie proste.
var myObject =newFaker<MyObject>().RuleFor(p => p.MyAlphaNumericProperty, f => f.Random.AlphaNumeric(/*lenght*/7)).Generate();
nie jestem w 100% pewien, ponieważ nie testowałem KAŻDEJ opcji tutaj, ale spośród tych, które testowałem, ta jest najszybsza. odmierzył czas stoperem i pokazał 9-10 tyknięć, więc jeśli prędkość jest ważniejsza niż bezpieczeństwo, spróbuj tego:
privatestaticRandom random =newRandom();publicstaticstringRandom(int length){var stringChars =newchar[length];for(int i =0; i < length; i++){
stringChars[i]=(char)random.Next(0x30,0x7a);returnnewstring(stringChars);}}
Random
klasie do generowania haseł. SiewRandom
ma bardzo niską entropię, więc nie jest tak naprawdę bezpieczny. Użyj hasła kryptograficznego PRNG.Odpowiedzi:
Słyszałem, że LINQ to nowa czerń, więc oto moja próba użycia LINQ:
(Uwaga: użycie tej
Random
klasy sprawia, że nie nadaje się do jakichkolwiek kwestii związanych z bezpieczeństwem , takich jak tworzenie haseł lub tokenów. UżyjRNGCryptoServiceProvider
klasy, jeśli potrzebujesz silnego generatora liczb losowych.)źródło
return new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
Nie tak elegancki jak rozwiązanie Linq.
(Uwaga:
Random
czyni to klasanie nadaje się do jakichkolwiek kwestii związanych z bezpieczeństwem , takich jak tworzenie haseł lub tokenów. UżyjRNGCryptoServiceProvider
klasy, jeśli potrzebujesz silnego generatora liczb losowych.)źródło
GetRandomFileName
jest szybsze, ale nie pozwala na kontrolę używanych znaków, a maksymalna możliwa długość to 11 znaków.Guid
Rozwiązanie Douglasa jest błyskawiczne, ale postacie są ograniczone do A-F0-9, a maksymalna możliwa długość to 32 znaki.GetRandomFileName
ale wtedy (a) stracisz przewagę wydajności i (b) kod stanie się bardziej skomplikowany.System.Random
nie nadaje się do bezpieczeństwa.W przeciwieństwie do niektórych przedstawionych alternatyw, ta jest kryptograficznie solidna .
Na podstawie dyskusji o alternatywach tutaj i zaktualizowanej / zmodyfikowanej na podstawie poniższych komentarzy.
Oto mała uprząż testowa, która demonstruje rozkład znaków w starych i zaktualizowanych wynikach. W celu dogłębnej dyskusji na temat analizy losowości odwiedź random.org.
źródło
RNGCSP
?) Używanie mod do indeksowania wchars
tablicy oznacza, że otrzymasz stronnicze wyjście, chyba żechars.Length
stanie się dzielnikiem 256.4*maxSize
losowych bajtów, a następnie użycie(UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length
. Użyłbym równieżGetBytes
zamiastGetNonZeroBytes
. I w końcu możesz usunąć pierwsze połączenie zGetNonZeroBytes
. Nie wykorzystujesz jego wyniku.Rozwiązanie 1 - największy „zasięg” o najbardziej elastycznej długości
To rozwiązanie ma większy zasięg niż używanie GUID, ponieważ GUID ma kilka stałych bitów, które są zawsze takie same i dlatego nie są losowe, na przykład 13 znaków w hex jest zawsze „4” - przynajmniej w GUID wersji 6.
To rozwiązanie pozwala również generować ciąg o dowolnej długości.
Rozwiązanie 2 - Jedna linia kodu - odpowiednia dla maksymalnie 22 znaków
Nie można generować ciągów, dopóki Rozwiązanie 1, a ciąg nie ma tego samego zakresu z powodu ustalonych bitów w identyfikatorach GUID, ale w wielu przypadkach to wystarczy.
Rozwiązanie 3 - Nieco mniej kodu
Przeważnie trzymam to tutaj do celów historycznych. Zużywa nieco mniej kodu, co jest kosztem posiadania mniejszego zasięgu - ponieważ używa hexa zamiast base64, potrzeba więcej znaków do przedstawienia tego samego zakresu w porównaniu z innymi rozwiązaniami.
Co oznacza większą szansę na kolizję - przetestowanie go przy 100 000 iteracjach 8 ciągów znaków wygenerowało jeden duplikat.
źródło
Oto przykład, który ukradłem z przykładu Sama Allena w Dot Net Perls
Jeśli potrzebujesz tylko 8 znaków, użyj Path.GetRandomFileName () w przestrzeni nazw System.IO. Sam mówi, że użycie „Path.GetRandomFileName tutaj jest czasem lepsze, ponieważ używa RNGCryptoServiceProvider dla lepszej losowości. Jednak jest ograniczone do 11 losowych znaków”.
GetRandomFileName zawsze zwraca ciąg 12 znaków z kropką na 9 znaku. Musisz więc usunąć kropkę (ponieważ nie jest to przypadek), a następnie pobrać 8 znaków z ciągu. Właściwie możesz wziąć pierwsze 8 znaków i nie martwić się o kropkę.
PS: dzięki Sam
źródło
Głównymi celami mojego kodu są:
Pierwszą właściwość uzyskuje się, biorąc 64-bitową wartość modulo wielkości alfabetu. W przypadku małych alfabetów (takich jak 62 znaki z pytania) prowadzi to do nieznacznego błędu. Drugą i trzecią właściwość uzyskuje się za pomocą
RNGCryptoServiceProvider
zamiastSystem.Random
.źródło
Najprostszy:
Możesz uzyskać lepszą wydajność, jeśli na stałe kodujesz tablicę znaków i polegasz na
System.Random
:Jeśli kiedykolwiek będziesz się martwić, angielskie alfabety mogą się kiedyś zmienić i możesz stracić biznes, możesz uniknąć twardego kodowania, ale powinno działać nieco gorzej (porównywalne do
Path.GetRandomFileName
podejścia)Dwa ostatnie podejścia wyglądają lepiej, jeśli można je na
System.Random
przykład ustawić jako metodę rozszerzenia .źródło
chars.Select
jest bardzo brzydkie, ponieważ polega na tym, że rozmiar wyjściowy to co najwyżej rozmiar alfabetu.'a'.To('z')
podejście?chars.Select()
Weź (n) `działa tylko, jeślichars.Count >= n
. Wybór sekwencji, której tak naprawdę nie używasz, jest nieco nieintuicyjny, szczególnie z tym ukrytym ograniczeniem długości. Wolałbym użyćEnumerable.Range
lubEnumerable.Repeat
. 2) Komunikat o błędzie „Końcowy znak powinien być mniejszy niż początkowy znak” jest nieprawidłowy / nie ma gonot
.chars.Count
jest sposób> n
. Nie dostaję też nieintuicyjnej części. To sprawia, że wszystkie zastosowania sąTake
nieintuicyjne, prawda? Nie wierzę w to. Dzięki za wskazanie literówki.Tylko kilka porównań wydajności różnych odpowiedzi w tym wątku:
Metody i konfiguracja
Wyniki
Testowane w LinqPad. Dla łańcucha o rozmiarze 10 generuje:
I numery wydajności wydają się nieznacznie różnić, bardzo rzadko
NonOptimized
jest rzeczywiście szybciej, a czasemForLoop
iGenerateRandomString
przełączyć kto jest w czołówce.źródło
var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());
, gdzie zamieniasz naEachRandomizingMethod
... każdą metodęJedna linia kodu
Membership.GeneratePassword()
załatwia sprawę :)Oto demo tego samego.
źródło
Kod napisany przez Erica J. jest dość niechlujny (jasne jest, że pochodzi on sprzed 6 lat ... prawdopodobnie nie napisałby dzisiaj tego kodu), a nawet są pewne problemy.
Nieprawdziwe ... Hasło zawiera błąd (jak napisano w komentarzu),
bcdefgh
jest nieco bardziej prawdopodobny niż inne (a
nie dlatego, że przezGetNonZeroBytes
to nie generuje bajtów o wartości zero, więc błąd jest ponieważa
jest to zrównoważone), więc nie jest tak naprawdę kryptograficznie.To powinno rozwiązać wszystkie problemy.
źródło
Używamy również niestandardowego ciągu losowego, ale zaimplementowaliśmy go jako pomocnika łańcucha, dzięki czemu zapewnia on pewną elastyczność ...
Stosowanie
lub
źródło
Mój prosty kod jednoliniowy działa dla mnie :)
Aby rozwinąć ten ciąg dla dowolnej długości łańcucha
źródło
Inną opcją może być użycie Linqa i agregacja losowych znaków w budującym ciągi znaków.
źródło
Pytanie: Dlaczego mam marnować czas
Enumerable.Range
na pisanie zamiast pisania"ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789"
?Odpowiedź: Magiczne ciągi są ZŁE. Czy ktokolwiek zauważył, że nie ma „
I
w mojej strunie na górze ”? Moja matka nauczyła mnie nie używać magicznych sznurków z tego właśnie powodu ...nb 1: Jak wiele osób mówiło @dtb, nie używaj
System.Random
jeśli potrzebujesz bezpieczeństwa kryptograficznego ...Uwaga 2: Ta odpowiedź nie jest najskuteczniejsza ani najkrótsza, ale chciałem, aby przestrzeń oddzieliła odpowiedź od pytania. Celem mojej odpowiedzi jest bardziej ostrzeżenie przed magicznymi łańcuchami niż dostarczenie wymyślnej, innowacyjnej odpowiedzi.
źródło
I
?”[A-Z0-9]
. Jeśli przypadkowo ciąg losowy obejmuje[A-HJ-Z0-9]
tylko wynik, nie obejmuje on pełnego dopuszczalnego zakresu, co może być problematyczne.I
. Czy to dlatego, że jest o jedną postać mniej, a to ułatwia złamanie? Jakie są statystyki haseł do zgryzania, które zawierają 35 znaków w zakresie niż 36. Myślę, że wolałbym zaryzykować ... lub po prostu sprawdzić zakres znaków ... niż zawrzeć wszystkie dodatkowe śmieci w moim kodzie. Ale to ja. To znaczy, żeby nie być dziurą w tyłku, mówię tylko. Czasami myślę, że programiści mają tendencję do wybierania bardzo skomplikowanej drogi ze względu na to, że są bardzo skomplikowani.I
iO
z tego typu losowych ciągów, aby uniknąć pomylenia ich przez ludzi z1
i0
. Jeśli nie zależy ci na łańcuchu czytelnym dla człowieka, dobrze, ale jeśli jest to coś, co ktoś może potrzebować wpisać, to naprawdę mądrze jest usunąć te znaki.Nieco czystsza wersja rozwiązania DTB.
Twoje preferencje dotyczące stylu mogą się różnić.
źródło
źródło
Po przejrzeniu innych odpowiedzi i rozważeniu komentarzy CodeInChaos, a także CodeInChaos wciąż stronniczej (choć mniejszej) odpowiedzi, pomyślałem, że konieczne jest ostateczne rozwiązanie cięcia i wklejania . Tak więc, aktualizując moją odpowiedź, postanowiłem wyjść na całość.
Aby uzyskać aktualną wersję tego kodu, odwiedź nowe repozytorium Hg na Bitbucket: https://bitbucket.org/merarischroeder/secureswiftrandom . Zalecam skopiowanie i wklejenie kodu z: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?atevdefault. (upewnij się, kliknij przycisk Raw, aby ułatwić kopiowanie i upewnić się, że masz najnowszą wersję, myślę, że ten link prowadzi do konkretnej wersji kodu, a nie najnowszej).
Zaktualizowane uwagi:
Zakończ rozwiązanie pytania:
Ale potrzebujesz mojej nowej (nieprzetestowanej) klasy:
W przypadku historii - moje starsze rozwiązanie dla tej odpowiedzi wykorzystałem obiekt Random:
Wydajność:
Sprawdź także:
Te linki to inne podejście. Buforowanie można dodać do tej nowej bazy kodu, ale najważniejsze było zbadanie różnych podejść do usuwania stronniczości oraz porównanie prędkości i zalet / wad.
źródło
charSet.Length
zamiast62
. 2) StatycznyRandom
bez blokady oznacza, że ten kod nie jest bezpieczny dla wątków. 3) zmniejszenie 0-255 mod 62 wprowadza wykrywalne odchylenie. 4) Nie możesz używaćToString
na tablicy char, która zawsze zwraca"System.Char[]"
. Musisz użyćnew String(rName)
zamiast tego.System.Random
), a następnie ostrożnie unikać stronniczości we własnym kodzie. Przychodzi na myśl wyrażenie „polerowanie gówna”.Okropne, wiem, ale po prostu nie mogłem się powstrzymać:
źródło
Szukałem bardziej szczegółowej odpowiedzi, w której chcę kontrolować format losowego ciągu i natknąłem się na ten post. Na przykład: tablice rejestracyjne (samochodów) mają określony format (w zależności od kraju) i chciałem stworzyć losowe tablice rejestracyjne.
Postanowiłem do tego napisać własną metodę rozszerzenia Random. (ma to na celu ponowne użycie tego samego obiektu losowego, ponieważ w scenariuszach wielowątkowych można mieć podwójne). Utworzyłem listę ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 ), ale skopiuję tutaj również klasę rozszerzenia:
źródło
Teraz w smaku jednowarstwowym.
źródło
RNGCryptoServiceProvider
po użyciu należy je usunąć.Spróbuj połączyć dwie części: unikalną (sekwencję, licznik lub datę) i losową
Testy:
źródło
<=
i>=
zamiast<
i>
. 3) Dodałbym niepotrzebne nawiasy wokół&&
wyrażeń, aby wyjaśnić, że mają one pierwszeństwo, ale oczywiście jest to tylko wybór stylistyczny.Rozwiązanie bez użycia
Random
:źródło
Oto wariant rozwiązania Erica J, tj. Dźwięk kryptograficzny, dla WinRT (aplikacja ze Sklepu Windows):
Jeśli wydajność ma znaczenie (szczególnie gdy długość jest wysoka):
źródło
Wiem, że to nie jest najlepszy sposób. Ale możesz spróbować.
źródło
Nie wiem, jak kryptograficznie to brzmi, ale jest on bardziej czytelny i zwięzły niż bardziej skomplikowane rozwiązania (imo) i powinien być bardziej „losowy” niż
System.Random
oparty na rozwiązaniach.Nie mogę zdecydować, czy uważam, że ta wersja czy następna jest „ładniejsza”, ale dają dokładnie takie same wyniki:
To prawda, że nie jest zoptymalizowany pod kątem szybkości, więc jeśli misja ma generować miliony losowych ciągów co sekundę, spróbuj innego!
UWAGA: To rozwiązanie nie pozwala na powtarzanie symboli w alfabecie, a alfabet MUSI mieć taki sam lub większy rozmiar niż ciąg wyjściowy, co sprawia, że takie podejście jest mniej pożądane w niektórych okolicznościach, wszystko zależy od przypadku użycia.
źródło
Jeśli twoje wartości nie są całkowicie losowe, ale w rzeczywistości mogą zależeć od czegoś - możesz obliczyć skrót md5 lub sha1 tego „czegoś”, a następnie skrócić go do dowolnej długości.
Możesz także wygenerować i obciąć GUID.
źródło
źródło
random.Next
bezpośrednio z wyniku ? Komplikuje kod i nie osiąga niczego użytecznego.Oto mechanizm generowania losowego ciągu alfanumerycznego (używam go do generowania haseł i danych testowych) bez definiowania alfabetu i liczb,
CleanupBase64 usunie niezbędne części ciągu i będzie rekurencyjnie dodawał losowe litery alfanumeryczne.
źródło
GenerateRandomString
i wykonałeś połączenieGetRandomString
z wewnątrzSanitiseBase64String
. Również zostały uznaneSanitiseBase64String
i wywołanieCleanUpBase64String
wGenerateRandomString
.Jest jeden z niesamowitych pakietów nugetów , które sprawiają, że jest to takie proste.
Jeden z dobrych przykładów jest tutaj .
źródło
nie jestem w 100% pewien, ponieważ nie testowałem KAŻDEJ opcji tutaj, ale spośród tych, które testowałem, ta jest najszybsza. odmierzył czas stoperem i pokazał 9-10 tyknięć, więc jeśli prędkość jest ważniejsza niż bezpieczeństwo, spróbuj tego:
źródło