Generowanie losowych haseł

229

Gdy użytkownik naszej witryny utraci hasło i przejdzie na stronę Utracone hasło, musimy mu dać nowe hasło tymczasowe. Nie mam nic przeciwko temu, jak losowa jest ta sytuacja, lub jeśli pasuje do wszystkich „potrzebnych” silnych reguł haseł, wszystko, co chcę zrobić, to dać im hasło, które mogą zmienić później.

Aplikacja to aplikacja internetowa napisana w języku C #. więc myślałem o byciu wrednym i podążaniu łatwą drogą korzystania z części Guida. to znaczy

Guid.NewGuid().ToString("d").Substring(1,8)

Suggesstions? myśli?

FryHard
źródło
12
Kilka dobrych rozwiązań tutaj, ale mała rada: Nie generuj haseł zawierających którekolwiek z tych znaków: Oo0Ili (rozumiesz dlaczego) :)
stian.net
2
Dodałem odpowiedź, która używa KeePass jako generatora haseł, a spośród wielu ujawnionych opcji zawarłem również opcję wykluczenia wyglądu podobnych znaków, jak wspomniano przez @ stian.net.
Peter

Odpowiedzi:

570

Zawsze System.Web.Security.Membership.GeneratePassword(int length, int numberOfNonAlphanumericCharacters) .

Rik
źródło
7
Nie wiedziałem, że Framework ma taką metodę! Niesamowite! Wymienię na to mój obecny kod!
FryHard
35
Znalazłem go po prawie dniu doskonalenia własnego kodu pw gen. Wyobraźcie sobie, jak się czułem;)
Rik,
16
AFAIK ta metoda nie generuje hasła zgodnego z polityką haseł w domenie, więc nie nadaje się do każdego użycia.
teebot
19
Główny problem z tym rozwiązaniem polega na tym, że nie można kontrolować zestawu znaków, więc nie można wyeliminować wizualnie niejednoznacznych znaków (0oOl1i! |), Co może być naprawdę ważne w praktyce.
David Hammond
20
Po co ASP.NET Core?
shashwat
114
public string CreatePassword(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();
}

Ma to tę zaletę, że można wybrać z listy dostępnych znaków dla wygenerowanego hasła (np. Tylko cyfry, tylko wielkie lub tylko małe litery itp.)

Radu094
źródło
2
ta metoda (podstawa 62) jest lepsza niż GUID (podstawa 16) pod względem siły: 8-znakowy ciąg szesnastkowy jest równoważny 4-5-znakowemu alfanumerycznemu
Jimmy
57
Randomnie jest kryptograficznie bezpieczny; System.Security.Cryptography.RNGCryptoServiceProviderjest lepszym wyborem.
anaximander
3
Generowałoby to to samo hasło przy każdym wywołaniu metody, ponieważ klasa Random jest tworzona za każdym razem. Można to zabezpieczyć, usuwając Random z tej metody i ponownie wykorzystując instancję.
Jon
6
Nie zrobiłby tego. Chyba że dwie osoby zdecydują się zmienić hasło w tym samym czasie.
Radu094,
10
cytat z pytania: nie przejmuj się ”, jeśli pasuje do wszystkich„ potrzebnych ”silnych reguł hasła” ... dziękuję za zanotowanie tej odpowiedzi
Radu094
35

Głównymi celami mojego kodu są:

  1. Rozkład ciągów jest prawie równomierny (nie przejmuj się drobnymi odchyleniami, o ile są małe)
  2. 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.
  3. 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;

public static string GetRandomAlphanumericString(int length)
{
    const string alphanumericCharacters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "abcdefghijklmnopqrstuvwxyz" +
        "0123456789";
    return GetRandomString(length, alphanumericCharacters);
}

public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
    if (length < 0)
        throw new ArgumentException("length must not be negative", "length");
    if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
        throw new ArgumentException("length is too big", "length");
    if (characterSet == null)
        throw new ArgumentNullException("characterSet");
    var characterArray = characterSet.Distinct().ToArray();
    if (characterArray.Length == 0)
        throw new ArgumentException("characterSet must not be empty", "characterSet");

    var bytes = new byte[length * 8];
    new RNGCryptoServiceProvider().GetBytes(bytes);
    var result = new char[length];
    for (int i = 0; i < length; i++)
    {
        ulong value = BitConverter.ToUInt64(bytes, i * 8);
        result[i] = characterArray[value % (uint)characterArray.Length];
    }
    return new string(result);
}

