Alternative to System.Web.Security.Membership.GeneratePassword in aspnetcore (netcoreapp1.0)
Asked Answered
U

2

48

Is there any alternative to System.Web.Security.Membership.GeneratePassword in AspNetCore (netcoreapp1.0).

The easiest way would be to just use a Guid.NewGuid().ToString("n") which is long enough to be worthy of a password but it's not fully random.

Uremia answered 17/8, 2016 at 11:28 Comment(2)
What does System.Web.Security.Membership.GeneratePassword do that you don't want?Athenaathenaeum
All that method does is generate a random password, that super easy to do yourself.Cady
H
94

Here's a class/method, based on the source of Membership.GeneratePassword of that works on .NET Core:

public static class Password
{
    private static readonly char[] Punctuations = "!@#$%^&*()_-+=[{]};:>|./?".ToCharArray();

    public static string Generate(int length, int numberOfNonAlphanumericCharacters)
    {
        if (length < 1 || length > 128)
        {
            throw new ArgumentException(nameof(length));
        }

        if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0)
        {
            throw new ArgumentException(nameof(numberOfNonAlphanumericCharacters));
        }

        using (var rng = RandomNumberGenerator.Create())
        {
            var byteBuffer = new byte[length];

            rng.GetBytes(byteBuffer);

            var count = 0;
            var characterBuffer = new char[length];

            for (var iter = 0; iter < length; iter++)
            {
                var i = byteBuffer[iter] % 87;

                if (i < 10)
                {
                    characterBuffer[iter] = (char)('0' + i);
                }
                else if (i < 36)
                {
                    characterBuffer[iter] = (char)('A' + i - 10);
                }
                else if (i < 62)
                {
                    characterBuffer[iter] = (char)('a' + i - 36);
                }
                else
                {
                    characterBuffer[iter] = Punctuations[i - 62];
                    count++;
                }
            }

            if (count >= numberOfNonAlphanumericCharacters)
            {
                return new string(characterBuffer);
            }

            int j;
            var rand = new Random();

            for (j = 0; j < numberOfNonAlphanumericCharacters - count; j++)
            {
                int k;
                do
                {
                    k = rand.Next(0, length);
                }
                while (!char.IsLetterOrDigit(characterBuffer[k]));

                characterBuffer[k] = Punctuations[rand.Next(0, Punctuations.Length)];
            }

            return new string(characterBuffer);
        }
    }
}

I've omitted the do...while loop over the CrossSiteScriptingValidation.IsDangerousString. You can add that back in yourself if you need it.

You use it like this:

var password = Password.Generate(32, 12);

Also, make sure you reference System.Security.Cryptography.Algorithms.

Hipparchus answered 17/8, 2016 at 13:7 Comment(6)
This was very helpful. Is there some neat way to add the rest of nonalphanumeric characters, [cause you omitted some ;)]? Creating StringRaw class or something?Disrespectful
This method (as the one in the framework) has a bias due to the use of the modulo in byte % 87. Might not be a problem for most use cases, but something to keep in mind. Bias changes if characters are added to or removed from the Punctuations array.Derogatory
This will throw, with the punctuation list in this length, if i == 86 as you will get an out of index error.Manyplies
@Manyplies Hah, Microsoft probably never got the bug report because (I'm assuming) very few people generate passwords longer than 85 characters :PHipparchus
i is not connected to the password length, but to the length of your special signs. We ended up doing something like var i = byteBuffer[iter] % Punctuations.length + 62;Manyplies
I'm wondering if Rene's answer below should be merged into khellangs answer?Samaniego
T
3

System.Random doesn't provide enough entropy when used for security reasons.

https://cwe.mitre.org/data/definitions/331.html

Why use the C# class System.Random at all instead of System.Security.Cryptography.RandomNumberGenerator?

Please see the example below for a more secure version of @khellang version

    public static class Password
    {
        private static readonly char[] Punctuations = "!@#$%^&*()_-+[{]}:>|/?".ToCharArray();
        public static string Generate(int length, int numberOfNonAlphanumericCharacters)
        {
            if (length < 1 || length > 128)
            {
                throw new ArgumentException("length");
            }

            if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0)
            {
                throw new ArgumentException("numberOfNonAlphanumericCharacters");
            }

            using (var rng = RandomNumberGenerator.Create())
            {
                var byteBuffer = new byte[length];
               
                rng.GetBytes(byteBuffer);

                var count = 0;
                var characterBuffer = new char[length];

                for (var iter = 0; iter < length; iter++)
                {
                    var i = byteBuffer[iter] % 87;

                    if (i < 10)
                    {
                        characterBuffer[iter] = (char)('0' + i);
                    }
                    else if (i < 36)
                    {
                        characterBuffer[iter] = (char)('A' + i - 10);
                    }
                    else if (i < 62)
                    {
                        characterBuffer[iter] = (char)('a' + i - 36);
                    }
                    else
                    {
                        characterBuffer[iter] = Punctuations[GetRandomInt(rng, Punctuations.Length)];
                        count++;
                    }
                }

                if (count >= numberOfNonAlphanumericCharacters)
                {
                    return new string(characterBuffer);
                }

                int j;
                
                for (j = 0; j < numberOfNonAlphanumericCharacters - count; j++)
                {
                    int k;
                    do
                    {
                        k = GetRandomInt(rng, length);
                    }
                    while (!char.IsLetterOrDigit(characterBuffer[k]));

                    characterBuffer[k] = Punctuations[GetRandomInt(rng, Punctuations.Length)];
                }

                return new string(characterBuffer);
            }
        }

        private static int GetRandomInt(RandomNumberGenerator randomGenerator)
        {
            var buffer = new byte[4];
            randomGenerator.GetBytes(buffer);

            return BitConverter.ToInt32(buffer);
        }
        private static int GetRandomInt(RandomNumberGenerator randomGenerator, int maxInput)
        {
            return Math.Abs(GetRandomInt(randomGenerator) % maxInput);
        }
    }
Tallboy answered 1/8, 2022 at 11:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.