30

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

mLar
  • 2,967
  • 2
  • 22
  • 23

8 Answers8

51

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.

Darkseal
  • 9,205
  • 8
  • 78
  • 111
  • 2
    Thanks for this! fwiw, I modded to skip a few characters that can look alike, like 0 and O, 1 and l, etc. Also, for those wondering like I was (assuming I didn't miss something obvious), you can inject IdentityOptions by injecting IOptions and then access the pw config like: identityOptions.Value.Password – JohnnyFun Jun 15 '18 at 20:52
  • 9
    Please don't use `Random rand = new Random(Environment.TickCount);` in password generator functions. – Brian Nov 30 '18 at 21:52
  • The implementation does not work for specific scenarios (like `RequireDigit = false`). In the `for` loop all values of the `randomChars` are used, without respecting the provided `PasswordOptions`. I use https://github.com/prjseal/PasswordGenerator/, which also contains unit tests. – JeroenW Nov 18 '19 at 14:14
  • 4
    +1 for @Brian, Random is not cryptographically safe, use RandomNumberGenerator from System.Security.Cryptography instead https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.randomnumbergenerator?view=netframework-4.8 – Santhos Feb 03 '20 at 16:26
  • 1
    If you're using ASP.NET Core Identity, then the CryptoRandom class is a drop-in replacement for Random which uses RandomNumberGenerator under the hood, without code rewriting. – Sam Dec 02 '20 at 04:50
  • I created a NuGet package based on this answer: https://www.nuget.org/packages/IdentityPasswordGenerator/ It's pretty much a heavily refactored version of the above code. I also added the excludeNonRequiredChars parameter, so it can be used in a more diverse way. – Péter Bozsó Jan 02 '22 at 11:22
18

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.

Adam Williams
  • 1,712
  • 3
  • 17
  • 30
  • 4
    It would be most helpful if whoever down voted this could explain themselves. – 7wp Mar 20 '15 at 14:07
  • 2
    Membership isn't part of ASP.NET Identity, it is its predecessor. It implements a *safer* method of sending reset links with unique reset tokens. – Panagiotis Kanavos Mar 23 '16 at 09:22
  • @PanagiotisKanavos I've tried the method you are suggesting, but I always get `Invalid Token` validation error. I've posted the issue [here](https://stackoverflow.com/q/46249048/1232087) if you have a time to provide a suggestion/solution etc. – nam Sep 19 '17 at 19:42
  • 8
    @7wp I just downvoted, because whoever doesn't know the enormous differences between Identity and Membership shouldn't be answering a question of Identity using Membership. This is a completely useless answer – Camilo Terevinto Nov 19 '18 at 11:31
  • Thank you mister Camilo for raising attention to this. One of our stackoverflow colleague allready said this 2 years ago. With a simple search on google we can all find out what is the difference between these two authentication and authorization generations. –  Nov 20 '18 at 12:20
  • 1
    @CamiloTerevinto I can respect that. But i'm still confused, the question essentially boils down to "how to generate a random password" I don't think they care if comes from Identity or Membership or their relationship between them. The answer is still accurate, wouldn't you say? – 7wp Feb 20 '19 at 23:52
  • @7wp You would need to validate that the Membership generated password with Identity, good luck with that. Also, ASP.NET Core supports Identity and not Membership, that's a very important import, even if the question isn't specifically about ASP.NET Core, most functionality remained the same between Identity 2 (System.Web) and Identity 3 (Microsoft.AspNetCore.Identity) – Camilo Terevinto Feb 20 '19 at 23:59
  • @CamiloTerevinto all good stuff, but he didn't say he wanted to validate it with Identity. Just how to generate a password. Honestly I don't even care, nice of you to explain your down vote though. I was just responding because StackOverflow has become so hostile from the early days. And that I do care about. In fact it appears the original answerer has left the platform, I can only speculate why. – 7wp Feb 21 '19 at 01:20
11

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

David Paquette
  • 524
  • 6
  • 14
  • Using the link you provided, I'm able to send user a reset password link, the link get successfully sent to an email - but after I click on the link in the email and fill in the info in the reset password form and submit it, I always get `Invalid Token` validation error. I've posted the issue [here](https://stackoverflow.com/q/46249048/1232087) if you have a time to provide a suggestion/solution etc. – nam Sep 19 '17 at 19:39
9

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;
        }
    }
Rubenisme
  • 787
  • 1
  • 8
  • 15
5

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);
CDspace
  • 2,639
  • 18
  • 30
  • 36
