使用字母数字子集生成100万个唯一的随机密钥

3
我希望生成100万个随机的唯一的字母数字密钥,并将它们存储在数据库中。每个密钥长度为8个字符,仅使用子集“abcdefghijk n pqrstuvxyz和0-9”。
字母l,m,o和w被舍弃了。由于每个密钥将在一个非常小的空间内打印在产品上,因此舍弃m和w是因为有限的打印空间。删除m和w使得字母大小增加了2pt,提高了可读性。字母l和o被删除是因为它们在当前打印尺寸下很容易与1、i和0混淆。我们进行了一些测试,发现1、i和0的字符总是正确识别,而l和o则错误率较高。大写字母之所以被省略,是出于和“m和w”同样的原因。
那么为什么不采用顺序呢?有几个原因:密钥可以在之后进行注册,我们不想让任何人猜测下一个密钥序列并注册别人的密钥。外观:我们不需要客户和竞争对手知道我们只发货了几千个密钥。
是否有一种实用的方法来生成密钥,确保每个密钥的唯一性并将其存储在数据库中?谢谢!

Gartman写道:“外表上,我们不需要顾客和竞争对手知道我们只出货了几千个钥匙。”从我的经验来看,他们发现的。 - starbolin
秘诀在于获得重复数据的均匀分布。 - starbolin
@starbolin - 当然,您可以生成唯一的键而不会有任何重复。 - Security Hound
@Ramhound 是的,当时我没有算好,以为他想要重复使用密钥。我没有意识到他产生的超额数量有多大。其他人已经讨论过这个话题了,所以我退出了。 - starbolin
5个回答

6

编辑:@CodeInChaos指出了一个问题:System.Random不够安全,很容易就能复制出序列。在这里我已经将Random替换为更安全的生成器:

var possibilities = "abcdefghijknpqrstuvxyz0123456789".ToCharArray();
int goal = 1000000;
int codeLength = 8;
var codes = new HashSet<string>();
var random = new RNGCryptoServiceProvider();
while (codes.Count < goal)
{
    var newCode = new char[codeLength];
    for (int i = 0; i < codeLength; i++)
        newCode[i] = possibilities[random.Next(possibilities.Length)];
    codes.Add(new string(newCode));
}
// now write codes to database

static class Extensions
{
    public static byte Next(this RNGCryptoServiceProvider provider, byte maximum)
    {
        var b = new byte[1];
        while (true)
        {
            provider.GetBytes(b);
            if (b[0] < maximum)
                return b[0];
        }
    }
}

(Next方法并不是很快,但对于您的目的可能已经足够)

这是一个很好的答案,但有一个主要问题。这不能保证唯一值。当然,生成随机密钥会很好。您可以通过在for loop内使用while loop来改进代码,条件是生成的字符串在HashSet中不存在。 - Security Hound
1
实际上,它确保唯一值。HashSet<string>.Add在实际添加值之前检查集合中是否存在该值。它返回一个bool,所以你可以知道,但我们真正需要知道的是当我们达到100万个键的目标时。 - Tim S.
每天学点新东西。=) - Tim S.
感谢您的回答,Tim S。我一定会仔细阅读您的解决方案。 - Gartman
你的代码没有满足其中一个要求,即“我们不希望任何人猜测序列中的下一个密钥”。如果攻击者知道算法并观察到单个代码,则可以计算使用你的代码生成的所有过去和未来代码。 - CodesInChaos
显示剩余2条评论

1
有没有一种实用的方法来生成密钥,确保每个密钥的唯一性并将它们存储在数据库中?
由于这是单个操作,您可以简单地执行以下操作:
1)生成一个单一的密钥 2)验证所生成的密钥不存在于数据库中。 3)如果存在,则生成一个新的密钥。 3b)如果不存在,则将其写入数据库 4)返回步骤1
当然还有其他选择,最终归结为生成密钥并确保其不存在于数据库中。
理论上,您可以生成1000万个密钥(以节省处理能力),将它们写入文件。 一旦生成了密钥,只需查看每个密钥并查看它是否已存在于数据库中。 您可能可以编写一个工具,在不到48小时内完成此操作。

1

我曾经遇到过类似的问题...我的解决方法是创建一个唯一的序列YYYY/MM/DD/HH/MM/SS/millis/nano,然后获取它的哈希码。之后我将哈希码用作密钥。你的客户和竞争对手将无法猜测下一个值。这可能不是完全可靠的,但在我的情况下已经足够了!


这将生成哈希码,其中包含他的要求中的“非法”字符。此外... 这基本上就是 Random 类的工作原理。 - Security Hound
Ramhound,“Random”根本不使用哈希。 - Joey

1

现在1百万并不算多,您可能可以在单台机器上很快完成这个操作。毕竟这只是一次性的操作。

  1. 使用哈希表(或哈希集)
  2. 生成随机密钥并将其作为键(或直接作为集合)放入其中,直到计数达到1百万
  3. 将它们写入数据库

我的快速测试代码如下:

function new-key {-join'abcdefghijknpqrstuvxyz0123456789'[(0..7|%{random 32})]}
$keys = @{}
for(){$keys[(new-key)]=1}

但是PowerShell很慢,所以我期望C++或C#在这里表现得非常出色。


0
要获取随机字符串,您可以使用类似于以下代码的代码:
Random rand = new Random(new DateTime().Millisecond);
String[] possibilities = {"a","b","c","d","e","f","g","h","i","j","k",
    "l","n","p","q","r","s","t","u","v","x","y","z","0","1","2","3","4",
    "5","6","7","8","9"};
for (int i = 0; i < 1000000; ++i)
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    for (int j = 0; j < 8; ++j)
    {
        sb.Append(possibilities[rand.Next(possibilities.Length)]);
    }
    if (!databaseContains(sb.ToString()))
        databaseAdd(sb.ToString());
    else
        --i;
}

1
你可以追加字符,所以你只需要写char[] possibilities = "...".ToCharArray()。这样更容易阅读和编写。 - Joey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接