Generando contraseñas aleatorias

229

Cuando un usuario en nuestro sitio pierde su contraseña y se dirige a la página Contraseña perdida, debemos darle una nueva contraseña temporal. Realmente no me importa cuán aleatorio sea esto, o si coincide con todas las reglas de contraseña fuertes "necesarias", todo lo que quiero hacer es darles una contraseña que puedan cambiar más adelante.

La aplicación es una aplicación web escrita en C #. así que estaba pensando en ser malo e ir por la ruta fácil de usar parte de un Guid. es decir

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

Sugerencias? pensamientos?

FryHard
fuente
12
Algunas buenas soluciones aquí, pero un pequeño consejo: no generes contraseñas que contengan ninguno de estos caracteres: Oo0Ili (ya ves por qué) :)
stian.net
2
Agregué una respuesta que usa KeePass como generador de contraseñas, y de las muchas opciones expuestas también incluí la opción de excluir caracteres parecidos, como lo menciona @ stian.net.
Peter

Respuestas:

570

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

Rik
fuente
77
¡No sabía que el Marco tiene tal método! ¡Increíble! ¡Cambiaré mi código actual por esto!
FryHard
35
Lo encontré después de pasar casi un día perfeccionando mi propio código pw gen. Imagen cómo me sentí;)
Rik
16
AFAIK este método no genera una contraseña que cumpla con una política de contraseña en el dominio, por lo que no es adecuado para cada uso.
teebot
19
El principal problema con esta solución es que no puede controlar el conjunto de caracteres, por lo que no puede eliminar los caracteres visualmente ambiguos (0oOl1i! |) Que pueden ser realmente importantes en la práctica.
David Hammond
20
¿Algo para 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();
}

Esto tiene una buena ventaja de poder elegir de una lista de caracteres disponibles para la contraseña generada (por ejemplo, solo dígitos, solo mayúsculas o minúsculas, etc.)

Radu094
fuente
2
este método (base 62) es superior al GUID (base 16) en fuerza: una cadena hexadecimal de 8 caracteres es equivalente a una secuencia alfanumérica de 4-5 caracteres
Jimmy
57
Randomno es criptográficamente seguro; System.Security.Cryptography.RNGCryptoServiceProviderEs una mejor opción.
anaximander
3
Esto generaría la misma contraseña cada vez que se llama al método, ya que la clase Random se instancia cada vez. Esto podría hacerse seguro moviendo Random fuera de este método y reutilizando la instancia.
Jon
66
No, no lo haría. A menos que dos personas decidan cambiar las contraseñas al mismo tiempo.
Radu094
10
cita de la pregunta: no me importa "si coincide con todas las" necesarias "reglas de contraseña seguras" ... aunque gracias por rechazar esta respuesta
Radu094
35

Los objetivos principales de mi código son:

  1. La distribución de las cadenas es casi uniforme (no importa las desviaciones menores, siempre que sean pequeñas)
  2. Produce más de unos pocos miles de millones de cadenas para cada conjunto de argumentos. Generar una cadena de 8 caracteres (~ 47 bits de entropía) no tiene sentido si su PRNG solo genera 2 mil millones (31 bits de entropía) de valores diferentes.
  3. Es seguro, ya que espero que la gente use esto para contraseñas u otros tokens de seguridad.

La primera propiedad se logra tomando un módulo de valor de 64 bits del tamaño del alfabeto. Para alfabetos pequeños (como los 62 caracteres de la pregunta) esto lleva a un sesgo insignificante. La segunda y tercera propiedad se logran utilizando en RNGCryptoServiceProviderlugar de 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);
}

(Esta es una copia de mi respuesta a ¿Cómo puedo generar cadenas alfanuméricas de 8 caracteres al azar en C #? )

CodesInChaos
fuente
1
Si UInt64.MaxValue no es divisible de manera uniforme por characterArray.Length, entonces los caracteres seleccionados al azar no se distribuirán de manera uniforme (aunque esto será un efecto muy pequeño).
Jeff Walker Code Ranger
1
@JeffWalkerCodeRanger Es por eso que dije un sesgo insignificante, no un sesgo. Incluso con un petabyte de salida, tiene menos del 1% para distinguirlo de un generador de cadenas perfectamente imparcial. La complejidad adicional de la eliminación de la distorsión perfecta claramente no vale la ganancia teórica en aleatoriedad aquí.
CodesInChaos
99
Para aquellos que usan .NET Core, sustituyan "nuevo RNGCryptoServiceProvider (). GetBytes (bytes);" con "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);
    }
}

(También podría hacer que la clase en la que vive este método implemente IDisposable, mantenga una referencia al RNGCryptoServiceProvider, y lo deseche correctamente, para evitar crear instancias repetidamente).