(To jest kopia mojej odpowiedzi na Jak wygenerować losowy ciąg 8 znaków alfanumerycznych w C #? )

CodesInChaos
źródło
1
Jeśli UInt64.MaxValue nie jest równomiernie podzielny przez characterArray.Length, losowo wybrane znaki nie będą równomiernie rozmieszczone (choć będzie to bardzo niewielki efekt).
Jeff Walker Code Ranger
1
@JeffWalkerCodeRanger Właśnie dlatego powiedziałem nieznaczne odchylenie, a nie brak odchylenia. Nawet przy petabajcie mocy wyjściowej masz mniej niż 1%, aby odróżnić to od całkowicie bezstronnego generatora strun. Dodatkowa złożoność idealnego rozproszenia wyraźnie nie jest warta raczej teoretycznego przyrostu przypadkowości.
CodesInChaos
9
Dla tych, którzy używają .NET Core, zastąp „nowy RNGCryptoServiceProvider (). GetBytes (bytes);” z „System.Security.Cryptography.RandomNumberGenerator.Create (). GetBytes (bytes);”
NPNelson
24
public string GenerateToken(int length)
{
    using (RNGCryptoServiceProvider cryptRNG = new RNGCryptoServiceProvider())
    {
        byte[] tokenBuffer = new byte[length];
        cryptRNG.GetBytes(tokenBuffer);
        return Convert.ToBase64String(tokenBuffer);
    }
}

(Możesz również mieć klasę, w której żyje ta metoda, zaimplementować IDisposable, przechowywać odniesienie do RNGCryptoServiceProvideri usuwać je odpowiednio, aby uniknąć wielokrotnego tworzenia instancji.)

Zauważono, że ponieważ zwraca łańcuch 64, długość wyjściowa jest zawsze wielokrotnością 4, a dodatkowe spacje są używane =jako znak dopełniający. lengthParametr określa długość bufora bajtów, a nie ciąg wyjściowy (iw związku z tym może nie najlepsza nazwa dla tego parametru, teraz myślę o tym). Kontroluje to, ile bajtów entropii będzie miało hasło. Ponieważ jednak base-64 używa 4-znakowego bloku do kodowania każdego 3 bajtu danych wejściowych, jeśli poprosisz o długość, która nie jest wielokrotnością liczby 3, pojawi się dodatkowe „spacja” i użyje= do wypełnienia dodatkowy.

Jeśli nie lubisz używać ciągów base-64 z jakiegokolwiek powodu, możesz zastąpić Convert.ToBase64String()wywołanie konwersją na ciąg zwykły lub dowolną z Encodingmetod; na przykład. Encoding.UTF8.GetString(tokenBuffer)- po prostu upewnij się, że wybierasz zestaw znaków, który może reprezentować pełny zakres wartości wychodzących z RNG, i który produkuje znaki, które są kompatybilne z każdym miejscem, w którym je wysyłasz lub przechowujesz. Na przykład użycie Unicode daje dużo chińskich znaków. Korzystanie z base-64 gwarantuje szeroki kompatybilny zestaw znaków, a charakterystyka takiego łańcucha nie powinna sprawiać, że będzie mniej bezpieczny, pod warunkiem, że użyjesz porządnego algorytmu haszującego.

anaksymander
źródło
Myślę, że masz zamiar umieścić tokenBuffer tam, gdzie masz linkBuf.
PIntag 18.04.15
Gdy używam tego kodu i przekazuję długość 10, zwracany ciąg ma zawsze 16 znaków, a ostatnie 2 znaki to zawsze „==”. Czy używam go nieprawidłowo? Czy długość jest podana w postaci szesnastkowej?
PIntag
1
Podana długość jest liczbą bajtów losowości (lub „entropią”, jak jest to technicznie znane). Jednak zwracana wartość jest zakodowana w formacie base-64, co oznacza, że ​​z powodu działania kodowania base-64 długość wyjściowa jest zawsze wielokrotnością 4. Czasami jest to więcej znaków niż potrzeba do zakodowania wszystkich znaków, więc używa = znaki, aby uzupełnić resztę.
anaximander
1
RNGCryptoServiceProvider jest IDisposable, więc zaimplementowałbym go za pomocą wzorca using (tj. Używając (var cryptRNG = new RNGCryptoServiceProvider ()) {...})
kloarubeek
@kloarubeek Ważny punkt i dobra sugestia. Dodałem to do mojej próbki kodu.
anaximander
20

Jest o wiele większy, ale myślę, że wygląda nieco bardziej kompleksowo: http://www.obviex.com/Samples/Password.aspx

///////////////////////////////////////////////////////////////////////////////
// SAMPLE: Generates random password, which complies with the strong password
//         rules and does not contain ambiguous characters.
//
// To run this sample, create a new Visual C# project using the Console
// Application template and replace the contents of the Class1.cs file with
// the code below.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// 
// Copyright (C) 2004 Obviex(TM). All rights reserved.
// 
using System;
using System.Security.Cryptography;

/// <summary>
/// This class can generate random passwords, which do not include ambiguous 
/// characters, such as I, l, and 1. The generated password will be made of
/// 7-bit ASCII symbols. Every four characters will include one lower case
/// character, one upper case character, one number, and one special symbol
/// (such as '%') in a random order. The password will always start with an
/// alpha-numeric character; it will not start with a special symbol (we do
/// this because some back-end systems do not like certain special
/// characters in the first position).
/// </summary>
public class RandomPassword
{
    // Define default min and max password lengths.
    private static int DEFAULT_MIN_PASSWORD_LENGTH  = 8;
    private static int DEFAULT_MAX_PASSWORD_LENGTH  = 10;

    // Define supported password characters divided into groups.
    // You can add (or remove) characters to (from) these groups.
    private static string PASSWORD_CHARS_LCASE  = "abcdefgijkmnopqrstwxyz";
    private static string PASSWORD_CHARS_UCASE  = "ABCDEFGHJKLMNPQRSTWXYZ";
    private static string PASSWORD_CHARS_NUMERIC= "23456789";
    private static string PASSWORD_CHARS_SPECIAL= "*$-+?_&=!%{}/";

    /// <summary>
    /// Generates a random password.
    /// </summary>
    /// <returns>
    /// Randomly generated password.
    /// </returns>
    /// <remarks>
    /// The length of the generated password will be determined at
    /// random. It will be no shorter than the minimum default and
    /// no longer than maximum default.
    /// </remarks>
    public static string Generate()
    {
        return Generate(DEFAULT_MIN_PASSWORD_LENGTH, 
                        DEFAULT_MAX_PASSWORD_LENGTH);
    }

    /// <summary>
    /// Generates a random password of the exact length.
    /// </summary>
    /// <param name="length">
    /// Exact password length.
    /// </param>
    /// <returns>
    /// Randomly generated password.
    /// </returns>
    public static string Generate(int length)
    {
        return Generate(length, length);
    }

    /// <summary>
    /// Generates a random password.
    /// </summary>
    /// <param name="minLength">
    /// Minimum password length.
    /// </param>
    /// <param name="maxLength">
    /// Maximum password length.
    /// </param>
    /// <returns>
    /// Randomly generated password.
    /// </returns>
    /// <remarks>
    /// The length of the generated password will be determined at
    /// random and it will fall with the range determined by the
    /// function parameters.
    /// </remarks>
    public static string Generate(int   minLength,
                                  int   maxLength)
    {
        // Make sure that input parameters are valid.
        if (minLength <= 0 || maxLength <= 0 || minLength > maxLength)
            return null;

        // Create a local array containing supported password characters
        // grouped by types. You can remove character groups from this
        // array, but doing so will weaken the password strength.
        char[][] charGroups = new char[][] 
        {
            PASSWORD_CHARS_LCASE.ToCharArray(),
            PASSWORD_CHARS_UCASE.ToCharArray(),
            PASSWORD_CHARS_NUMERIC.ToCharArray(),
            PASSWORD_CHARS_SPECIAL.ToCharArray()
        };

        // Use this array to track the number of unused characters in each
        // character group.
        int[] charsLeftInGroup = new int[charGroups.Length];

        // Initially, all characters in each group are not used.
        for (int i=0; i<charsLeftInGroup.Length; i++)
            charsLeftInGroup[i] = charGroups[i].Length;

        // Use this array to track (iterate through) unused character groups.
        int[] leftGroupsOrder = new int[charGroups.Length];

        // Initially, all character groups are not used.
        for (int i=0; i<leftGroupsOrder.Length; i++)
            leftGroupsOrder[i] = i;

        // Because we cannot use the default randomizer, which is based on the
        // current time (it will produce the same "random" number within a
        // second), we will use a random number generator to seed the
        // randomizer.

        // Use a 4-byte array to fill it with random bytes and convert it then
        // to an integer value.
        byte[] randomBytes = new byte[4];

        // Generate 4 random bytes.
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        rng.GetBytes(randomBytes);

        // Convert 4 bytes into a 32-bit integer value.
        int seed = BitConverter.ToInt32(randomBytes, 0);

        // Now, this is real randomization.
        Random  random  = new Random(seed);

        // This array will hold password characters.
        char[] password = null;

        // Allocate appropriate memory for the password.
        if (minLength < maxLength)
            password = new char[random.Next(minLength, maxLength+1)];
        else
            password = new char[minLength];

        // Index of the next character to be added to password.
        int nextCharIdx;

        // Index of the next character group to be processed.
        int nextGroupIdx;

        // Index which will be used to track not processed character groups.
        int nextLeftGroupsOrderIdx;

        // Index of the last non-processed character in a group.
        int lastCharIdx;

        // Index of the last non-processed group.
        int lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

        // Generate password characters one at a time.
        for (int i=0; i<password.Length; i++)
        {
            // If only one character group remained unprocessed, process it;
            // otherwise, pick a random character group from the unprocessed
            // group list. To allow a special character to appear in the
            // first position, increment the second parameter of the Next
            // function call by one, i.e. lastLeftGroupsOrderIdx + 1.
            if (lastLeftGroupsOrderIdx == 0)
                nextLeftGroupsOrderIdx = 0;
            else
                nextLeftGroupsOrderIdx = random.Next(0, 
                                                     lastLeftGroupsOrderIdx);

            // Get the actual index of the character group, from which we will
            // pick the next character.
            nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];

            // Get the index of the last unprocessed characters in this group.
            lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;

            // If only one unprocessed character is left, pick it; otherwise,
            // get a random character from the unused character list.
            if (lastCharIdx == 0)
                nextCharIdx = 0;
            else
                nextCharIdx = random.Next(0, lastCharIdx+1);

            // Add this character to the password.
            password[i] = charGroups[nextGroupIdx][nextCharIdx];

            // If we processed the last character in this group, start over.
            if (lastCharIdx == 0)
                charsLeftInGroup[nextGroupIdx] = 
                                          charGroups[nextGroupIdx].Length;
            // There are more unprocessed characters left.
            else
            {
                // Swap processed character with the last unprocessed character
                // so that we don't pick it until we process all characters in
                // this group.
                if (lastCharIdx != nextCharIdx)
                {
                    char temp = charGroups[nextGroupIdx][lastCharIdx];
                    charGroups[nextGroupIdx][lastCharIdx] = 
                                charGroups[nextGroupIdx][nextCharIdx];
                    charGroups[nextGroupIdx][nextCharIdx] = temp;
                }
                // Decrement the number of unprocessed characters in
                // this group.
                charsLeftInGroup[nextGroupIdx]--;
            }

            // If we processed the last group, start all over.
            if (lastLeftGroupsOrderIdx == 0)
                lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
            // There are more unprocessed groups left.
            else
            {
                // Swap processed group with the last unprocessed group
                // so that we don't pick it until we process all groups.
                if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
                {
                    int temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
                    leftGroupsOrder[lastLeftGroupsOrderIdx] = 
                                leftGroupsOrder[nextLeftGroupsOrderIdx];
                    leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
                }
                // Decrement the number of unprocessed groups.
                lastLeftGroupsOrderIdx--;
            }
        }

        // Convert password characters into a string and return the result.
        return new string(password);
     }
}

