Generating a strong password in C#?
Asked Answered
A

10

34

I was wondering how I can generate a strong and secure password in C#.

I googled a little bit and saw this formula in Wikipedia, where L is the length of the password and N is the number of possible symbols:

alt text

Also, I've found this question, but for some reason the method Membership.GeneratePassword just returns a random number with 1 digit, which absolutely no password. All the rest solutions, were very slow (>= 0.5 secs).

I need help implementing this formula (I don't know where to start). You may also suggest another solution or explain why the GeneratePassword isn't working.

Appendicular answered 15/2, 2010 at 14:58 Comment(0)
K
88

I just tried the following in linqpad:

System.Web.Security.Membership.GeneratePassword(25, 10)

This is the password I got:

[XTJ_67g.i/ag1rL)6_Yv>*+%

Or, if that's not secure enough, try this:

System.Web.Security.Membership.GeneratePassword(128, 100)

which got me the following when running it three times:

|c^.:?m)#q+(]V;}[Z(})/?-;$]+@!|^/8*_9.$&.&!(?=^!Wx?[@%+&-@b;)>N;&+*w[>$2+_$%l;+h+#zhs^{e?&=*(}X_%|:}]]}*X[+)Er%J/-=;Q0{:+=%c7:^$

/:_)hxF+*){2|;(>:*N^+!_&|}B.$})?[V=[+v({-:-@9-Z$j?.[-}(@MHx+}(}Mz_S(7#4}{..>@G|!+++{+C=|_}=+r^@&$0;L*|kz-;$++/N3$=}?;%&]]*/^#^!+

:*{]-x^$g{|?*))_=B@^.#%L;g|+)#[nq}?y(_(m;]S^I$*q=l-[_/?}&-!k^(+[_{Z|&:^%!_)!=p%=)=wYd-#.UP$%s1{*l%+[%?!c+7=@=.;{+M)!^}&d/]{];(&}

this took way less than a second, btw. The framework is your friend.

See http://msdn.microsoft.com/en-us/library/system.web.security.membership.generatepassword.aspx

Kylie answered 15/2, 2010 at 15:4 Comment(16)
Please note the editor doesn't play friendly with some of the characters in the password.Kylie
As I said, this is a great method but is just returns a single digit in my computer, and in many other computers.Appendicular
@alon you're doing it wrong. Trust me. This method works and is bulletproof on any .NET installation on any computer.Kylie
@AlonGubkin Are you passing in 1 as the first param value?Hobbledehoy
@billpg: You know, this question is over two years old, and is answered, right?Kylie
@Will - I wouldn't call "you're doing it wrong" much of an answer. :)Hobbledehoy
@billpg: OP did! And I think its an awesome answer full of virtue and wisdom. So there.Kylie
Is there an alternative not dependent on the System.Web... namespace?Lovmilla
@Lovmilla I think that Membership.GeneratePassword is based on System.Security.Cryptography.RNGCryptoServiceProvider, so you can implement your own GeneratePassword with that. Here is an example: obviex.com/Samples/Password.aspxAppendicular
Thanks, I found a decent alternative :)Lovmilla
While this is strong in the maths sense - it completely overlooks the human element. Any password in this format is going to have to be written down or stored which opens a whole heap more issues. Better to use a pass phrase "HelpMeObiWanKanobieYou'reMyOnlyHope" ...Pinckney
"This method works and is bulletproof on any .NET installation" Except the assembly isn't available with .NET Core and isn't part of .NET Standard..Wolenik
ther is no System.Security.Cryptography.RNGCryptoServiceProvider.Membership nor System.Web.SecurityHypabyssal
@AdamWilliams you'll have to forgive user1228 for not being able to see the future since .net core and .net standard did not exist back then...Supersaturated
Old answers that are based on legacy .NET should be downvoted, commented on and buried as appropriate. Times change, and an answer that was useful back in 2010 may be completely useless in 2023. StackOverflow should be timeless, and answers revised over time to be relevant to today's technology.Wolenik
@Adam Williams most absurd commentary I've read here in my entire life. For that rule of three, let's burn and forget all questions and solutions about any technology that has a better replacement today, like those shitty codes from Batch-Script or from VBS or from VB6 and even from C/C++ considering it exists the modern VC++, right?. My god. Let's burn and hide all the entire .NET framework class library because it just exists .NET Core and everybody use it (irony).Drudgery
R
16

Not sure where I found this but here's a class to generate high entropy, truly random strings that can be used as passwords.

using System.Security.Cryptography;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class PasswordGenerator
{
    public int MinimumLengthPassword { get; private set; }
    public int MaximumLengthPassword { get; private set; }
    public int MinimumLowerCaseChars { get; private set; }
    public int MinimumUpperCaseChars { get; private set; }
    public int MinimumNumericChars { get; private set; }
    public int MinimumSpecialChars { get; private set; }

    public static string AllLowerCaseChars { get; private set; }
    public static string AllUpperCaseChars { get; private set; }
    public static string AllNumericChars { get; private set; }
    public static string AllSpecialChars { get; private set; }
    private readonly string _allAvailableChars;

    private readonly RandomSecureVersion _randomSecure = new RandomSecureVersion();
    private int _minimumNumberOfChars;

    static PasswordGenerator()
    {
        // Define characters that are valid and reject ambiguous characters such as ilo, IO and 1 or 0
        AllLowerCaseChars = GetCharRange('a', 'z', exclusiveChars: "ilo");
        AllUpperCaseChars = GetCharRange('A', 'Z', exclusiveChars: "IO");
        AllNumericChars = GetCharRange('2', '9');
        AllSpecialChars = "!@#%*()$?+-=";

    }

    public PasswordGenerator(
        int minimumLengthPassword = 15,
        int maximumLengthPassword = 20,
        int minimumLowerCaseChars = 2,
        int minimumUpperCaseChars = 2,
        int minimumNumericChars = 2,
        int minimumSpecialChars = 2)
    {
        if (minimumLengthPassword < 15)
        {
            throw new ArgumentException("The minimumlength is smaller than 15.",
                "minimumLengthPassword");
        }

        if (minimumLengthPassword > maximumLengthPassword)
        {
            throw new ArgumentException("The minimumLength is bigger than the maximum length.",
                "minimumLengthPassword");
        }

        if (minimumLowerCaseChars < 2)
        {
            throw new ArgumentException("The minimumLowerCase is smaller than 2.",
                "minimumLowerCaseChars");
        }

        if (minimumUpperCaseChars < 2)
        {
            throw new ArgumentException("The minimumUpperCase is smaller than 2.",
                "minimumUpperCaseChars");
        }

        if (minimumNumericChars < 2)
        {
            throw new ArgumentException("The minimumNumeric is smaller than 2.",
                "minimumNumericChars");
        }

        if (minimumSpecialChars < 2)
        {
            throw new ArgumentException("The minimumSpecial is smaller than 2.",
                "minimumSpecialChars");
        }

        _minimumNumberOfChars = minimumLowerCaseChars + minimumUpperCaseChars +
                                minimumNumericChars + minimumSpecialChars;

        if (minimumLengthPassword < _minimumNumberOfChars)
        {
            throw new ArgumentException(
                "The minimum length of the password is smaller than the sum " +
                "of the minimum characters of all catagories.",
                "maximumLengthPassword");
        }

        MinimumLengthPassword = minimumLengthPassword;
        MaximumLengthPassword = maximumLengthPassword;

        MinimumLowerCaseChars = minimumLowerCaseChars;
        MinimumUpperCaseChars = minimumUpperCaseChars;
        MinimumNumericChars = minimumNumericChars;
        MinimumSpecialChars = minimumSpecialChars;

        _allAvailableChars =
            OnlyIfOneCharIsRequired(minimumLowerCaseChars, AllLowerCaseChars) +
            OnlyIfOneCharIsRequired(minimumUpperCaseChars, AllUpperCaseChars) +
            OnlyIfOneCharIsRequired(minimumNumericChars, AllNumericChars) +
            OnlyIfOneCharIsRequired(minimumSpecialChars, AllSpecialChars);
    }

    private string OnlyIfOneCharIsRequired(int minimum, string allChars)
    {
        return minimum > 0 || _minimumNumberOfChars == 0 ? allChars : string.Empty;
    }

    public string Generate()
    {
        var lengthOfPassword = _randomSecure.Next(MinimumLengthPassword, MaximumLengthPassword);

        // Get the required number of characters of each catagory and 
        // add random charactes of all catagories
        var minimumChars = GetRandomString(AllLowerCaseChars, MinimumLowerCaseChars) +
                        GetRandomString(AllUpperCaseChars, MinimumUpperCaseChars) +
                        GetRandomString(AllNumericChars, MinimumNumericChars) +
                        GetRandomString(AllSpecialChars, MinimumSpecialChars);
        var rest = GetRandomString(_allAvailableChars, lengthOfPassword - minimumChars.Length);
        var unshuffeledResult = minimumChars + rest;

        // Shuffle the result so the order of the characters are unpredictable
        var result = unshuffeledResult.ShuffleTextSecure();
        return result;
    }

    private string GetRandomString(string possibleChars, int lenght)
    {
        var result = string.Empty;
        for (var position = 0; position < lenght; position++)
        {
            var index = _randomSecure.Next(possibleChars.Length);
            result += possibleChars[index];
        }
        return result;
    }

    private static string GetCharRange(char minimum, char maximum, string exclusiveChars = "")
    {
        var result = string.Empty;
        for (char value = minimum; value <= maximum; value++)
        {
            result += value;
        }
        if (!string.IsNullOrEmpty(exclusiveChars))
        {
            var inclusiveChars = result.Except(exclusiveChars).ToArray();
            result = new string(inclusiveChars);
        }
        return result;
    }
}

internal static class Extensions
{
    private static readonly Lazy<RandomSecureVersion> RandomSecure =
        new Lazy<RandomSecureVersion>(() => new RandomSecureVersion());
    public static IEnumerable<T> ShuffleSecure<T>(this IEnumerable<T> source)
    {
        var sourceArray = source.ToArray();
        for (int counter = 0; counter < sourceArray.Length; counter++)
        {
            int randomIndex = RandomSecure.Value.Next(counter, sourceArray.Length);
            yield return sourceArray[randomIndex];

            sourceArray[randomIndex] = sourceArray[counter];
        }
    }

    public static string ShuffleTextSecure(this string source)
    {
        var shuffeldChars = source.ShuffleSecure().ToArray();
        return new string(shuffeldChars);
    }
}

internal class RandomSecureVersion
{
    //Never ever ever never use Random() in the generation of anything that requires true security/randomness
    //and high entropy or I will hunt you down with a pitchfork!! Only RNGCryptoServiceProvider() is safe.
    private readonly RNGCryptoServiceProvider _rngProvider = new RNGCryptoServiceProvider();

    public int Next()
    {
        var randomBuffer = new byte[4];
        _rngProvider.GetBytes(randomBuffer);
        var result = BitConverter.ToInt32(randomBuffer, 0);
        return result;
    }

    public int Next(int maximumValue)
    {
        // Do not use Next() % maximumValue because the distribution is not OK
        return Next(0, maximumValue);
    }

    public int Next(int minimumValue, int maximumValue)
    {
        var seed = Next();

        //  Generate uniformly distributed random integers within a given range.
        return new Random(seed).Next(minimumValue, maximumValue);
    }
}

Consume in your code thusly:

var generator = new PasswordGenerator();
string password = generator.Generate();
Console.WriteLine(password);
Remus answered 16/10, 2014 at 14:13 Comment(1)
This took a little bit massaging, but is a solid start.Millrace
A
10

To address your question about that formula:

The formula is saying that a password of length L drawn from an alphabet of N symbols is equivalent to a password of length H drawn from an alphabet of two symbols. So if you have, say, 64 symbols (say abc...xyzABC...XYZ01...89_!) and the password is 10 characters long, then that gives you equivalent security to a password 10 log2 64 = 60 characters long drawn from the alphabet "ab".

A "log" is the inverse operation of exponentiation. Two to the sixth power gives you sixty-four, therefore the "log two" of sixty-four gives you six.

Anabiosis answered 15/2, 2010 at 15:16 Comment(0)
B
4

I don't know if this will help you, but this is what I use when I want to generate a random password which is also strong. It's quick and simple to implement/understand and isn't as much of an overkill as the one through the membership provider above...

    private string Token(byte Length) {
        char[] Chars = new char[] {
            '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',
            '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',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
        };
        string String = string.Empty;
        Random Random = new Random();

        for (byte a = 0; a < Length; a++) {
            String += Chars[Random.Next(0, 61)];
        };

        return (String);
    }
Balladeer answered 21/2, 2010 at 7:30 Comment(4)
System.Random should never be used for generating passwords its more predictable and isn't intended for that purpose. Instead use System.Security.Cryptography.RNGCryptoServiceProvider See https://mcmap.net/q/450738/-how-random-is-system-random-in-net-3 and https://mcmap.net/q/22156/-unique-random-string-generationMaritamaritain
I would not recommend roll-your-own for security design. Using a built-in security framework like .net's will gives you thoroughly tested code thought-through by cryptogrophy experts. It's easy to make a lot of mistakes on your own.Halbert
I like the comment in the code from Richard's answer: Never ever ever never use Random() in the generation of anything that requires true security/randomness and high entropy or I will hunt you down with a pitchfork!! Only RNGCryptoServiceProvider() is safe.Potluck
I agree with those who commented before me. And one more thing: because of Random.Next(0,61) there will never be '9' in the generated string. Regarding to the definition of Random.Next the generated number is always less then 61. Better to use Random.Next(0,Chars.Length)Mcgehee
B
1

Why not just fill an array with some characters and pick on random a number of them. You can divide them in groups to be sure that are include letters numbers and special characters.

You will also have to pick a proper length and how much of every group of characters to include and that's it. I don't think you need some sophisticated formulas.

Bendicty answered 15/2, 2010 at 15:0 Comment(6)
Nothing is... But if you limit your logins to 5 logins per hour say you reduce the risk of the brute force attack.Helyn
What could be harder than picking random chars ?Bendicty
@Bendicty I agree, Alon whats this for?Helyn
Nope, I'm not going to limit my logins per hour, it is very irrating to the user. Instead, I'm going to put CAPTCHA after 3 tries.Appendicular
@Alon that method will work in the same way. So brite force attacks have been restricted.Helyn
@Alon, after reading all of these I am curious why you need something so overkill? In my applications, which deal with a ton of personal customer information, I just use the password generator I posted below with varying length. Its nice to see you're interested in securing your application, but you have to keep ease of use in mind as well. Ultimately if someone wants to hack you, they'll manage to do it somehow no matter how strong a password you have.Balladeer
H
1

For systems that don't allow user-generated passwords it's very easy, actually: Any password is as secure as it's long. Not counting, of course, people who tack post-its to monitors, etc.

You probably want to maximize the set of characters from which the password is generated. But restricting the generated passwords greatly reduces the search space and therefore makes the password less secure. Again, this only holds if the user can't choose their own password.

If you deal with both generated and user-created passwords, then all bets are off, obviously. You then probably want to generate the passwords in a way that it uses as many characters from different classes as possible, resembling a strong user-chosen password. Ideally it should conform to the same constraints that the user-created password has to pass as well (if any).

Hirohito answered 15/2, 2010 at 15:4 Comment(0)
C
1

I used random string from characters like below

public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789&?%$@";
        return new string(Enumerable.Repeat(chars, length)
          .Select(s => s[Random.Next(s.Length)]).ToArray());
    }
Cioban answered 4/11, 2022 at 18:25 Comment(1)
This won't uses a cryptographically secure random, recommend to use RandomNumberGenerator instead of RandomKor
S
0

I originally used random strings similar to this answer but got flagged by SonarQube. Random.Next is considered as Insecure Randomness. SonarQube suggests generating a cryptographically strong pseudo-random number instead.

var randomGenerator = RandomNumberGenerator.Create();
var data = new byte[16];
randomGenerator.GetBytes(data);
return BitConverter.ToString(data);
Sharmainesharman answered 16/11, 2023 at 4:8 Comment(1)
Good point, but this should be a comment, not a answerKor
L
0

I liked @user1228 so I think this is a decent equivalent for .net core

public static class PasswordGenerator
{
    public static string GeneratePassword(int length, int numberOfNonAlphanumericCharacters)
    {
        const string alphanumericCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        const string nonAlphanumericCharacters = "!@#$%^&*()_-+=[{]};:<>|./?";

        var randomBytes = new byte[length];
        var chars = new char[length];
        int nonAlphanumericCount = 0;

        using (var rng = new RNGCryptoServiceProvider())
        {
            rng.GetBytes(randomBytes);
        }

        for (int i = 0; i < length; i++)
        {
            if (nonAlphanumericCount < numberOfNonAlphanumericCharacters)
            {
                int rnd = randomBytes[i] % nonAlphanumericCharacters.Length;
                chars[i] = nonAlphanumericCharacters[rnd];
                nonAlphanumericCount++;
            }
            else
            {
                int rnd = randomBytes[i] % alphanumericCharacters.Length;
                chars[i] = alphanumericCharacters[rnd];
            }
        }

        return new string(chars.OrderBy(s => Guid.NewGuid()).ToArray());
    }
}

You can use it with

string password = PasswordGenerator.GeneratePassword(25, 10);
Lactobacillus answered 26/4, 2024 at 14:36 Comment(0)
L
-1

The following is pretty quick and works well.

  1. Random special characters for the required length divided by 4
  2. Random lowercase characters for the required length divided by 3
  3. Random uppercase characters for the required length divided by 3
  4. Random numerical characters for the required length divided by 2
  5. Concatenate the 4 random arrays
  6. Take random characters from the concatenated array for the required length
  7. Return the new string

Note the Random is a global static variable.

public static Random random = new Random();

public static string RandomString(int length)
{
    var specialLength = (int)Math.Ceiling(length / 4d);
    var lowerUpperLength = (int)Math.Ceiling(length / 3d);
    var numericLength = (int)Math.Ceiling(length / 2d);

    var special = Enumerable.Repeat("!@#$%^&|+-.,?", specialLength)
        .Select(chars => chars[random.Next(chars.Length)]).ToArray();
    var lower = Enumerable.Repeat("abcdefghijklmnopqrstuvwxyz", lowerUpperLength)
        .Select(chars => chars[random.Next(chars.Length)]).ToArray();
    var upper = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", lowerUpperLength)
        .Select(chars => chars[random.Next(chars.Length)]).ToArray();
    var numeric = Enumerable.Repeat("0123456789", numericLength)
        .Select(chars => chars[random.Next(chars.Length)]).ToArray();
    
    var scrambledConcat = special.Concat(lower)
        .Concat(upper).Concat(numeric)
        .ToArray();
    
    var scrambledChars = Enumerable.Repeat(scrambledConcat, length)
        .Select(chars => chars[random.Next(chars.Length)]).ToArray();
    
    return new string(scrambledChars);
}

Output 5X examples for a length of 6:

@n.c7c

&T,T0S

2b$9$H

AEEcE0

6x6VB3

Output 5X examples for a length of 20:

41@8?@K4@y@uSu$K31zS

L750T01T#00A17Tq5O+^

%#z#@BEBEbG4xU2AUx26

Ry90j4RyW6.VBRV0-60!

Z84SJ!t075%7a8!nn84M

Output 5X examples for a length of 100:

!9i7vz297Dlq$ffL35qzi4j63UJ9Nv53^9K@6-t6N17@jy73@VK80^y423H1bLiaHx0q9Ba5b?&9@4154lo?$6@e5L9e6-B00X63

-Ykk9%Mt08Ky4TW426rI53k12F#z6G8WWuR0|Bh?w,mY4XkU2eA8%Wz565to5m42Z6|84%UF-^8N3Uv#$72#d65#BkNtcn%3i8M.

f58vaQg0@VB6sgXeChud@^8f1e63q3e@68ep0d6Eg,861Xq8@dgBZfs2L3d3@7$djZZ232Q&V6q?psX$VB6tSsqZ3HVZk67qA06q

&uZecy^nA?2m4a#&M4?M9%hj7d1dmPUqlIiUaj4Z4zKZInG33uQk161sD4?m4e3B^mKu22n0h1uPJ&#97UQ^ys519^XE1,9UE+4J

4%q,B#0Vi5%BjfG@43Mo^k6+24P3ek#32L94s3%!Yki3^53%iCmP,.K%4+m.bw06K,JfU4e2P8WJ0^%40%^K150iz44-0fn?U8L0

Feel free to add or remove characters in the strings.

Limpkin answered 10/4, 2023 at 17:37 Comment(1)
This won't uses a cryptographically secure random, recommend to use RandomNumberGenerator instead of RandomKor

© 2022 - 2025 — McMap. All rights reserved.