Se ha observado que, dado que esto devuelve una cadena de base 64, la longitud de salida siempre es un múltiplo de 4, y el espacio adicional se utiliza =como carácter de relleno. El lengthparámetro especifica la longitud del búfer de bytes, no la cadena de salida (y, por lo tanto, quizás no sea el mejor nombre para ese parámetro, ahora lo pienso). Esto controla cuántos bytes de entropía tendrá la contraseña. Sin embargo, debido a que base-64 usa un bloque de 4 caracteres para codificar cada 3 bytes de entrada, si solicita una longitud que no sea múltiplo de 3, habrá un "espacio" adicional, y lo usará =para llenar el extra.

Si no le gusta usar cadenas de base 64 por algún motivo, puede reemplazar la Convert.ToBase64String()llamada con una conversión a cadena normal o con cualquiera de los Encodingmétodos; p.ej. Encoding.UTF8.GetString(tokenBuffer)- solo asegúrese de elegir un conjunto de caracteres que pueda representar el rango completo de valores que salen del RNG, y que produzca caracteres que sean compatibles con donde sea que esté enviando o almacenando esto. Usar Unicode, por ejemplo, tiende a dar muchos caracteres chinos. El uso de base-64 garantiza un conjunto de caracteres ampliamente compatible, y las características de dicha cadena no deberían hacerla menos segura siempre que use un algoritmo de hash decente.

anaximandro
fuente
Creo que te refieres a poner tokenBuffer donde tienes linkBuf.
Pentag
Cuando uso este código y lo paso en una longitud de 10, la cadena devuelta siempre tiene 16 caracteres y los 2 caracteres finales siempre son "==". ¿Lo estoy usando incorrectamente? ¿La longitud se especifica en hexadecimal?
PIntag
1
La longitud especificada es el número de bytes de aleatoriedad (o "entropía", como se conoce técnicamente). Sin embargo, el valor devuelto está codificado en base 64, lo que significa que, debido a cómo funciona la codificación en base 64, la longitud de salida siempre es un múltiplo de 4. A veces, son más caracteres de los que necesita para codificar todos los caracteres, por lo que utiliza = personajes para rellenar el resto.
anaximandro
1
RNGCryptoServiceProvider es un ID desechable, por lo que lo implementaría con el patrón de uso (es decir, usando (var cryptRNG = new RNGCryptoServiceProvider ()) {...})
kloarubeek
@kloarubeek Un punto válido y una buena sugerencia. He agregado eso a mi muestra de código.
anaximandro
20

Esto es mucho más grande, pero creo que se ve un poco más completo: 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
fuente
2
Resulta que el marco lo respalda. ¡Así que acepto esa respuesta!
FryHard
1
Generar solo 2 ^ 31 contraseñas diferentes, incluso con tamaños de salida largos, es un poco bajo. Puede ser suficiente contra ataques en línea, pero ciertamente demasiado pequeño para ataques fuera de línea. => No recomendaría esto.
CodesInChaos
Esta sigue siendo una buena respuesta porque el soporte "integrado" es realmente Membresía, y ¿qué pasa si ha decidido no usar la Membresía ASP.NET? Todavía funciona, ya que la dependencia es System.Web.dll, pero es un poco incómodo porque el método no es autónomo. @GEOCHET: Gracias por publicar esta alternativa.
Chris Gomez
8

Sé que este es un hilo viejo, pero tengo lo que podría ser una solución bastante simple para que alguien la use. Fácil de implementar, fácil de entender y fácil de validar.

Considere el siguiente requisito:

Necesito generar una contraseña aleatoria que tenga al menos 2 letras minúsculas, 2 letras mayúsculas y 2 números. La contraseña también debe tener un mínimo de 8 caracteres de longitud.

La siguiente expresión regular puede validar este caso:

^(?=\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,}$

Está fuera del alcance de esta pregunta, pero la expresión regular se basa en mirar hacia adelante / mirar atrás y mirar hacia atrás .

El siguiente código creará un conjunto aleatorio de caracteres que coinciden con este requisito:

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);

}

Para cumplir con el requisito anterior, simplemente llame a lo siguiente:

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

El código comienza con un carácter no válido ( "!"), de modo que la cadena tiene una longitud en la que se pueden inyectar nuevos caracteres.

Luego recorre en bucle de 1 a la cantidad de caracteres en minúsculas requeridos, y en cada iteración, toma un elemento aleatorio de la lista en minúsculas y lo inyecta en una ubicación aleatoria en la cadena.

Luego repite el ciclo para letras mayúsculas y para números.

Esto le devuelve cadenas de longitud = lowercase + uppercase + numericsen las que se han colocado en minúscula, mayúscula y caracteres numéricos del recuento que desea en un orden aleatorio.