/// <summary>
/// Illustrates the use of the RandomPassword class.
/// </summary>
public class RandomPasswordTest
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // Print 100 randomly generated passwords (8-to-10 char long).
        for (int i=0; i<100; i++)
            Console.WriteLine(RandomPassword.Generate(8, 10));
    }
}
//
// END OF FILE
///////////////////////////////////////////////////////////////////////////////
GEOCHET
źródło
2
Okazuje się, że jest to wspierane przez framework. Więc raczej akceptuję tę odpowiedź!
FryHard
1
Generowanie tylko 2 ^ 31 różnych haseł, nawet przy dużych rozmiarach wyjściowych, jest nieco wadą. Może wystarczać do ataków online, ale z pewnością do ataków offline. => Nie poleciłbym tego.
CodesInChaos
To wciąż dobra odpowiedź, ponieważ „wbudowane” wsparcie to tak naprawdę członkostwo, a co jeśli zdecydujesz się nie używać członkostwa ASP.NET? Nadal działa, ponieważ zależność to System.Web.dll, ale jest to trochę niewygodne, ponieważ metoda nie jest samodzielna. @GEOCHET: Dziękujemy za opublikowanie tej alternatywy.
Chris Gomez
8

Wiem, że to stary wątek, ale mam coś, co może być dość prostym rozwiązaniem dla kogoś. Łatwy do wdrożenia, łatwy do zrozumienia i łatwy do sprawdzenia.

