如何生成和验证软件许可证密钥?

302

我目前正在开发一个产品(使用C#语言开发),该产品将提供免费下载和安装,但是只有非常有限的功能。要想获得所有功能,用户必须支付许可证费用并收到钥匙。然后将该钥匙输入应用程序中以“解锁”完整版本。

由于使用类似的许可证密钥很普遍,因此我在想:

  1. 通常如何解决这个问题?
  2. 如何生成密钥,并且应用程序如何验证密钥?
  3. 如何避免密钥被发布在互联网上并被其他未支付许可证的人使用(这个密钥实际上不属于他们)?

我想我还应该将密钥与应用程序版本关联起来,以便可以在未来版本中为新密钥收费。

这种情况下还有什么需要考虑的吗?

15个回答

149
注意:你无法防止用户盗版,只能让诚实的用户更容易地做正确的事情。
假设你不想为每个用户创建一个特殊版本,那么:
  • 生成产品的秘密密钥。
  • 获取用户的名称。
  • 将用户名称和秘密密钥连接起来,并使用(例如)SHA1进行哈希处理。
  • 将SHA1哈希解包为字母数字字符串。这就是每个个体用户的“产品密钥”。
  • 在程序中执行相同的哈希操作,并与产品密钥进行比较。如果相等,则为OK。
但是,我再次重申:这不能防止盗版
我最近读到这种方法在密码学上并不是很安全。但是这个解决方案已经很脆弱了(因为软件本身必须在某处包含秘密密钥),所以我认为这个发现并不会使该解决方案失效。
只是想提醒一下,如果您计划从中派生其他东西,请注意。

20
如果程序包含秘密密钥(如上所述),那么破解它就很容易。 - Steven A. Lowe
35
使用非对称加密方法(例如RSA)生成和解码产品密钥,以避免将秘密嵌入代码中。 - Amir Moghimi
12
如果有人在汇编级别上黑入你的代码来寻找秘密密钥,那么他们很可能已经具备了足够的技术水平可以完全绕过你的检查。我认为没有一种安全的注册方法能够抵御好的黑客本地运行程序的攻击。正如原评论所说,关键在于任何使复制文件变得更加困难的因素。现在很多游戏已经放弃了防拷贝保护,只需将游戏内容在线上提供,这样黑客就无法获得代码了。 - JamieB
2
在许可证密钥中包含限制是常见的吗?例如,时间限制、并发用户数、要安装的模块等等? - Carlo
3
@Brent.Longborough 只有作者(或生成许可证密钥的一方)拥有它。查找非对称加密。 - Luke
显示剩余15条评论

137

有许多方法可以生成授权密钥,但其中很少有方法是真正安全的。这很遗憾,因为对于公司而言,授权密钥几乎具有与真正的现金相同的价值。

理想情况下,您希望您的许可证密钥具有以下属性:

  1. 即使有人完全逆向工程你的产品(这肯定会发生,我由经验所知),也只能由公司生成许可证密钥。如果您认真控制许可证,则混淆算法或在软件中隐藏加密密钥确实行不通。如果您的产品成功,那么发布后几天内就会有人制作密钥生成器。

  2. 许可证密钥应仅可在一台计算机上使用(或者至少您应该能够严格控制此操作)

  3. 许可证密钥应该短小并易于输入或通过电话口述。您不希望每个客户都打电话给技术支持,因为他们不明白密钥中是否包含“l”还是“1”。您的支持部门会感谢您,并且在这个领域您的成本将更低。

那么,如何解决这些挑战?

  1. 答案很简单但在技术上具有挑战性:使用公钥密码学的数字签名。实际上,您的许可证密钥应该是已签名的“文档”,包含一些有用的数据,并使用公司的私钥进行签名。签名应该是许可证密钥的一部分。产品应该使用相应的公钥验证许可证密钥。这样,即使某人完全访问了您的产品逻辑,他们也无法生成许可证密钥,因为他们没有私钥。许可证密钥看起来像这样:BASE32(CONCAT(DATA,PRIVATE_KEY_ENCRYPTED(HASH(DATA)))) 这里最大的挑战是典型的公钥算法具有较大的签名大小。RSA512具有1024位签名。您不希望您的许可证密钥具有数百个字符。

最强大的方法之一是使用椭圆曲线密码(需要小心实现以避免现有专利)。与RSA密钥相比,ECC密钥长度约短6倍,但安全强度相同。您可以使用Schnorr数字签名算法进一步减小签名大小(专利已于2008年过期,不错 :) )。
这可通过产品激活实现(Windows是一个很好的例子)。基本上,对于具有有效许可密钥的客户,您需要生成一些“激活数据”,其中包含计算机硬件ID作为签名数据的信息。通常,这是通过互联网完成的,但只需一次:产品将许可密钥和计算机硬件ID发送到激活服务器,激活服务器返回已签名的消息(也可以简短且易于在电话中传达)。从那时起,产品在启动时不再检查许可密钥,而是检查激活数据,该数据需要计算机相同才能验证(否则,数据将不同,数字签名也将无法验证)。请注意,激活数据检查不需要通过互联网进行验证:仅需使用已嵌入产品中的公钥验证激活数据的数字签名即可。
好吧,只需消除密钥中的冗余字符,如“1”、“l”、“0”、“o”。将许可密钥字符串拆分为字符组即可。