RFex
  • 94
  • 1
  • 2
  • (PasswordValidator validator) on https://learn.microsoft.com/en-us/previous-versions/aspnet/dn613295(v=vs.108) – mainmind83 Jan 19 '22 at 11:10
0

There is a package that you can install from NuGet that can generate Passwords, here is the link to the package NuGet Link. It includes example codes that you can work with but I'll show one below.

using System;
                
public class Program
{
public static void Main()
{   
    //These are usage examples for the NuGet package PasswordGenerator
    //https://www.nuget.org/packages/PasswordGenerator
    
    //You can install it in NuGet by searching for PasswordGenerator 
    //or in Package Manager Console you can enter Install-Package PasswordGenerator 
    
    
    
    // By default, all characters available for use and a length of 16
    // Will return a random password with the default settings 
    PasswordGenerator pwdGen1 = new PasswordGenerator();
    string password1 = pwdGen1.Next();
    Console.WriteLine("Example 1: " + password1);
        

    // Same as above but you can set the length. Must be between 8 and 128
    // Will return a password which is 32 characters long       
    PasswordGenerator pwdGen2 = new PasswordGenerator(32);
    string password2 = pwdGen2.Next();
    Console.WriteLine("Example 2: " + password2);
    
    
    // Same as above but you can set the length. Must be between 8 and 128
    // Will return a password which only contains lowercase and uppercase characters and is 21 characters long.
    PasswordGenerator pwdGen3 = new PasswordGenerator(includeLowercase: true, includeUppercase: true, includeNumeric: false, includeSpecial: false, passwordLength: 21);
    string password3 = pwdGen3.Next();  
    Console.WriteLine("Example 3: " + password3);
    
            
    //### Fluent usage ###
    
    // You can build up your reqirements by adding things to the end, like .AddNumeric()
    // This will return a password which is just numbers and has a default length of 16
    PasswordGenerator pwdGen4 = new PasswordGenerator().IncludeNumeric();
    string password4 = pwdGen4.Next();      
    Console.WriteLine("Example 4: " + password4);
    
    
    // As above, here is how to get lower, upper and special characters using this approach     
    PasswordGenerator pwdGen5 = new PasswordGenerator().IncludeLowercase().IncludeUppercase().IncludeSpecial();
    string password5 = pwdGen5.Next();
    Console.WriteLine("Example 5: " + password5);
    
    
    // This is the same as the above, but with a length of 59
    PasswordGenerator pwdGen6 = new PasswordGenerator(59).IncludeLowercase().IncludeUppercase().IncludeSpecial();
    string password6 = pwdGen6.Next();
    Console.WriteLine("Example 6: " + password6);
    
    
    PasswordGenerator pwdGen7 = new PasswordGenerator().IncludeLowercase().IncludeUppercase().IncludeSpecial().LengthRequired(128);
    string password7 = pwdGen7.Next();
    Console.WriteLine("Example 7: " + password7);
}
}
CutCopyPaste
  • 51
  • 1
  • 2
-1
public static class PasswordCombinationType
{
    public const int Any = 1;
    public const int AtLeast2Types = 2;
    public const int AtLeast3Types = 3;
    public const int All4Types = 4;
}
public class PasswordStore
{

