我有此方法,该方法返回一串具有加密强度的随机字符。
首先,GetBytes()
填充一个字节数组,其值从0到255。
接下来,通过从字符集中选择字符编号{byte value} % {length of character set}
来构建返回字符串。
问题是我的大多数字符集的长度不能被256整除,因此结果将偏向某些字符。例如,如果字符集的长度为8、16或32个字符,则其余为0,则没有问题。
所以我在想-我可以限制GetBytes()
返回的值,以使字符集的长度可以被最大值均匀除尽吗?例如,如果字符集的长度为62,则最大值应为247。
我当然可以一次只得到一个字节,如果值太高,我可以再得到一个字节。但这不是很优雅。
/// <summary>
/// Returns a string of cryptographically sound random characters
/// </summary>
/// <param name="type">Accepted parameter variables are HEX (0-F), hex (0-f),
/// DEC/dec/NUM/num (0-9), ALPHA (A-Z), alpha (a-z), ALPHANUM (A-Z and 0-9),
/// alphanum (a-z and 0-9) and FULL/full (A-Z, a-z and 0-9)</param>
/// <param name="length">The length of the output string</param>
/// <returns>String of cryptographically sound random characters</returns>
public static string Serial(string type, int length)
{
if (length < 1) return "";
string chars;
switch (type)
{
case "HEX":
chars = "0123456789ABCDEF"; // 16
break;
case "hex":
chars = "0123456789abcdef"; // 16
break;
case "DEC":
case "dec":
case "NUM":
case "num":
chars = "0123456789"; // 10
break;
case "ALPHA":
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 26
break;
case "alpha":
chars = "abcdefghijklmnopqrstuvwxyz"; // 26
break;
case "ALPHANUM":
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // 36
break;
case "alphanum":
chars = "abcdefghijklmnopqrstuvwxyz0123456789"; // 36
break;
case "FULL":
case "full":
default:
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // 62
break;
}
byte[] data = new byte[length];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(length);
foreach (byte b in data)
{
result.Append(chars[b % chars.Length]);
}
return result.ToString();
}
答案 0 :(得分:1)
public static string Serial(string type, int length)
{
if (length < 1) return "";
string chars;
switch (type)
{
case "HEX":
chars = "0123456789ABCDEF"; // 16
break;
case "hex":
chars = "0123456789abcdef"; // 16
break;
case "DEC":
case "dec":
case "NUM":
case "num":
chars = "0123456789"; // 10
break;
case "ALPHA":
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 26
break;
case "alpha":
chars = "abcdefghijklmnopqrstuvwxyz"; // 26
break;
case "ALPHANUM":
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // 36
break;
case "alphanum":
chars = "abcdefghijklmnopqrstuvwxyz0123456789"; // 36
break;
case "FULL":
case "full":
default:
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // 62
break;
}
int limit = (256 / chars.Length) * chars.Length;
StringBuilder result = new StringBuilder(length);
foreach (byte b in SecureBytesInRange(limit,length))
{
result.Append(chars[b % chars.Length]);
}
return result.ToString();
}
private const int SECURE_BYTE_BUFFER_SIZE = 32;
static IEnumerable<byte> SecureBytesInRange(int exclusiveUpperBound, int countRequired)
{
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
byte[] buffer = new byte[SECURE_BYTE_BUFFER_SIZE];
int ix = SECURE_BYTE_BUFFER_SIZE;
int countProduced = 0;
while (countProduced < countRequired)
{
if (ix == SECURE_BYTE_BUFFER_SIZE)
{
crypto.GetBytes(buffer);
ix = 0;
}
while (ix < SECURE_BYTE_BUFFER_SIZE)
{
if (buffer[ix] < exclusiveUpperBound)
{
yield return buffer[ix];
countProduced++;
if (countProduced == countRequired) break;
}
ix++;
}
}
}
}
正如我在注释中也指出的那样,我将为受支持的编码类型创建一个枚举,而不是使用字符串,或者,使用命名常量/属性来返回要使用的实际字符范围,因此相反,您直接传递的是chars
而不是type
(这还使您的函数可以与其他范围的字符一起使用,从而增加了灵活性,而不仅仅是您现在能想到的那些字符。