ASP.NET Identity : Generate random password

前端 未结 6 1091
天涯浪人
天涯浪人 2021-02-02 10:50

Is there any built in function that creates random passwords ? Asp.net simple memebership used to have a similar method

相关标签:
6条回答
  • 2021-02-02 11:40

    I know this is a bit of an old question, and there have been others who put out source for randomly generating passwords but Membership.GeneratePassword is implemented like this:

    Luckily this is licensed under The MIT License https://github.com/Microsoft/referencesource/blob/master/LICENSE.txt

    public class PasswordStore
    {
            private static readonly char[] Punctuations = "!@#$%^&*()_-+=[{]};:>|./?".ToCharArray();
            private static readonly char[] StartingChars = new char[] { '<', '&' };
            /// <summary>Generates a random password of the specified length.</summary>
            /// <returns>A random password of the specified length.</returns>
            /// <param name="length">The number of characters in the generated password. The length must be between 1 and 128 characters. </param>
            /// <param name="numberOfNonAlphanumericCharacters">The minimum number of non-alphanumeric characters (such as @, #, !, %, &amp;, and so on) in the generated password.</param>
            /// <exception cref="T:System.ArgumentException">
            /// <paramref name="length" /> is less than 1 or greater than 128 -or-<paramref name="numberOfNonAlphanumericCharacters" /> is less than 0 or greater than <paramref name="length" />. </exception>
            public static string GeneratePassword(int length, int numberOfNonAlphanumericCharacters)
            {
                if (length < 1 || length > 128)
                    throw new ArgumentException("password_length_incorrect", nameof(length));
                if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0)
                    throw new ArgumentException("min_required_non_alphanumeric_characters_incorrect", nameof(numberOfNonAlphanumericCharacters));
                string s;
                int matchIndex;
                do
                {
                    var data = new byte[length];
                    var chArray = new char[length];
                    var num1 = 0;
                    new RNGCryptoServiceProvider().GetBytes(data);
                    for (var index = 0; index < length; ++index)
                    {
                        var num2 = (int)data[index] % 87;
                        if (num2 < 10)
                            chArray[index] = (char)(48 + num2);
                        else if (num2 < 36)
                            chArray[index] = (char)(65 + num2 - 10);
                        else if (num2 < 62)
                        {
                            chArray[index] = (char)(97 + num2 - 36);
                        }
                        else
                        {
                            chArray[index] = Punctuations[num2 - 62];
                            ++num1;
                        }
                    }
                    if (num1 < numberOfNonAlphanumericCharacters)
                    {
                        var random = new Random();
                        for (var index1 = 0; index1 < numberOfNonAlphanumericCharacters - num1; ++index1)
                        {
                            int index2;
                            do
                            {
                                index2 = random.Next(0, length);
                            }
                            while (!char.IsLetterOrDigit(chArray[index2]));
                            chArray[index2] = Punctuations[random.Next(0, Punctuations.Length)];
                        }
                    }
                    s = new string(chArray);
                }
                while (IsDangerousString(s, out matchIndex));
                return s;
            }
    
            internal static bool IsDangerousString(string s, out int matchIndex)
            {
                //bool inComment = false;
                matchIndex = 0;
    
                for (var i = 0; ;)
                {
    
                    // Look for the start of one of our patterns 
                    var n = s.IndexOfAny(StartingChars, i);
    
                    // If not found, the string is safe
                    if (n < 0) return false;
    
                    // If it's the last char, it's safe 
                    if (n == s.Length - 1) return false;
    
                    matchIndex = n;
    
                    switch (s[n])
                    {
                        case '<':
                            // If the < is followed by a letter or '!', it's unsafe (looks like a tag or HTML comment)
                            if (IsAtoZ(s[n + 1]) || s[n + 1] == '!' || s[n + 1] == '/' || s[n + 1] == '?') return true;
                            break;
                        case '&':
                            // If the & is followed by a #, it's unsafe (e.g. &#83;) 
                            if (s[n + 1] == '#') return true;
                            break;
                    }
    
                    // Continue searching
                    i = n + 1;
                }
            }
    
            private static bool IsAtoZ(char c)
            {
                if ((int)c >= 97 && (int)c <= 122)
                    return true;
                if ((int)c >= 65)
                    return (int)c <= 90;
                return false;
            }
        }
    
    0 讨论(0)
  • 2021-02-02 11:49

    Membership.GeneratePassword() create a password that isn't compliant with Identity validator.

    I have written a simple function that consider UserManager Validator for creating a right random password to assign to user.

    It simply generates random chars and check if chars satisfy Validator requirements. If the requirements aren't satisfied, it appends the remaining chars to satisfy rules.

    private string GeneratePassword(MessagePasswordValidator validator)
    {
        if (validator == null)
            return null;
    
        bool requireNonLetterOrDigit = validator.RequireNonLetterOrDigit;
        bool requireDigit = validator.RequireDigit;
        bool requireLowercase = validator.RequireLowercase;
        bool requireUppercase = validator.RequireUppercase;
    
        string randomPassword = string.Empty;
    
        int passwordLength = validator.RequiredLength;
    
        Random random = new Random();
        while (randomPassword.Length != passwordLength)
        {
            int randomNumber = random.Next(48, 122);  // >= 48 && < 122 
            if (randomNumber == 95 || randomNumber == 96) continue;  // != 95, 96 _'
    
            char c = Convert.ToChar(randomNumber);
    
            if (requireDigit)
                if (char.IsDigit(c))
                    requireDigit = false;
    
            if (requireLowercase)
                if (char.IsLower(c))
                    requireLowercase = false;
    
            if (requireUppercase)
                if (char.IsUpper(c))
                    requireUppercase = false;
    
            if (requireNonLetterOrDigit)
                if (!char.IsLetterOrDigit(c))
                    requireNonLetterOrDigit = false;
    
            randomPassword += c;
        }
    
        if (requireDigit)
            randomPassword += Convert.ToChar(random.Next(48, 58));  // 0-9
    
        if (requireLowercase)
            randomPassword += Convert.ToChar(random.Next(97, 123));  // a-z
    
        if (requireUppercase)
            randomPassword += Convert.ToChar(random.Next(65, 91));  // A-Z
    
        if (requireNonLetterOrDigit)
            randomPassword += Convert.ToChar(random.Next(33, 48));  // symbols !"#$%&'()*+,-./
    
        return randomPassword;
    }
    

    and calling:

    string password = GeneratePassword(UserManager.PasswordValidator as MessagePasswordValidator);
    
    0 讨论(0)
  • 2021-02-02 11:52

    Although I'm a bit late to the party, I would like to share the helper method I put together to handle these kind of scenarios in an ASP.NET Core compatible way.

    The function below ensures a decent char distribution, adding the required character types randomly within the string and not altering the required length (unless edge-case scenarios with lots of required unique chars, which was meant by design). It also features the support for the RequiredUniqueChars rule, which is one of the strength requirements available for the ASP.NET Core Identity framework.

        /// <summary>
        /// Generates a Random Password
        /// respecting the given strength requirements.
        /// </summary>
        /// <param name="opts">A valid PasswordOptions object
        /// containing the password strength requirements.</param>
        /// <returns>A random password</returns>
        public static string GenerateRandomPassword(PasswordOptions opts = null)
        {
            if (opts == null) opts = new PasswordOptions()
            {
                RequiredLength = 8,
                RequiredUniqueChars = 4,
                RequireDigit = true,
                RequireLowercase = true,
                RequireNonAlphanumeric = true,
                RequireUppercase = true
            };
    
            string[] randomChars = new[] {
                "ABCDEFGHJKLMNOPQRSTUVWXYZ",    // uppercase 
                "abcdefghijkmnopqrstuvwxyz",    // lowercase
                "0123456789",                   // digits
                "!@$?_-"                        // non-alphanumeric
            };
    
            Random rand = new Random(Environment.TickCount);
            List<char> chars = new List<char>();
    
            if (opts.RequireUppercase)
                chars.Insert(rand.Next(0, chars.Count), 
                    randomChars[0][rand.Next(0, randomChars[0].Length)]);
    
            if (opts.RequireLowercase)
                chars.Insert(rand.Next(0, chars.Count), 
                    randomChars[1][rand.Next(0, randomChars[1].Length)]);
    
            if (opts.RequireDigit)
                chars.Insert(rand.Next(0, chars.Count), 
                    randomChars[2][rand.Next(0, randomChars[2].Length)]);
    
            if (opts.RequireNonAlphanumeric)
                chars.Insert(rand.Next(0, chars.Count), 
                    randomChars[3][rand.Next(0, randomChars[3].Length)]);
    
            for (int i = chars.Count; i < opts.RequiredLength
                || chars.Distinct().Count() < opts.RequiredUniqueChars; i++)
            {
                string rcs = randomChars[rand.Next(0, randomChars.Length)];
                chars.Insert(rand.Next(0, chars.Count), 
                    rcs[rand.Next(0, rcs.Length)]);
            }
    
            return new string(chars.ToArray());
        }
    

    The function takes a PasswordOptions object as parameter, which is shipped by the Microsoft.AspNetCore.Identity assembly, but you can easily replace it with a two int / four bool parameter group (or POCO class) if you don't have that package installed.

    In the likely case you have it in your ASP.NET Core project, you can use the exact same object used in the ConfigureService method of the Startup class when defining the password requirements:

    [...]
    
    // Add ASP.NET Identity support
    services.AddIdentity<ApplicationUser, IdentityRole>(
        opts =>
        {
            opts.Password.RequireDigit = true;
            opts.Password.RequireLowercase = true;
            opts.Password.RequireUppercase = true;
            opts.Password.RequireNonAlphanumeric = false;
            opts.Password.RequiredLength = 7;
        })
        .AddEntityFrameworkStores<ApplicationDbContext>();
    
    [...]
    

    For additional details regarding this helper function you can also read this post on my blog.

    0 讨论(0)
  • 2021-02-02 11:52

    https://msdn.microsoft.com/ru-ru/library/system.guid.newguid(v=vs.110).aspx Check this out. GUID should work just fine (just remove all the '-' from it and cut required number if characters)

    0 讨论(0)
  • 2021-02-02 11:53

    If you are using .NET Framework and System.Web.Security.Membership is available to you:

    Check here:

     string password = Membership.GeneratePassword(12, 1);
    

    Note that this class is not available in .NET Standard or .NET Core.

    0 讨论(0)
  • 2021-02-02 11:53

    ASP.NET Identity does not have a generate password method.

    I'm not sure of your exact use case, but I believe the preferred approach would be to send the user a reset password link that allows the user to enter their own password. This is generally considered more secure than sending out a generated password in plain text.

    See the Reset Password section in this tutorial: http://www.asp.net/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity

    0 讨论(0)
提交回复
热议问题