12
他们不能只编辑软件并添加/删除代码,以完全跳过检查吗? - Pacerier
5
我想指出这个答案比其他哈希函数要优越得多。 - Erik Aronesty
7
值得注意的是,即使使用非对称加密的私钥/公钥,仍然可以通过简单地将软件中提供的公钥替换为另一个公钥,并使用其相应的私钥签署虚假许可证来生成伪造许可证。这就是为什么我们有并需要受信任的证书颁发机构,他们将公钥与身份绑定。因此,尽管这可能增加了一道障碍,但它本身并不能保证第一点。 - Saeb Amini
2
@Saeb Amini,可信任的证书颁发机构只会保护公钥,用户无法添加新的证书到存储中。黑客可以注册自己的证书作为受信任的CA,并用其证书签名的公钥替换程序中的公钥。这使得黑客攻击变得更加困难了一点。 - Julius Tuskenis
1
@SaebAmini 替换密钥似乎与修改 EXE 以完全跳过许可证检查的效果类似。 - StayOnTarget
显示剩余4条评论

89

简单回答-无论使用何种方案,都可以被破解。

不要惩罚诚实的客户,以防止黑客攻击,因为黑客会破解它。

一个简单的哈希代码绑定他们的电子邮件或类似的东西可能已经足够了。基于硬件的ID总是在人们需要重新安装或更新硬件时成为问题。

关于这个问题的好帖子:http://discuss.joelonsoftware.com/default.asp?biz.5.82298.34


5
同意,你不想让实际购买你产品的用户感到不满!(请注意微软、苹果等) - Jason
4
由于MS、苹果等公司提供的核心产品难以在其他地方获取,或者它们拥有可以用来迫使人们的巨大市场影响力,因此它们可以逃避责任。而小型开发者则做不到。 - schooner
4
不能破解公/私钥签名方案以生成新的有效密钥,让用户能够运行从发布者网站下载的已签名代码,而不是破解软件。相比之下,哈希/对称方案可以被破解以生成与无效密钥无法区分的新的有效许可证密钥。这是一个巨大的区别。 - Erik Aronesty
2
此线程链接已失效。这是存档版本 -> https://web.archive.org/web/20181006064951/http://discuss.joelonsoftware.com/default.asp?biz.5.82298.34 - Tim Holt

65

生成密钥时,请不要忘记将版本和构建号拼接到计算哈希字符串的字符串中。这样就不会有一个单一的密钥可以解锁您发布的所有内容。

如果您在astalavista.box.sk找到了一些密钥或补丁,那么说明您已成功地制作出了受欢迎到某人费心破解的东西。庆祝吧!


12
不要忘记将版本号和构建号连接到您计算哈希的字符串中,但是当用户更新到次要修补程序时,这样做不会使密钥失效吗? - thomthom
5
那么,将最大版本与密钥关联起来的想法如何? 版本概念本身是可行的,可以增加更多的安全性。 - Marvin Thobejane
2
@MarvinThobejane 要关联一个最大版本,你可以签署允许的最大版本,并使代码迭代其版本一点。但是,在签名中不允许使用 >= 运算符。 - Erik Aronesty
1
对于昂贵的软件,他们只会在发送升级公告时每六个月发送一个新的许可文件。 - Rick

