如果没有真正需要安全性,那么这里有一个非常快速的序列号生成器,带有一个检查器:
- 使用计数器。将其初始化为 0。当您想要一个新的序列号时,将您的计数器增加 1000;新的计数器值是序列号。检查器的工作原理是这样的:如果序列号以三个零结尾,则它是有效的。每 1000 个号码中只有一个被检查员接受。
如果此解决方案不让您满意,那么您确实需要安全性,这需要密码学。
加密安全的解决方案是使用签名的序列号:序列号是一些有效负载的编码(例如,您生成的所有序列号的计数器)和有效负载的签名。生成器有一个私钥,用于计算签名;检查器只知道相应的公钥。这个设置的问题并不在于验证时间,即使在 Javascript 中也是如此;相反,签名的大小是个问题。我假设序列号将在某些时候由用户输入。加密安全签名的最小理论大小约为 80 位(因为签名可以仅使用公钥来验证,攻击者可以尝试所有可能的位序列,我们通常要求安全级别至少为 280 )。但是,“假定为安全方案”中最小的签名接近 160 位(使用BLS,它使用配对,实现起来有点复杂)或 320 位(使用DSA或ECDSA)。有一些关于较短签名的签名系统(Quartz或McEliece-Niederreiter)的工作,但它们的安全性存在相当大的争议。
即使同时使用大写字母和数字(36 个可能的字符,并且你有“I”和“1”,还有“O”和“0”),160 位签名将使用 31 个字符。连同有效负载,您最终会得到长度为 35 左右的序列号,这对于普通用户来说可能输入太多(但幅度不大;80 位签名非常适合)。
如果你不使用签名方案,那么你必须意识到一个合理确定的攻击者将能够通过一些反汇编来绕过检查器,甚至能够学习足够的知识来产生他自己的序列号(检查器会很乐意接受)。那时,您将无法获得量化的安全性:您将无法说:“我的计划在 38 亿美元的预算内是安全的”。相反,它将是:“我的方案是安全的,直到一个足够机智和无聊的学生出现,对检查器代码进行逆向工程,并将结果发布在 Facebook 上”。
经典的非真正安全方案如下所示:
- 使用前一个方案中的计数器(以三个零结尾的计数器)。将其编码为 64 位块。使用块密码用一些硬编码的对称密钥加密该块。检查器知道密钥,并通过解密它(使用相同的硬编码对称密钥)并查看最后的零来验证序列号。
这并不比普通计数器更安全,但至少序列号看起来是随机的。对于 34 个字符的字母表(数字和大写字母,“O”和“I”除外),64 位块需要 13 个字母,这可能是可以接受的(典型的 Microsoft 序列号有 25 个字母)。对于 64 位分组密码,我推荐XTEA,它应该足够快速且易于实现。