Troy Alford
fuente
3
No lo use System.Randompara cosas críticas de seguridad como contraseñas. UsoRNGCryptoServiceProvider
CodesInChaos
lowers[random.Next(lowers.Length - 1)].ToString() Este código nunca generará una 'z'. random.Next produce enteros menores que el número proporcionado, por lo que no debe restar ese extra de la longitud.
notbono
6

Para este tipo de contraseña, tiendo a usar un sistema que probablemente genere contraseñas "usadas" más fácilmente. Corto, a menudo compuesto por fragmentos pronouncables y algunos números, y sin ambigüedad entre caracteres (¿es eso un 0 o un O? ¿A 1 o un I?). Algo como

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;

(Escrito directamente en el navegador, así que úselo solo como guía. Además, agregue más palabras).

Adam Wright
fuente
6

No me gustan las contraseñas que crea Membership.GeneratePassword (), ya que son demasiado feas y tienen demasiados caracteres especiales.

Este código genera una contraseña de 10 dígitos no demasiado fea.

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

Claro, podría usar un Regex para hacer todos los reemplazos, pero esto es más fácil de leer y mantener.

Matt Frear
fuente
2
No se debe abusar de un GUID como crypto PRNG
CodesInChaos
3
Si va a usar este método, puede usar .ToString ("N") y no tendrá que reemplazar el "-". Tampoco hay necesidad de reemplazar "l", ya que no es un dígito hexadecimal.
JackAce
Totalmente ver por qué hiciste esto. Solo necesitaba una contraseña de corta duración (menos de un día) que no tenía que ser demasiado única por usuario. Sacar los unos y los ceros es una excelente manera de eliminar la confusión. También cambié la mía a mayúscula y luego corté la longitud a 6. Al igual que la sugerencia 'N', también. ¡Gracias!
Bill Noel
6

He creado esta clase que utiliza RNGCryptoServiceProvider y es flexible. Ejemplo:

var generator = new PasswordGenerator(minimumLengthPassword: 8,
                                      maximumLengthPassword: 15,
                                      minimumUpperCaseChars: 2,
                                      minimumNumericChars: 3,
                                      minimumSpecialChars: 2);
string password = generator.Generate();
Alex Siepman
fuente
Eso es genial, ¿cuál es la licencia en esa clase? ¿Puedo usarlo?
codeulike
¡Úselo como quiera!
Alex Siepman el
3

Creé este método similar al disponible en el proveedor de membresía. Esto es útil si no desea agregar la referencia web en algunas aplicaciones.

Funciona muy bien

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
fuente
66
No lo use System.Randompara cosas críticas de seguridad como contraseñas. UsoRNGCryptoServiceProvider
CodesInChaos
3

Siempre estuve muy contento con el generador de contraseñas incorporado a KeePass. Como KeePass es un programa .Net y de código abierto, decidí investigar un poco el código. Terminé simplemente haciendo referencia a KeePass.exe, la copia proporcionada en la instalación de la aplicación estándar, como referencia en mi proyecto y escribiendo el código a continuación. Puedes ver cuán flexible es gracias a KeePass. Puede especificar la longitud, qué caracteres incluir / no incluir, etc.

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();
    }
Peter
fuente
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;
    }
Saeed Ahmad
fuente
Por favor explique su respuesta
Linus
1
Esta función funciona muy bien con algunos cambios. 1. Se agregó entropía si está haciendo varias llamadas a la vez. Esto significa llamada Sleep () o alguna otra función que causa entropía entre llamadas. 2. Aumente el número y la aleatoriedad de los caracteres de origen. Creé una serie de 500 contraseñas de caracteres con paso de teclas, excluyendo un montón de caracteres similares y otros caracteres escapables con los que no quería tratar y utilicé la cadena de 2000 caracteres resultante. La aleatoriedad después de solo 100 ms de Entropía fue bastante buena.
Wizengamot
2

Agregaré otra respuesta mal aconsejada a la olla.

Tengo un caso de uso en el que necesito contraseñas aleatorias para la comunicación máquina-máquina, por lo que no tengo ningún requisito para la legibilidad humana. Tampoco tengo acceso a Membership.GeneratePasswordmi proyecto y no quiero agregar la dependencia.

Estoy bastante seguro de que Membership.GeneratePasswordestá haciendo algo similar a esto, pero aquí puede ajustar los grupos de personajes para dibujar.

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();
    }
}

Y algunos resultados de ejemplo:

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

Para evitar quejas sobre el uso de Random: La fuente principal de aleatoriedad sigue siendo el Crypto RNG. Incluso si pudieras predestinar de manera determinista la secuencia que sale Random(digamos que solo produjo 1), aún no sabrías el próximo carácter que se elegiría (aunque eso sería limitar el abanico de posibilidades).

Una extensión simple sería agregar ponderación a los diferentes conjuntos de caracteres, lo que podría ser tan simple como aumentar el valor máximo y agregar casos fallidos para aumentar el peso.

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;
}

