替代 aspnetcore 中的 System.Web.Security.Membership.GeneratePassword (netcoreapp1.0)

问题描述 投票:0回答:3

System.Web.Security.Membership.GeneratePassword
(
AspNetCore
) 中的
netcoreapp1.0
有没有其他替代方案。

最简单的方法是使用足够长的

Guid.NewGuid().ToString("n")
,足以配得上密码,但它不是完全随机的。

asp.net asp.net-mvc asp.net-core asp.net-core-1.0 .net-standard-1.5
3个回答
95
投票

这是一个类/方法,基于

Membership.GeneratePassword
的来源,适用于 .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);
        }
    }
}

我省略了

do...while
 上的 
CrossSiteScriptingValidation.IsDangerousString
循环。如果需要,您可以将其添加回自己。

你这样使用它:

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

另外,请确保引用

System.Security.Cryptography.Algorithms


4
投票

出于安全原因,System.Random 在使用时无法提供足够的熵。

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

为什么要使用 C# 类 System.Random 而不是 System.Security.Cryptography.RandomNumberGenerator?

请参阅下面的示例,了解更安全的 @khellang 版本

    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);
        }
    }

0
投票

不幸的是(或者可能是有意为之),即使

numberOfNonAlphanumericCharacters
设置为 0,该线程中的两个解决方案仍然会生成带有符号的密码。此外,生成的符号数量很可能会高于
numberOfNonAlphanumericCharacters

所以我调整了@ReneLombard的答案并进行了调整以确保输出与输入变量一致。

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

    public string Generate(int length = 64, int maximumNumberOfSymbolsInPassword = 0)
    {
        ArgumentOutOfRangeException.ThrowIfLessThan(length, 1);
        ArgumentOutOfRangeException.ThrowIfGreaterThan(length, 128);
        ArgumentOutOfRangeException.ThrowIfLessThan(maximumNumberOfSymbolsInPassword, 0);
        ArgumentOutOfRangeException.ThrowIfGreaterThan(maximumNumberOfSymbolsInPassword, length);

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

        for (var i = 0; i < length; i++)
        {
            var idx = byteBuffer[i] % 62;
            switch (idx)
            {
                case < 10:
                    characterBuffer[i] = (char)('0' + idx);
                    break;
                case < 36:
                    characterBuffer[i] = (char)('A' + idx - 10);
                    break;
                default:
                    characterBuffer[i] = (char)('a' + idx - 36);
                    break;
            }
        }

        // Replace characters in the buffer with symbols, note that this might generate
        // fewer passwords than is specified in maximumNumberOfSymbolsInPassword if
        // the same k index value is chosen more than once
        for (var i = 0; i < maximumNumberOfSymbolsInPassword; i++)
        {
            int k;
            do
            {
                k = GetRandomInt(rng, length);
            }
            while (!char.IsLetterOrDigit(characterBuffer[k]));
            characterBuffer[k] = symbols[GetRandomInt(rng, symbols.Length)];
        }

        return new string(characterBuffer);
    }

    private static int GetRandomInt(RandomNumberGenerator randomGenerator, int maxInput)
    {
        var buffer = new byte[4];
        randomGenerator.GetBytes(buffer);
        return Math.Abs(BitConverter.ToInt32(buffer) % maxInput);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.