1

我有这个方法,它返回一串加密强的随机字符。

首先,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();
}
4

1 回答 1

1

如评论中所述,拒绝抽样是执行此操作的标准方法。我们可以通过将我们对 RNG Crypto 提供程序的使用转移到一个辅助方法中来分摊一些成本,这样我们就不必逐个字节地处理它:

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

正如我在评论中指出的那样,我会为支持的编码类型创建一个枚举,而不是使用字符串,或者在替代方案中,已经命名了返回要使用的字符的实际范围的常量/属性,所以你' re 直接传递chars而不是type(这也增加了灵活性,允许您的函数与其他字符范围一起使用,而不仅仅是您当前可以想到的字符。

于 2018-08-24T08:58:03.403 回答