Rozważ następujące wymaganie:

Potrzebuję wygenerować losowe hasło, które ma co najmniej 2 małe litery, 2 duże litery i 2 cyfry. Hasło musi także mieć co najmniej 8 znaków.

Poniższe wyrażenie regularne może sprawdzić poprawność tego przypadku:

^(?=\b\w*[a-z].*[a-z]\w*\b)(?=\b\w*[A-Z].*[A-Z]\w*\b)(?=\b\w*[0-9].*[0-9]\w*\b)[a-zA-Z0-9]{8,}$

To jest poza zakresem tego pytania - ale regex jest oparty na lookahead / lookbehind i lookaround .

Poniższy kod utworzy losowy zestaw znaków spełniających to wymaganie:

public static string GeneratePassword(int lowercase, int uppercase, int numerics) {
    string lowers = "abcdefghijklmnopqrstuvwxyz";
    string uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    string number = "0123456789";

    Random random = new Random();

    string generated = "!";
    for (int i = 1; i <= lowercase; i++)
        generated = generated.Insert(
            random.Next(generated.Length), 
            lowers[random.Next(lowers.Length - 1)].ToString()
        );

    for (int i = 1; i <= uppercase; i++)
        generated = generated.Insert(
            random.Next(generated.Length), 
            uppers[random.Next(uppers.Length - 1)].ToString()
        );

    for (int i = 1; i <= numerics; i++)
        generated = generated.Insert(
            random.Next(generated.Length), 
            number[random.Next(number.Length - 1)].ToString()
        );

    return generated.Replace("!", string.Empty);

}

Aby spełnić powyższe wymagania, wystarczy wywołać następujące:

String randomPassword = GeneratePassword(3, 3, 3);

Kod zaczyna się od niepoprawnego znaku ("!" ) - dzięki czemu łańcuch ma długość, do której można wstawiać nowe znaki.

Następnie zapętla od 1 do wymaganej liczby małych liter, a przy każdej iteracji pobiera losowy element z listy małych liter i wstrzykuje go w losowe miejsce w ciągu.

Następnie powtarza pętlę dla wielkich liter i cyfr.

To daje ci ciągi znaków = długość, lowercase + uppercase + numericsw której małe, wielkie i liczbowe znaki liczby, które chcesz, zostały umieszczone w losowej kolejności.

Troy Alford
źródło
3
Nie używaj System.Randomdo krytycznych elementów bezpieczeństwa, takich jak hasła. UżyjRNGCryptoServiceProvider
CodesInChaos
lowers[random.Next(lowers.Length - 1)].ToString() Ten kod nigdy nie wygeneruje „z”. random.Next generuje liczby całkowite mniejsze niż podana liczba, więc nie należy odejmować tej dodatkowej od długości.
notbono
6

Do tego rodzaju hasła używam systemu, który prawdopodobnie generuje łatwiej „używane” hasła. Krótkie, często złożone z wymawianych fragmentów i kilku liczb, bez dwuznaczności między znakami (czy to 0 lub O? A 1 lub I?). Coś jak

string[] words = { 'bur', 'ler', 'meh', 'ree' };
string word = "";

Random rnd = new Random();
for (i = 0; i < 3; i++)
   word += words[rnd.Next(words.length)]

int numbCount = rnd.Next(4);
for (i = 0; i < numbCount; i++)
  word += (2 + rnd.Next(7)).ToString();

return word;

(Wpisane bezpośrednio w przeglądarce, więc używaj tylko jako wskazówek. Dodaj też więcej słów).

Adam Wright
źródło
6

Nie podoba mi się hasło, które tworzy Membership.GeneratePassword (), ponieważ są zbyt brzydkie i mają zbyt wiele znaków specjalnych.

Ten kod generuje 10-cyfrowe niezbyt brzydkie hasło.

string password = Guid.NewGuid().ToString("N").ToLower()
                      .Replace("1", "").Replace("o", "").Replace("0","")
                      .Substring(0,10);

Jasne, mógłbym użyć Regex, aby wykonać wszystkie zamiany, ale jest to bardziej czytelne i łatwe w utrzymaniu IMO.

Matt Frear
źródło
2
GUID nie powinien być nadużywany jako kryptograficzny PRNG
CodesInChaos
3
Jeśli zamierzasz użyć tej metody, możesz użyć .ToString („N”) i nie będziesz musiał zamieniać „-”. Nie ma również potrzeby zastępowania „l”, ponieważ nie jest to cyfra szesnastkowa.
JackAce
Całkowicie zobacz, dlaczego to zrobiłeś. Potrzebowałem tylko krótkotrwałego (niecałego dnia) hasła, które nie musi być zbyt unikalne dla każdego użytkownika. Usunięcie zer i jedynek to świetny sposób na usunięcie zamieszania. Po prostu przesunąłem mój na wielkie litery, a następnie skróciłem długość do 6. Podobnie jak sugestia „N”. Dzięki!
Bill Noel
6

