System.Web.Security.Membership.GeneratePassword
(AspNetCore
) 中的 netcoreapp1.0
有没有其他替代方案。
最简单的方法是使用足够长的
Guid.NewGuid().ToString("n")
,足以配得上密码,但它不是完全随机的。
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
。
出于安全原因,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);
}
}
不幸的是(或者可能是有意为之),即使
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);
}
}