40

How can I use Membership.GeneratePassword to return a password that ONLY contains alpha or numeric characters? The default method will only guarantee a minimum and not a maximum number of non alphanumeric passwords.

Kyle Trauberman
  • 25,414
  • 13
  • 85
  • 121
Curtis White
  • 6,213
  • 12
  • 59
  • 83
  • Why do you want to do this? Special characters are a good thing to have in your password. If (for some bizarre reason beyond my comprehension) you really want one, write your own method to generate it and don't use GeneratePassword. – Matti Virkkunen Apr 12 '10 at 20:29
  • 27
    One reasons is because those generated passwords aren't really globalization friendly. Another reason is that the CEO using the application got mad at the ugly, yet secure generated password and told you change it. – Greg Apr 20 '10 at 21:14
  • 2
    Or in my case, the punctuation marks are causing a server error in the API I am calling. – Drew Aug 12 '14 at 22:38
  • 4
    Or in my case, I've been told that they're more difficult to enter if you're using the keyboard on a mobile device. – ChrisW Mar 04 '16 at 14:39
  • 3
    In my case, I am generating passwords that are then to be entered into a physical device whose UI does not allow input of most special characters. – p e p Sep 16 '16 at 15:39

8 Answers8

62
string newPassword = Membership.GeneratePassword(15, 0);
newPassword = Regex.Replace(newPassword, @"[^a-zA-Z0-9]", m => "9" );

This regular expression will replace all non alphanumeric characters with the numeric character 9.

Curtis White
  • 6,213
  • 12
  • 59
  • 83
  • This will lose quite a bit of randomness in your password. Not recommended. – Matti Virkkunen Apr 12 '10 at 20:25
  • 13
    @Matti Possible to replace the "9" with Random.Next(0, 9) integer. Placing non alphanumeric characters into a typical password is typically overkill. A randomly generated password that doesn't use words is fine for many applications. The reason to do this is usability. Many non alpha-numeric characters are easily mistaken or confused or not able to be entered by average user. – Curtis White Apr 12 '10 at 21:11
  • @Matti It is also possible with this code to then go back and only insert a subset of approved non-alphanumeric characters. I don't see the need for any for many applications though. – Curtis White Apr 12 '10 at 21:14
  • 1
    @Curtis White: I prefer to draw the line where I consider a user fit to user a computer *above* the ability to read punctuation and use the keyboard correctly. – Matti Virkkunen Apr 12 '10 at 21:52
  • 1
    +1 Not sure why this was voted down. Whether or not you agree w/ the approach taken, this is a great answer to the question. – Kevin Babcock Aug 08 '10 at 02:16
  • 1
    And to comment on whether or not this is secure...using Random.Next() w/ the code above generates a password w/ 7.7e26 possible combinations - I'd say that is still very secure. – Kevin Babcock Aug 08 '10 at 02:21
  • 15
    Use 10 as your maximum since the upper limit is exclusive on the `Random.Next` method. `string newPassword = Membership.GeneratePassword(8, 0); Random rnd = new Random(); newPassword = Regex.Replace(newPassword, @"[^a-zA-Z0-9]", m => rnd.Next(0,10).ToString() );` – jsumrall Oct 08 '13 at 20:16
  • @"[^0-9]" is better then @"[^a-zA-Z0-9]" beacuaes its added some non alphabetical characters – Silence Peace Jul 11 '17 at 20:01
  • GeneratePassword may be less than ideal. They use non crypto Random sometimes for non alphanumeric characters. See https://referencesource.microsoft.com/#System.Web/Security/Membership.cs,fe744ec40cace139,references So that would result in less distributed usage of non-alphanumeric characters in theory. Looks like someone did that analysis and that does appear to be a reduction in randomness in their current algo for non-alphanumeric chars in some cases see https://poshhelp.wordpress.com/2017/01/30/why-you-should-stop-using-generatepassword/ – user2685937 Jan 18 '19 at 16:29
17

I realised that there may be ways of doing this. The GUID method is great, except it doesn't mix UPPER and lower case alphabets. In my case it produced lower-case only.

So I decided to use the Regex to remove the non-alphas then substring the results to the length that I needed.

string newPassword = Membership.GeneratePassword(50, 0); 

newPassword = Regex.Replace(newPassword, @"[^a-zA-Z0-9]", m => ""); 

newPassword = newPassword.Substring(0, 10);
RealSollyM
  • 1,530
  • 1
  • 22
  • 35
  • Thanks i have used it in my project – Muhammad Amin Jun 13 '13 at 10:54
  • 3
    Although unlikely, it is feasible that the initial password generated could contain less than 10 alphanumeric characters, which would then result in ArgumentOutOfRangeException() thrown by the Substring() method. – Chris Jan 14 '14 at 11:11
  • I beg to differ @Chris. The 50 in `Membership.GeneratePassword` specifies the length of the password, not just the `MaxLength`. – RealSollyM Jan 20 '14 at 08:36
  • 2
    @SollyM yes it does. However there is nothing to stop all 50 characters being symbols (i.e. not alphanumeric). It's unlikely, but feasible. If more than 40 were symbols, you would get an exception thrown by the `Substring()` method in the third line. – Chris Jan 20 '14 at 12:16
  • I get what you mean now. Which makes sense. But unlikely due to the next digit being 0. That reduces the occurrence of the non-AlphaNumerics. I am sure technically speaking this could result in an error, but I have been using this code for 9 months without a problem whatsoever. – RealSollyM Jan 20 '14 at 12:45