ja stworzyłem tę klasę, która wykorzystuje RNGCryptoServiceProvider i jest elastyczna. Przykład:

var generator = new PasswordGenerator(minimumLengthPassword: 8,
                                      maximumLengthPassword: 15,
                                      minimumUpperCaseChars: 2,
                                      minimumNumericChars: 3,
                                      minimumSpecialChars: 2);
string password = generator.Generate();
Alex Siepman
źródło
To świetnie, jaka jest licencja na tę klasę? Mogę to użyć?
codeulike
Używaj go tak, jak chcesz!
Alex Siepman,
3

Stworzyłem tę metodę podobną do dostępnej w dostawcy członkostwa. Jest to przydatne, jeśli nie chcesz dodawać odwołania internetowego w niektórych aplikacjach.

Działa świetnie.

public static string GeneratePassword(int Length, int NonAlphaNumericChars)
    {
        string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";
        string allowedNonAlphaNum = "!@#$%^&*()_-+=[{]};:<>|./?";
        Random rd = new Random();

        if (NonAlphaNumericChars > Length || Length <= 0 || NonAlphaNumericChars < 0)
            throw new ArgumentOutOfRangeException();

            char[] pass = new char[Length];
            int[] pos = new int[Length];
            int i = 0, j = 0, temp = 0;
            bool flag = false;

            //Random the position values of the pos array for the string Pass
            while (i < Length - 1)
            {
                j = 0;
                flag = false;
                temp = rd.Next(0, Length);
                for (j = 0; j < Length; j++)
                    if (temp == pos[j])
                    {
                        flag = true;
                        j = Length;
                    }

                if (!flag)
                {
                    pos[i] = temp;
                    i++;
                }
            }

            //Random the AlphaNumericChars
            for (i = 0; i < Length - NonAlphaNumericChars; i++)
                pass[i] = allowedChars[rd.Next(0, allowedChars.Length)];

            //Random the NonAlphaNumericChars
            for (i = Length - NonAlphaNumericChars; i < Length; i++)
                pass[i] = allowedNonAlphaNum[rd.Next(0, allowedNonAlphaNum.Length)];

            //Set the sorted array values by the pos array for the rigth posistion
            char[] sorted = new char[Length];
            for (i = 0; i < Length; i++)
                sorted[i] = pass[pos[i]];

            string Pass = new String(sorted);

            return Pass;
    }
Hugo
źródło
6
Nie używaj System.Randomdo krytycznych elementów bezpieczeństwa, takich jak hasła. UżyjRNGCryptoServiceProvider
CodesInChaos
3

Zawsze byłem bardzo zadowolony z generatora haseł wbudowanego w KeePass. Ponieważ KeePass to program .Net i oprogramowanie typu open source, postanowiłem trochę rozejrzeć się po kodzie. Skończyło się na tym, że po prostu odświeżyłem KeePass.exe, kopię dostarczoną w standardowej instalacji aplikacji, jako odniesienie w moim projekcie i napisałem poniższy kod. Możesz zobaczyć, jak elastyczna jest dzięki KeePass. Możesz określić długość, które znaki dołączyć / nie uwzględnić itp.

using KeePassLib.Cryptography.PasswordGenerator;
using KeePassLib.Security;


public static string GeneratePassword(int passwordLength, bool lowerCase, bool upperCase, bool digits,
        bool punctuation, bool brackets, bool specialAscii, bool excludeLookAlike)
    {
        var ps = new ProtectedString();
        var profile = new PwProfile();
        profile.CharSet = new PwCharSet();
        profile.CharSet.Clear();

        if (lowerCase)
            profile.CharSet.AddCharSet('l');
        if(upperCase)
            profile.CharSet.AddCharSet('u');
        if(digits)
            profile.CharSet.AddCharSet('d');
        if (punctuation)
            profile.CharSet.AddCharSet('p');
        if (brackets)
            profile.CharSet.AddCharSet('b');
        if (specialAscii)
            profile.CharSet.AddCharSet('s');

        profile.ExcludeLookAlike = excludeLookAlike;
        profile.Length = (uint)passwordLength;
        profile.NoRepeatingCharacters = true;

        KeePassLib.Cryptography.PasswordGenerator.PwGenerator.Generate(out ps, profile, null, _pool);

        return ps.ReadString();
    }
Piotr
źródło
2
public static string GeneratePassword(int passLength) {
        var chars = "abcdefghijklmnopqrstuvwxyz@#$&ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        var random = new Random();
        var result = new string(
            Enumerable.Repeat(chars, passLength)
                      .Select(s => s[random.Next(s.Length)])
                      .ToArray());
        return result;
    }
powiedział Ahmad
źródło
Proszę wyjaśnić swoją odpowiedź
Linus
1
Ta funkcja działa bardzo dobrze z kilkoma zmianami. 1. Dodano entropię, jeśli wykonujesz wiele połączeń jednocześnie. Oznacza to nazwane Sleep () lub inną entropią powodującą funkcję między wywołaniami. 2. Zwiększ liczbę i losowość znaków źródłowych. Utworzyłem serię haseł o długości 500 znaków z kluczem, z wyłączeniem szeregu podobnych i innych znaków, których można było uciec, z którymi nie chciałem sobie poradzić i wykorzystałem wynikowy ciąg znaków 2000. Losowość po zaledwie 100 ms Entropii była całkiem niezła.
Wizengamot
2

Dodam kolejną niewłaściwą odpowiedź do puli.

Mam przypadek użycia, w którym potrzebuję losowych haseł do komunikacji maszyna-maszyna, więc nie mam żadnych wymagań dotyczących czytelności dla ludzi. Nie mam również dostępu do Membership.GeneratePasswordmojego projektu i nie chcę dodawać zależności.

