好的,所以两周没有答案。我将用我想出的一种非常简单的方法来回答,该方法使用基本方法生成相对安全且高度可扩展的序列号。
作为一名数学家,我非常肯定有一些先进的技术可以将各种信息存储在一个序列号中——但我主要对快速和肮脏的东西感兴趣。
这里有一个幼稚的、非数学的、蛮力的技术可供考虑:
创建一个byte[]
包含要使用的字符的数组。您只能使用十六进制,但没有理由限制自己。为什么不使用整个字母数字范围减去“0”/“O”和“1”/“I”(原因很明显)。
接下来写一个函数如下(例子是C#):
byte[] genRandomSerial(int length, byte[] characters, Random r)
{
var sn = new byte[length];
for (int i = 0; i < length; i++)
sn[i] = characters[r.Next(0, characters.Length)];
return sn;
}
这会给你一个随机的序列号,我们不知道它是否有效。
下一个:
int sum(byte[] sn, MD5 md5)
{
val = 0;
foreach (byte b in md5.ComputeHash(sn))
val += (int)b;
return val;
}
进而
bool validate(byte[] sn, uint radix, uint expected, MD5 md5)
{
return (sum(sn, md5) % radix == expected);
}
我们现在拥有的是一种将 MD5 散列函数的 16 字节输出相加的方法,并评估求和模n是否等于某个x。
现在,决定要存在多少个序列号。存在的序列号越多,就越容易有人随机猜出一个有效的组合。
将您的随机序列分成块。假设 5 个 4 块,以下列形式给出 20 个字符:ABCD-EFGH-IJKL-MNOP-QRST
从您的序列号中创建 5 个阵列:
{A、B、C、D}、{E、F、G、H}、{I、J、K、L}、{M、N、O、P} 和 {Q、R、S、T} .
测试您的 5 个数组是否验证如下:
if (validate(block1, radix, expected, md5))
// This block is valid.
如果将基数设置为 2,则该块有效的概率为 1/2。如果将基数设置为 10,则该块有效的概率为 1/10。如果您有 5 个块,并且将每个基数设置为 10,则整个序列号有效的概率为 0.1^5 = 0.00001。(换句话说,每 100000 个随机序列中有 1 个是有效的。这意味着如果您使用完整的字母数字范围减去“0”/“O”、“1”/“I”,那么您有 (8 + 24)^ n * 0.00001 = ~1.2 * 10^19 个有效密钥,序列长度为 20。数量很多 - 但请记住,无论如何您都不会找到它们。基数越高,序列越安全,但生成所需的时间更长)。
请注意,“预期”应该介于 0 和 radix-1 之间。
所以现在我们有了一种验证特定序列号是否有效的方法,但是我们如何存储它是什么类型的序列号呢?事实上,我们已经有了这样做的方法。取整个随机(但经过验证)序列“sn”:
int licenseType = sum(sn, md5) % 4; // Where 4 is the number of licenses you want to have
if (licenseType == 0)
{
// Evaluation
}
else if (licenseType == 1)
{
// Standard
}
else if (licenseType == 2)
{
// Full
}
else // licenseType == 3
{
// Unrestricted
}
随着您生成越来越多的密钥,每种许可证的数量将逐渐趋于平稳。
如果您想在密钥中存储其他信息,例如到期日期,您可以使用类似的方法。例如,您可以将奇数字符的总和取模 12 来获得到期月份,并将偶数字符总和的模 31 得出到期日。
您应用的这些限制和细分越多,生成每种类型的密钥所需的时间就越长。