31
我是Cryptolens软件许可平台的开发人员之一,从14岁开始就一直在研究许可系统。根据多年的经验,我在这个答案中提供了一些建议。
解决此问题的最佳方法是设置一个许可密钥服务器,每个应用程序实例都将调用该服务器以验证许可密钥。
许可密钥服务器的优点是:
1. 您可以随时立即更新或阻止许可密钥。 2. 每个许可证密钥可以锁定到某些机器上(这有助于防止用户将许可密钥发布在网上供其他人使用)。
虽然在线验证许可证可以更好地控制每个应用程序实例,但互联网连接并不总是存在(特别是如果您面向更大的企业),因此我们需要另一种执行许可密钥验证的方法。
解决方案是始终使用公钥密码系统(例如RSA或ECC)对来自服务器的许可密钥响应进行签名(如果您计划在嵌入式系统上运行,则可能更好)。您的应用程序应仅具有公钥以验证许可密钥响应。
因此,在没有互联网连接的情况下,您可以使用先前的许可密钥响应。确保在响应中存储日期机器标识符,并检查它是否过时(例如,您允许用户离线最多30天等)以及许可密钥响应是否属于正确的设备。
请注意,即使您已连接到互联网,也应始终检查许可证密钥响应的证书,以确保自离开服务器以来未发生更改(即使您的许可证密钥服务器的 API 使用 https,仍必须执行此操作)。
保护秘密算法:大多数 .NET 应用程序都可以很容易地进行反向工程处理(Microsoft 提供了反汇编器以获取 IL 代码,一些商业产品甚至可以检索源代码,例如 C#)。当然,您总是可以混淆代码,但这永远不是100%安全的。在大多数情况下,任何软件许可解决方案的目的是帮助诚实的人保持诚实(即愿意支付费用的诚实用户在试用期过期后不会忘记支付等)。但是,您可能仍然有一些代码,您绝不想泄漏给公众(例如预测股票价格的算法等)。在这种情况下,唯一的方法是创建一个 API 端点,每次执行该方法时,您的应用程序将调用它。这需要互联网连接,但它确保您的秘密代码永远不会在客户端机器上执行。
实现:如果您不想自己实现所有内容,我建议您查看 Cryptolensthis tutorial

1
一个有关于限制保存的许可证密钥不要太旧的问题:由于计算机可能没有连接到互联网,他们的日期和时间可以随时被改回以保持在同一个有效日期上? - Amir Mahdi Nassiri
用户是否可以通过在Windows中定义回环主机来绕过在线许可证服务器?我见过很多应用程序被盗版,例如Resharper和Matlab。 - Amir Mahdi Nassiri
2
对于问题1:如果PC永久离线,您可以使用实时时钟(RTC)dongle作为可信时间来源。对于问题2:由于响应是使用供应商的私钥签名的(并使用应用程序内部的公钥进行验证),因此攻击者需要在不知道私钥的情况下重新签署文件,而在撰写本文时,这是不可能的2048位RSA密钥。 - Artem
@Artem 公钥是应用程序二进制文件的一部分吗?是否可能将其替换为另一个公钥,并使用相应的私钥创建伪造许可证服务器? - huggie
1
@huggie,没错,公钥是应用程序二进制文件的一部分。你说得对,它可以被攻击者替换。为了防止这种情况发生,我建议对应用程序进行混淆。这将使攻击者难以但不是不可能地替换公钥。对于敏感代码,我建议通过API端点公开访问它,这样应用程序就可以访问它。 - Artem
这应该是被接受的答案。谢谢你在这里提供的详细信息。 - Anthony V

27

除了已经提到的内容之外……

.NET应用程序的任何使用都会因为中间语言问题而天生具有漏洞。简单地反汇编.NET代码将公开您的产品给任何人,他们可以轻松地绕过您的许可代码。

您甚至不能再使用硬件值来创建密钥。虚拟机现在允许某人创建一个“已获得许可”的计算机镜像,并在任何平台上运行它。

如果这是昂贵的软件,则有其他解决方案。如果不是,请设法让即兴黑客难以破解。并接受最终会存在未授权的副本的事实。

如果您的产品很复杂,那么固有的支持问题将为您创建一些保护。


11
+1 是为了防止虚拟机对硬件价值造成削弱。 - Rubens Mariuzzo
3
这就是为 .NET 强命名和 PE 签名采用 Authenticode 的原因。如果有人对您的库进行了反编译、修改和重建,那么它将无法被签名,应用程序也将无法运行。.NET 虚拟机会拒绝它。 - Stephen Tunney
2
签名是用于验证您将要运行的程序的来源。如果用户不关心程序的来源,因为他知道它已被修改和破解,黑客将会剥离签名,甚至使用自己的签名进行签名。签名确实可以防止可信程序集与不可信程序集混合在一起。 - jesusduarte
一款移动应用程序可以用作临时硬件加密狗,用于昂贵软件... 只需使用应用程序进行支付,并在应用程序的安全元素中嵌入签名密钥。然后,您可以使用桌面电脑 + 应用程序进行激活... 取消其他桌面电脑的激活状态。将一些关键代码区域放置在应用程序和/或在线同态计算服务中可以帮助防止简单反编译。 - Erik Aronesty

11
我们用于生成许可证密钥的C# / .NET引擎现在作为开源项目进行维护:

https://github.com/appsoftware/.NET-Licence-Key-Generator

它基于“部分密钥验证”系统,这意味着您用于生成密钥的密钥的子集只需要编译到可分发的程序中。由您自己创建密钥,因此许可实现对您的软件是唯一的。

如上所述,如果您的代码可以反编译,则相对容易规避大多数许可系统。


1
你愿意为使用这个产品做一个教程吗?我发现他们的维基有点不足。 - Anthony Ruffino
该项目现在已经在GitHub上开源,如果有帮助的话(答案已编辑附带链接)。 - gbro3n
我不建议使用部分密钥验证。它可能会被破解,一旦破解,就会造成灾难性后果(https://keygen.sh/blog/how-to-generate-license-keys-in-2021/)。我建议使用安全的公钥加密算法,如EdDSA或RSA。 - ezekg

7
我坚信,只有基于公钥加密的许可证系统才是正确的方法,因为您不必在源代码中包含生成许可证所需的关键信息。
过去,我多次使用过 Treek's Licensing Library,因为它符合这些要求并提供了非常良好的价格。它将相同的许可证保护用于最终用户和自身,并且直到现在没有人破解它。您还可以在该网站上找到很好的提示以避免盗版和破解。

公钥加密是否需要使用在线激活服务?我的意思是,如果它不在源代码中(我想你也包括可执行文件),那么还能在哪里呢? - Dan W
不,您不必使用在线激活服务。您可以完全离线生成许可文件。 - panpernicek
1
关键在于,您只将公钥放置到代码中,这不能用于许可证生成,仅用于验证。 - panpernicek

6

我不知道您想要多详细的解释,

但我相信.NET可以访问硬盘序列号。

您可以让程序发送给您硬盘序列号和其他信息(例如用户名称和NIC的MAC地址)。

您可以基于这些信息计算出一个代码,并将密钥通过电子邮件发送给他们。

这将防止他们在获得密钥后更换设备。


6
并防止其他问题中替换失效的硬盘,以避免带来沮丧。不幸的是,没有简单的答案,您需要在信任和基本许可机制之间取得平衡。 - schooner
作为一名软件工程师,我曾多年从事一款产品的开发,该产品使用硬盘序列号作为验证方式,但是对于那些知道如何更新它的人来说,这种验证方式完全不安全。 - oden
我是在暗示将此数字与其他内容(如MAC地址、FQDN)一起使用,或许可以将它们全部放入哈希表中。重点是要使欺骗所有这些数据的难度略微增加,以免在第一次反向工程软件并删除检查时变得更容易。 - Crash893

5

我过去使用过Crypkey。它是众多可用方案之一。

任何许可证方案只能在某种程度上保护软件。


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