Jestem pewien, że Membership.GeneratePasswordrobi coś podobnego do tego, ale tutaj możesz dostroić pulę postaci, z których będziesz czerpać.

public static class PasswordGenerator
{
    private readonly static Random _rand = new Random();

    public static string Generate(int length = 24)
    {
        const string lower = "abcdefghijklmnopqrstuvwxyz";
        const string upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        const string number = "1234567890";
        const string special = "!@#$%^&*_-=+";

        // Get cryptographically random sequence of bytes
        var bytes = new byte[length];
        new RNGCryptoServiceProvider().GetBytes(bytes);

        // Build up a string using random bytes and character classes
        var res = new StringBuilder();
        foreach(byte b in bytes)
        {
            // Randomly select a character class for each byte
            switch (_rand.Next(4))
            {
                // In each case use mod to project byte b to the correct range
                case 0:
                    res.Append(lower[b % lower.Count()]);
                    break;
                case 1:
                    res.Append(upper[b % upper.Count()]);
                    break;
                case 2:
                    res.Append(number[b % number.Count()]);
                    break;
                case 3:
                    res.Append(special[b % special.Count()]);
                    break;
            }
        }
        return res.ToString();
    }
}

I niektóre przykładowe dane wyjściowe:

PasswordGenerator.Generate(12)
"pzY=64@-ChS$"
"BG0OsyLbYnI_"
"l9#5^2&adj_i"
"#++Ws9d$%O%X"
"IWhdIN-#&O^s"

Aby zapobiec reklamacjom związanym z użyciem Random: Głównym źródłem losowości jest nadal kryptowalut RNG. Nawet gdybyś mógł deterministycznie z góry ustalić sekwencję wychodzącą Random(powiedzmy, że została wyprodukowana 1), nadal nie znałbyś następnego znaku, który zostanie wybrany (choć to ograniczy zakres możliwości).

Jednym prostym rozszerzeniem byłoby dodanie ważenia do różnych zestawów znaków, co może być tak proste, jak zwiększenie maksymalnej wartości i dodanie przypadkowych przypadków, aby zwiększyć wagę.

switch (_rand.Next(6))
{
    // Prefer letters 2:1
    case 0:
    case 1:
        res.Append(lower[b % lower.Count()]);
        break;
    case 2:
    case 3:
        res.Append(upper[b % upper.Count()]);
        break;
    case 4:
        res.Append(number[b % number.Count()]);
        break;
    case 5:
        res.Append(special[b % special.Count()]);
        break;
}

Dla bardziej humanistycznego generatora losowych haseł kiedyś zaimplementowałem system monitów, używając listy kości-słów EFF .

kitsu.eb
źródło
1

Lubię patrzeć na generowanie haseł, tak jak generowanie kluczy oprogramowania. Powinieneś wybierać spośród szeregu znaków, które przestrzegają dobrej praktyki. Weź to, na co @ Radu094 odpowiedział, i zmodyfikuj je, aby postępować zgodnie z dobrą praktyką. Nie umieszczaj każdej litery w tablicy znaków. Niektóre litery są trudniejsze do powiedzenia lub zrozumienia przez telefon.

Należy również rozważyć użycie sumy kontrolnej hasła, które zostało wygenerowane, aby upewnić się, że zostało wygenerowane przez Ciebie. Dobrym sposobem na osiągnięcie tego jest użycie algorytmu LUHN .

Dale Ragan
źródło
1

Oto, co szybko poskładałem.

    public string GeneratePassword(int len)
    {
        string res = "";
        Random rnd = new Random();
        while (res.Length < len) res += (new Func<Random, string>((r) => {
            char c = (char)((r.Next(123) * DateTime.Now.Millisecond % 123)); 
            return (Char.IsLetterOrDigit(c)) ? c.ToString() : ""; 
        }))(rnd);
        return res;
    }
Skyler Campbell
źródło
1

Używam tego kodu do generowania hasła z kompozycją bilansu znaków alfabetu, liczb i znaków innych niż alfanumeryczne.

public static string GeneratePassword(int Length, int NonAlphaNumericChars)
    {
        string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";
        string allowedNonAlphaNum = "!@#$%^&*()_-+=[{]};:<>|./?";
        string pass = "";
        Random rd = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < Length; i++)
        {
            if (rd.Next(1) > 0 && NonAlphaNumericChars > 0)
            {
                pass += allowedNonAlphaNum[rd.Next(allowedNonAlphaNum.Length)];
                NonAlphaNumericChars--;
            }
            else
            {
                pass += allowedChars[rd.Next(allowedChars.Length)];
            }
        }
        return pass;
    }
Roohi Ali
źródło
0

To jest krótkie i działa świetnie dla mnie.

public static string GenerateRandomCode(int length)
{
    Random rdm = new Random();
    StringBuilder sb = new StringBuilder();

    for(int i = 0; i < length; i++)
        sb.Append(Convert.ToChar(rdm.Next(101,132)));

    return sb.ToString();
}
użytkownik1058637
źródło
Może „działa”, ale z pewnością nie jest bezpieczne. Hasła muszą być bezpieczne.
CodesInChaos
Myślę, że losowe hasła są hasłami tymczasowymi. Nie rozumiem, dlaczego muszą być bezpieczne, a nawet jeśli tak, możesz dodawać liczby i znaki specjalne w zakresie.
user1058637
1
Dodanie liczb i znaków specjalnych nie poprawi bezpieczeństwa, jeśli wygenerujesz je przy użyciu przewidywalnego PRNG. Jeśli wiesz, kiedy hasło zostało wygenerowane, możesz zawęzić je tylko do kilku kandydatów.
CodesInChaos
0