    /// <summary>
    /// Generate Random Password
    /// </summary>
    /// <param name="length">Minimum length of password </param>
    /// <param name="minLowerCaseCharacter">Minimum Lower Case Character</param>
    /// <param name="minUpperCaseCharacter">Minimum Upper Case Character</param>
    /// <param name="MinSpecialCharacter">Minimum Special Character</param>
    /// <param name="minDigit">Minimum Digit</param>
    /// <param name="passwordCombinationTypeId"> 1= Any, 2 =Character only , 3= Character with digit ,4 = Character and Special Character with  digit</param>
    /// <returns>Random Password</returns>
    public static string GeneratePassword(int passwordLength,
        int minLowerCaseCharacter,
        int minUpperCaseCharacter,
        int MinSpecialCharacter,
        int minDigit,
        int passwordCombinationTypeId)
    {
        string randomPassword = string.Empty;
        Random random = new Random();
        int minDigitCount = 0;
        int minLowerCaseCharacterCount = 0;
        int minUpperCaseCharacterCount = 0;
        int minSpecialCharacterCount = 0;

        do
        {
            switch (passwordCombinationTypeId)
            {
                case PasswordCombinationType.Any:
                case PasswordCombinationType.All4Types:
                    RandomDigit(minDigit, ref randomPassword, random, ref minDigitCount);
                    RandomLowerCaseCharacter(minLowerCaseCharacter, ref randomPassword, random, ref minLowerCaseCharacterCount);
                    RandomUpperCaseCharacter(minUpperCaseCharacter, ref randomPassword, random, ref minUpperCaseCharacterCount);
                    RandomSpecialCharacter(MinSpecialCharacter, ref randomPassword, random, ref minSpecialCharacterCount);
                    break;
                case PasswordCombinationType.AtLeast2Types:
                    RandomLowerCaseCharacter(minLowerCaseCharacter, ref randomPassword, random, ref minLowerCaseCharacterCount);
                    RandomUpperCaseCharacter(minUpperCaseCharacter, ref randomPassword, random, ref minUpperCaseCharacterCount);
                    break;
                case PasswordCombinationType.AtLeast3Types:
                    RandomDigit(minDigit, ref randomPassword, random, ref minDigitCount);
                    RandomLowerCaseCharacter(minLowerCaseCharacter, ref randomPassword, random, ref minLowerCaseCharacterCount);
                    RandomUpperCaseCharacter(minUpperCaseCharacter, ref randomPassword, random, ref minUpperCaseCharacterCount);
                    break;
            }
            if (passwordLength != randomPassword.Length)
            {
                randomPassword += Convert.ToChar(random.Next(33, 126));
            }
        } while (passwordLength != randomPassword.Length);
        return randomPassword;


    }
    internal static void RandomLowerCaseCharacter(int minLowerCaseCharacter, ref string randomPassword, Random random, ref int minLowerCaseCharacterCount)
    {
        while (minLowerCaseCharacter != minLowerCaseCharacterCount)
        {
            randomPassword += Convert.ToChar(random.Next(97, 123));  // a-z
            minLowerCaseCharacterCount++;
        }
    }
    internal static void RandomDigit(int minDigit, ref string randomPassword, Random random, ref int minDigitCount)
    {
        while (minDigit != minDigitCount)
        {
            randomPassword += Convert.ToChar(random.Next(48, 58));  // 0-9
            minDigitCount++;
        }
    }
    internal static void RandomUpperCaseCharacter(int minUpperCaseCharacter, ref string randomPassword, Random random, ref int minUpperCaseCharacterCount)
    {
        while (minUpperCaseCharacter != minUpperCaseCharacterCount)
        {
            randomPassword += Convert.ToChar(random.Next(65, 91));  // A-Z
            minUpperCaseCharacterCount++;
        }
    }
    internal static void RandomSpecialCharacter(int MinSpecialCharacter, ref string randomPassword, Random random, ref int minSpecialCharacterCount)
    {
        while (MinSpecialCharacter != minSpecialCharacterCount)
        {
            randomPassword += Convert.ToChar(random.Next(33, 48));  // symbols !"#$%&'()*+,-./
            minSpecialCharacterCount++;
        }
    }

}
-7

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)

Andrey
  • 1
  • Avoid posting link-only answers. While they may be correct, if the link changes/gets deleted, your answer is no longer relevant. Include any important parts of code/content from the link in your answer to avoid having it deleted. – Tim Lewis Feb 12 '15 at 14:45
  • 9
    Do *NOT* use a GUID for cryptographic protection. GUID's make guarantees around *uniqueness*, not *randomness*. – Dan Turner Oct 13 '15 at 07:23