许可证密钥/序列号生成器和检查器

信息安全 密码学 电子签名 随机的 许可证执行
2021-09-05 00:56:57

我需要一个序列号生成器和随附的检查器。我希望能够设置一个盐(也许是一个长度)。生成器应该只生成通过检查器测试的序列号。这些数字应该只占给定长度的所有可能数字的一小部分。

该算法不需要是密码安全的。相反,它应该很容易实现(在 javascript 中)并且应该非常快。

澄清一下:如果您购买商业软件,它有时会受到序列号/密钥的保护。如果你输入它,软件会通过算法验证它(通过检查它是否满足某些属性),而不是查找一个巨大的数据库。我也很确定密钥都是通过算法而不是手动生成的。并且在给定长度的所有可能密钥中,只有一小部分实际上是有效的,因此很难猜测密钥。

Salt:我不知道salt这个词是否正确,但是算法应该至少有一个参数,它的选择会改变生成的密钥(这样多人可以使用相同的算法而不必担心冲突)。

3个回答

如果没有真正需要安全性,那么这里有一个非常快速的序列号生成器,带有一个检查器:

  • 使用计数器。将其初始化为 0。当您想要一个新的序列号时,将您的计数器增加 1000;新的计数器值是序列号。检查器的工作原理是这样的:如果序列号以三个零结尾,则它是有效的。每 1000 个号码中只有一个被检查员接受。

如果此解决方案不让您满意,那么您确实需要安全性,这需要密码学。

加密安全的解决方案是使用签名的序列号:序列号是一些有效负载的编码(例如,您生成的所有序列号的计数器)和有效负载的签名。生成器有一个私钥,用于计算签名;检查器只知道相应的公钥。这个设置的问题并不在于验证时间,即使在 Javascript 中也是如此;相反,签名的大小是个问题。我假设序列号将在某些时候由用户输入。加密安全签名的最小理论大小约为 80 位(因为签名可以仅使用公钥来验证,攻击者可以尝试所有可能的位序列,我们通常要求安全级别至少为 280 )。但是,“假定为安全方案”中最小的签名接近 160 位(使用BLS,它使用配对,实现起来有点复杂)或 320 位(使用DSAECDSA)。有一些关于较短签名的签名系统(QuartzMcEliece-Niederreiter)的工作,但它们的安全性存在相当大的争议。

即使同时使用大写字母和数字(36 个可能的字符,并且你有“I”和“1”,还有“O”和“0”),160 位签名将使用 31 个字符。连同有效负载,您最终会得到长度为 35 左右的序列号,这对于普通用户来说可能输入太多(但幅度不大;80 位签名非常适合)。

如果你不使用签名方案,那么你必须意识到一个合理确定的攻击者能够通过一些反汇编来绕过检查器,甚至能够学习足够的知识来产生他自己的序列号(检查器会很乐意接受)。那时,您将无法获得量化的安全性:您将无法说:“我的计划在 38 亿美元的预算内是安全的”。相反,它将是:“我的方案是安全的,直到一个足够机智和无聊的学生出现,对检查器代码进行逆向工程,并将结果发布在 Facebook 上”。

经典的非真正安全方案如下所示:

  • 使用前一个方案中的计数器(以三个零结尾的计数器)。将其编码为 64 位块。使用块密码用一些硬编码的对称密钥加密该块。检查器知道密钥,并通过解密它(使用相同的硬编码对称密钥)并查看最后的零来验证序列号。

这并不比普通计数器更安全,但至少序列号看起来是随机的。对于 34 个字符的字母表(数字和大写字母,“O”和“I”除外),64 位块需要 13 个字母,这可能是可以接受的(典型的 Microsoft 序列号有 25 个字母)。对于 64 位分组密码,我推荐XTEA,它应该足够快速且易于实现。

如果您想要加密安全的方法,您将无法绕过互联网连接来验证串行。这意味着与传统的货架软件相比,这种保护方案更适合基于订阅的软件。

使用互联网连接很简单(因为您使用 JS,我认为这就是您想要的):

  1. 有人创建了密钥,该方必须知道秘密
  2. 服务器知道秘密并且能够解码数据
  3. 客户端发送密钥,这是一个用Base32编码的二进制 blob (另见此
  4. 服务器解密密钥并检查它是否有效(只是语法上或语义上)

在没有 Internet 连接的情况下,您仍然可以使用类似于 PGP 签名的方案来验证用户输入的 blob 是否由您创建。加密不会一样工作,因为解密总是需要知道秘密,这里的大问题是:

  1. 你不信任用户(否则你不需要保护方案)
  2. 你给用户秘密,编译成你的二进制文件

显然,这两点相互矛盾。一方面你不信任用户,另一方面你必须部署用于解密的秘密。

总而言之,我必须得出结论,没有互联网连接和基于服务器的有效性检查会导致一些知识(例如,仅在很长一段时间内有用的内容)的揭示,我迄今为止看到的任何保护方案或多或少饼干和小贩之间的军备竞赛。


现在,如果拥有一个加密安全的系统并不重要,我仍然会选择任何可以通过简单的 CRC 来保护的二进制数据。想到的是:

  1. 有一个单调递增的序列号
  2. 与您的盐值(或任何其他可逆操作)异或
  3. 取该值的 CRC32(或 Adler32 或其他) - 如果需要,在此处添加更多盐
  4. Base32编码(有助于可读性等)
  5. 只检查 CRC32 的有效性...

OP 写道“它应该很容易实现(在 javascript 中)并且应该非常快”

目前尚不清楚“in javascript”是指生成器还是检查器或两者兼而有之,或者它是指“浏览器中的javascript”,还是其他一些java/ecmascript实现(例如Web服务器中的服务器端)。

Javascript 作为实现语言不一定是问题,并且有几个用于 javascript 的加密库(支持 MD5、DES、AES、SHA、PBKDF2、HMAC):

速度也不应该是太大的问题(浏览器 javascript 引擎在这方面变得非常有用,一旦你通过了 IE6/7)。

但是,上述都不支持公钥加密(如果“检查器”分发给不受信任的用户,因此不能访问用于创建序列号的密钥,这是安全所必需的) - 更不用说建议的特定算法了托马斯,上图。Google 确实为 JavaScript 中的公钥实现提供了一些成功http://www-cs-students.stanford.edu/~tjw/jsbn/、http://ohdave.com/rsa/、http://www .hanewin.net/encrypt/rsa/rsa.htm)但我个人对它们的无错误性比上面的“更大”库更加紧张。

也许更重要的是,即使使用基于公钥的方法,如果生成器或检查器被分发给不受信任的用户,那么该方案也不安全。如果生成器代码对不受信任的用户可用,那么他们可以生成任意数量的密钥,而如果检查器对不受信任的用户可用,那么他们可以简单地修改 javascript 代码以跳过检查。[当然,本机代码也会出现同样的问题,但是,即使使用混淆,JavaScript 也可以说更容易通过本地修改进行攻击。]

在 JavaScript 中实施安全方案之前,您还应该考虑更广泛的信任问题:请参阅http://rdist.root.org/2010/11/29/final-post-on-javascript-crypto/以获得有用的摘要。