Na mojej stronie używam tej metody:

    //Symb array
    private const string _SymbolsAll = "~`!@#$%^&*()_+=-\\|[{]}'\";:/?.>,<";

    //Random symb
    public string GetSymbol(int Length)
    {
        Random Rand = new Random(DateTime.Now.Millisecond);
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < Length; i++)
            result.Append(_SymbolsAll[Rand.Next(0, _SymbolsAll.Length)]);
        return result.ToString();
    }

Edytuj ciąg _SymbolsAlldla listy tablic.

GRUNGER
źródło
Jak już wspomniano w opisie edycji. Jeśli link do strony internetowej nie jest kodem referencyjnym, to jesteś reklamą, która czyni go spamem, więc usuń ten link z odpowiedzi.
Bowdzone
0

Dodano dodatkowy kod do zaakceptowanej odpowiedzi. Poprawia się po odpowiedzi przy użyciu Losowo i pozwala na pewne opcje hasła. Podobały mi się również niektóre opcje z odpowiedzi KeePass, ale nie chciałem uwzględniać pliku wykonywalnego w moim rozwiązaniu.

private string RandomPassword(int length, bool includeCharacters, bool includeNumbers, bool includeUppercase, bool includeNonAlphaNumericCharacters, bool includeLookAlikes)
{
    if (length < 8 || length > 128) throw new ArgumentOutOfRangeException("length");
    if (!includeCharacters && !includeNumbers && !includeNonAlphaNumericCharacters) throw new ArgumentException("RandomPassword-Key arguments all false, no values would be returned");

    string pw = "";
    do
    {
        pw += System.Web.Security.Membership.GeneratePassword(128, 25);
        pw = RemoveCharacters(pw, includeCharacters, includeNumbers, includeUppercase, includeNonAlphaNumericCharacters, includeLookAlikes);
    } while (pw.Length < length);

    return pw.Substring(0, length);
}