15

A simple way to get an 8 character alphanumeric password would be to generate a guid and use that as the basis:

string newPwd = Guid.NewGuid().ToString().Substring(0, 8);

If you need a longer password, just skip over the dash using substrings:

string newPwd = Guid.NewGuid().ToString().Substring(0, 11);
newPwd = newPwd.Substring(0, 8) + newPwd.Substring(9, 2); // to skip the dash.

If you want to make sure the first character is alpha, you could just replace it when needed with a fixed string if (newPwd[0] >= '0' && newPwd[0] <= '9')...

I hope someone can find this helpful. :-)

Laura Blood
  • 175
  • 1
  • 2
  • 6
    This is not a good idea. For one, you're reducing your set of possible characters to 16 (instead of 36); for another, guids aren't necessarily random, and depending on the method use to generate the guid the passwords you're creating might be easily predictable. I highly recommend Eric Lippert's series on how guids work for more info about this: http://blogs.msdn.com/b/ericlippert/archive/2012/04/24/guid-guide-part-one.aspx – E.Z. Hart May 29 '12 at 14:19
2

You could also try to generate passwords and concatenate the non alphanumeric characters until you reach the desired password length.

public string GeneratePassword(int length)
{
    var sb = new StringBuilder(length);

    while (sb.Length < length)
    {
        var tmp = System.Web.Security.Membership.GeneratePassword(length, 0);

        foreach(var c in tmp)
        {
            if(char.IsLetterOrDigit(c))
            {
                sb.Append(c);

                if (sb.Length == length)
                {
                    break;
                }
            }
        }
    }

    return sb.ToString();
}
breigo
  • 386
  • 2
  • 5
2

There is similar approach with breigo's solution. Maybe this is not so effective but so clear and short

string GeneratePassword(int length)
{
     var password = "";
     while (password.Length < length)
     {
          password += string.Concat(Membership.GeneratePassword(1, 0).Where(char.IsLetterOrDigit));
     }
     return password;
}
AntonE
  • 632
  • 5
  • 13
0

Going from @SollyM's answer, putting a while loop around it, to prevent the very unlikely event of all characters, or too many characters being special characters, and then substring throwing an exception.

private string GetAlphaNumericRandomString(int length)
{
    string randomString = "";
    while (randomString.Length < length)
    {
      //generates a random string, of twice the length specified, to counter the 
      //probability of the while loop having to run a second time
      randomString += Membership.GeneratePassword(length * 2, 0);

      //replace non alphanumeric characters
      randomString = Regex.Replace(randomString, @"[^a-zA-Z0-9]", m => "");
    }
    return randomString.Substring(0, length);
}
Djensen
  • 1,337
  • 1
  • 22
  • 32
0

I also prefer the GUID method - here's the short version:

string password = Guid.NewGuid().ToString("N").Substring(0, 8);
  • 4
    Effectively the same exact answer as Laura's, with a string format specified for no reason (has zero effect). – Jeff Swensen Apr 27 '11 at 12:42
  • There is the difference that Laura's had to explicitly remove the dashes whereas the format string provided here would let someone get a longer set of characters without removing the dashes manually afterward. – Kyle Goode Apr 28 '22 at 00:08
0

This is what I use:

public class RandomGenerator
{
    //Guid.NewGuid().ToString().Replace("-", "");
    //System.Web.Security.Membership.GeneratePassword(12, 0);

    private static string AllowChars_Numeric = "0123456789";
    private static string AllowChars_EasyUpper = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static string AllowChars_EasyLower = "0123456789abcdefghijklmnopqrstuvwxyz";
    private static string AllowChars_Upper_Lower = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    private static string AllowedChars_Difficult = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#@$^*()";

    public enum Difficulty
    {
        NUMERIC = 1,
        EASY_LOWER = 2,
        EASY_UPPER = 3,
        UPPER_LOWER = 4, 
        DIFFICULT = 5
    }

    public static string GetRandomString(int length, Difficulty difficulty)
    {
        Random rng = new Random();
        string charBox = AllowedChars_Difficult;

        switch (difficulty)
        {
            case Difficulty.NUMERIC:
                charBox = AllowChars_Numeric;
                break;
            case Difficulty.EASY_LOWER:
                charBox = AllowChars_EasyUpper;
                break;
            case Difficulty.EASY_UPPER:
                charBox = AllowChars_EasyLower;
                break;
            case Difficulty.UPPER_LOWER:
                charBox = AllowChars_Upper_Lower;
                break;
            case Difficulty.DIFFICULT:
            default:
                charBox = AllowedChars_Difficult;
                break;
        }

        char[] chars = new char[length];

        for (int i=0; i< length; i++)
        {
            chars[i] = charBox[rng.Next(0, charBox.Length)];
        }

        return new string(chars);
    }
}
Leo Muller
  • 1,421
  • 1
  • 12
  • 20