Para un generador de contraseñas aleatorias más humanista, una vez implementé un sistema de solicitud utilizando la lista de palabras de dados EFF .

kitsu.eb
fuente
1

Me gusta mirar generar contraseñas, al igual que generar claves de software. Debe elegir entre una variedad de personajes que siguen una buena práctica. Tome lo que respondió @ Radu094 y modifíquelo para seguir las buenas prácticas. No ponga cada letra en la matriz de caracteres. Algunas letras son más difíciles de decir o entender por teléfono.

También debe considerar usar una suma de verificación en la contraseña que se generó para asegurarse de que fue generada por usted. Una buena forma de lograr esto es usar el algoritmo LUHN .

Dale Ragan
fuente
1

Aquí está lo que armé rápidamente.

    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
fuente
1

Utilizo este código para generar contraseña con composición de saldo de caracteres alfabéticos, numéricos y no alfabéticos.

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
fuente
0

Esto es corto y funciona muy bien para mí.

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();
}
usuario1058637
fuente
Puede "funcionar", pero ciertamente no es seguro. Las contraseñas deben ser seguras.
CodesInChaos
Creo que las contraseñas aleatorias son contraseñas temporales. No veo por qué tienen que ser seguros, e incluso si lo hacen, puede agregar números y caracteres especiales en el rango.
user1058637
1
Agregar números y caracteres especiales no mejora la seguridad si los genera utilizando un PRNG predecible. Si sabe cuándo se generó una contraseña, puede limitarla a unos pocos candidatos.
CodesInChaos
0

En mi sitio web utilizo este método:

    //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();
    }

Editar cadena _SymbolsAllpara su lista de matriz.

GRUNGER
fuente
Como ya se indicó en la descripción de edición. Si enlaza a su sitio web sin que sea una referencia de código, solo está anunciando lo que lo convierte en spam, por lo tanto, elimine ese enlace de su respuesta.
Bowdzone
0

Se agregó un código complementario a la respuesta aceptada. Mejora las respuestas simplemente usando Random y permite algunas opciones de contraseña. También me gustaron algunas de las opciones de la respuesta de KeePass, pero no quería incluir el ejecutable en mi solución.

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;
}

Este fue el primer enlace cuando busqué generar contraseñas aleatorias y lo siguiente está fuera del alcance de la pregunta actual, pero podría ser importante considerarlo.

  • Basado en el supuesto de que System.Web.Security.Membership.GeneratePasswordes criptográficamente seguro con un mínimo del 20% de los caracteres no alfanuméricos.
  • No estoy seguro de si eliminar caracteres y agregar cadenas se considera una buena práctica en este caso y proporciona suficiente entropía.
  • Es posible que desee considerar la implementación de alguna manera con SecureString para el almacenamiento seguro de contraseñas en la memoria.
Mate
fuente
Para su información, no necesitaría incluir el archivo ejecutable KeyPass, ya que es de código abierto (el código fuente está disponible para descargar aquí )
Alex Klaus
0

validChars puede ser cualquier construcción, pero decidí seleccionar en función de los rangos de código ascii eliminando caracteres de control. En este ejemplo, es una cadena de 12 caracteres.

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
fuente
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
fuente
Código únicas respuestas enviaban anima, ya que no proporcionan mucha información para los futuros lectores Sírvanse proporcionar una explicación a lo que ha escrito
WhatsThePoint
Crea la misma contraseña una y otra vez. Necesidad de tener una instancia Randomcomo estática
trailmax
¡Tenga en cuenta que esta respuesta generará la misma contraseña si se llama varias veces seguidas! Debe agregar Entropía entre llamadas si desea utilizar la función para asignar contraseñas usando código a múltiples registros a la vez. Todavía útil aunque ...
Wizengamot
0

Este paquete le permite generar una contraseña aleatoria mientras indica con fluidez qué caracteres debe contener (si es necesario):

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

Ejemplo:

var pwd = new Password().IncludeLowercase().IncludeUppercase().IncludeSpecial();
var password = pwd.Next();
Johan Maes
fuente
0

Si desea utilizar la generación de números aleatorios criptográficamente segura utilizada por System.Web.Security.Membership.GeneratePassword pero también desea restringir el conjunto de caracteres a caracteres alfanuméricos, puede filtrar el resultado con una expresión regular:

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
fuente
-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
fuente
¿Podría explicar qué hace su código y por qué? De la opinión
Wai Ha Lee
Es probable que solo se eliminen las respuestas de código sin explicación.
Torre
-4

Inserte un temporizador: timer1, 2 botones: button1, button2, 1 textBox: textBox1 y un comboBox: comboBox1. Asegúrate de declarar:

int count = 0;

Código fuente:

 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
fuente
1
No lo use System.Randompara seguridad.
CodesInChaos