private string RemoveCharacters(string passwordString, bool includeCharacters, bool includeNumbers, bool includeUppercase, bool includeNonAlphaNumericCharacters, bool includeLookAlikes)
{
    if (!includeCharacters)
    {
        var remove = new string[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
        foreach (string r in remove)
        {
            passwordString = passwordString.Replace(r, string.Empty);
            passwordString = passwordString.Replace(r.ToUpper(), string.Empty);
        }
    }

    if (!includeNumbers)
    {
        var remove = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
        foreach (string r in remove)
            passwordString = passwordString.Replace(r, string.Empty);
    }

    if (!includeUppercase)
        passwordString = passwordString.ToLower();

    if (!includeNonAlphaNumericCharacters)
    {
        var remove = new string[] { "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "_", "+", "=", "{", "}", "[", "]", "|", "\\", ":", ";", "<", ">", "/", "?", "." };
        foreach (string r in remove)
            passwordString = passwordString.Replace(r, string.Empty);
    }

    if (!includeLookAlikes)
    {
        var remove = new string[] { "(", ")", "0", "O", "o", "1", "i", "I", "l", "|", "!", ":", ";" };
        foreach (string r in remove)
            passwordString = passwordString.Replace(r, string.Empty);
    }

    return passwordString;
}

To był pierwszy link, kiedy szukałem generowania losowych haseł, a poniższe pytanie jest poza zakresem bieżącego pytania, ale może być ważne do rozważenia.

  • W oparciu o założenie, że System.Web.Security.Membership.GeneratePasswordjest kryptograficznie bezpieczny, przy czym co najmniej 20% znaków nie jest alfanumeryczne.
  • Nie jestem pewien, czy usuwanie znaków i dodawanie ciągów jest w tym przypadku uważane za dobrą praktykę i zapewnia wystarczającą entropię.
  • Może warto rozważyć zastosowanie w jakiś sposób SecureString do bezpiecznego przechowywania haseł w pamięci.
Matt
źródło
Do twojej wiadomości, nie musisz dołączać pliku wykonywalnego KeyPass, ponieważ jest to open source (kod źródłowy jest dostępny do pobrania tutaj )
Alex Klaus
0

validChars może być dowolnym konstruktem, ale zdecydowałem się wybrać na podstawie zakresów kodu ascii usuwając znaki kontrolne. W tym przykładzie jest to ciąg 12 znaków.

string validChars = String.Join("", Enumerable.Range(33, (126 - 33)).Where(i => !(new int[] { 34, 38, 39, 44, 60, 62, 96 }).Contains(i)).Select(i => { return (char)i; }));
string.Join("", Enumerable.Range(1, 12).Select(i => { return validChars[(new Random(Guid.NewGuid().GetHashCode())).Next(0, validChars.Length - 1)]; }))
Sean
źródło
0
 Generate random password of specified length with 
  - Special characters   
  - Number
  - Lowecase
  - Uppercase

  public static string CreatePassword(int length = 12)
    {
        const string lower = "abcdefghijklmnopqrstuvwxyz";
        const string upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        const string number = "1234567890";
        const string special = "!@#$%^&*";

        var middle = length / 2;
        StringBuilder res = new StringBuilder();
        Random rnd = new Random();
        while (0 < length--)
        {
            if (middle == length)
            {
                res.Append(number[rnd.Next(number.Length)]);
            }
            else if (middle - 1 == length)
            {
                res.Append(special[rnd.Next(special.Length)]);
            }
            else
            {
                if (length % 2 == 0)
                {
                    res.Append(lower[rnd.Next(lower.Length)]);
                }
                else
                {
                    res.Append(upper[rnd.Next(upper.Length)]);
                }
            }
        }
        return res.ToString();
    }
akshay tilekar
źródło
Odpowiedzi tylko na kod nie są zachęcane, ponieważ nie zawierają zbyt wielu informacji dla przyszłych czytelników, proszę podać wyjaśnienie tego, co napisałeś
WhatsThePoint,
Tworzy to samo hasło w kółko. Muszę mieć instancję Randomjako statyczną
trailmax
Pamiętaj, że ta odpowiedź wygeneruje to samo hasło, jeśli zostanie wywołane wiele razy z rzędu !!! Należy dodać Entropy między rozmowami, jeśli chcesz użyć funkcji do przypisania haseł za pomocą kodu do wielu rekordów jednocześnie. Nadal jednak przydatne ....
Wizengamot
0

Ten pakiet pozwala wygenerować losowe hasło, jednocześnie płynnie wskazując, które znaki powinien zawierać (w razie potrzeby):

https://github.com/prjseal/PasswordGenerator/

Przykład:

var pwd = new Password().IncludeLowercase().IncludeUppercase().IncludeSpecial();
var password = pwd.Next();
Johan Maes
źródło
0

Jeśli chcesz skorzystać z kryptograficznie bezpiecznego generowania liczb losowych wykorzystywanego przez System.Web.Security.Membership.GeneratePassword, ale chcesz także ograniczyć zestaw znaków do znaków alfanumerycznych, możesz odfiltrować wynik za pomocą wyrażenia regularnego:

static string GeneratePassword(int characterCount)
{
    string password = String.Empty;
    while(password.Length < characterCount)
        password += Regex.Replace(System.Web.Security.Membership.GeneratePassword(128, 0), "[^a-zA-Z0-9]", string.Empty);
    return password.Substring(0, characterCount);
}
samgak
źródło
-3
public string Sifre_Uret(int boy, int noalfa)
{

    //  01.03.2016   
    // Genel amaçlı şifre üretme fonksiyonu


    //Fonskiyon 128 den büyük olmasına izin vermiyor.
    if (boy > 128 ) { boy = 128; }
    if (noalfa > 128) { noalfa = 128; }
    if (noalfa > boy) { noalfa = boy; }


    string passch = System.Web.Security.Membership.GeneratePassword(boy, noalfa);

    //URL encoding ve Url Pass + json sorunu yaratabilecekler pass ediliyor.
    //Microsoft Garanti etmiyor. Alfa Sayısallar Olabiliyorimiş . !@#$%^&*()_-+=[{]};:<>|./?.
    //https://msdn.microsoft.com/tr-tr/library/system.web.security.membership.generatepassword(v=vs.110).aspx


    //URL ve Json ajax lar için filtreleme
    passch = passch.Replace(":", "z");
    passch = passch.Replace(";", "W");
    passch = passch.Replace("'", "t");
    passch = passch.Replace("\"", "r");
    passch = passch.Replace("/", "+");
    passch = passch.Replace("\\", "e");

    passch = passch.Replace("?", "9");
    passch = passch.Replace("&", "8");
    passch = passch.Replace("#", "D");
    passch = passch.Replace("%", "u");
    passch = passch.Replace("=", "4");
    passch = passch.Replace("~", "1");

    passch = passch.Replace("[", "2");
    passch = passch.Replace("]", "3");
    passch = passch.Replace("{", "g");
    passch = passch.Replace("}", "J");


    //passch = passch.Replace("(", "6");
    //passch = passch.Replace(")", "0");
    //passch = passch.Replace("|", "p");
    //passch = passch.Replace("@", "4");
    //passch = passch.Replace("!", "u");
    //passch = passch.Replace("$", "Z");
    //passch = passch.Replace("*", "5");
    //passch = passch.Replace("_", "a");

    passch = passch.Replace(",", "V");
    passch = passch.Replace(".", "N");
    passch = passch.Replace("+", "w");
    passch = passch.Replace("-", "7");





    return passch;



}
Ercan ILIK
źródło
Czy możesz wyjaśnić, co robi Twój kod i dlaczego? Z recenzji
Wai Ha Lee
Prawdopodobnie zostaną usunięte tylko odpowiedzi na kod bez wyjaśnienia.
Rook
-4

Wstawianie timera: timer1, 2 przyciski: button1, button2, 1 textBox: textBox1 i comboBox: comboBox1. Upewnij się, że deklarujesz:

int count = 0;

Kod źródłowy:

 private void button1_Click(object sender, EventArgs e)
    {
    // This clears the textBox, resets the count, and starts the timer
        count = 0;
        textBox1.Clear();
        timer1.Start();
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
    // This generates the password, and types it in the textBox
        count += 1;
            string possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
            string psw = "";
            Random rnd = new Random { };
            psw += possible[rnd.Next(possible.Length)];
            textBox1.Text += psw;
            if (count == (comboBox1.SelectedIndex + 1))
            {
                timer1.Stop();
            }
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        // This adds password lengths to the comboBox to choose from.
        comboBox1.Items.Add("1");
        comboBox1.Items.Add("2");
        comboBox1.Items.Add("3");
        comboBox1.Items.Add("4");
        comboBox1.Items.Add("5");
        comboBox1.Items.Add("6");
        comboBox1.Items.Add("7");
        comboBox1.Items.Add("8");
        comboBox1.Items.Add("9");
        comboBox1.Items.Add("10");
        comboBox1.Items.Add("11");
        comboBox1.Items.Add("12");
    }
    private void button2_click(object sender, EventArgs e)
    {
        // This encrypts the password
        tochar = textBox1.Text;
        textBox1.Clear();
        char[] carray = tochar.ToCharArray();
        for (int i = 0; i < carray.Length; i++)
        {
            int num = Convert.ToInt32(carray[i]) + 10;
            string cvrt = Convert.ToChar(num).ToString();
            textBox1.Text += cvrt;
        }
    }
Joe
źródło
1
Nie używaj System.Randomdla bezpieczeństwa.
CodesInChaos