如何为我的C#应用程序创建产品密钥?

96

相关: https://dev59.com/yXRB5IYBdhLWcg3wHkPi - stukelly
@stukelly在J3r3myK发布问题之后发表的回复... - Dozer789
15个回答

84

您可以创建包含要用于应用程序认证的数据的记录。这可以包括任何您想要的内容 - 例如启用的程序功能、过期日期、用户名(如果您想将其绑定到用户)。然后,使用固定密钥的某些加密算法对其进行加密或哈希。然后只需在程序中进行验证即可。在Windows上分发许可文件的一种方法是将其提供为更新注册表的文件(保存用户无需输入)。

但要注意虚假安全感 - 迟早有人会简单地修补您的程序以跳过该检查并分发已经过修补的版本。或者,他们将找出可以通过所有检查的密钥,并且分发它,或者回溯时钟等等。无论您使计划多么复杂,最终都将成为模糊安全性,而他们总是能够做到这一点。即使他们不能,有人也会,他们会分发这个被黑客攻击的版本。即使您提供了一个加密狗,情况也如此 - 如果有人愿意,他们也可以修补掉那个检查。数字签名代码也没用,因为他们可以删除该签名或重新签名。

您可以通过使用技术防止程序在调试器中运行等方式来使问题更加复杂,但即使这也不是万无一失的。因此,您应该让它足够困难,以至于诚实的用户不会忘记付款。还要非常注意,您的方案不会对付费用户产生障碍 - 最好有一些被盗版副本,而不是让您的付费客户无法使用他们已经支付的东西。

另一个选择是进行在线检查 - 仅向用户提供唯一ID,并在线检查该ID应具有的功能,并缓存一段时间。但同样要注意所有警告 - 人们可以绕过任何类似的操作。

还要考虑处理忘记密钥用户等用户支持成本。

编辑:我只想补充一点,不要在这上面花太多时间,也不要认为你的错综复杂的计划会有所不同并且难以破解。它不会,只要人们控制你的程序运行的硬件和操作系统就无法做到。开发人员一直在尝试提出越来越复杂的方案,认为如果他们为此开发自己的系统,则只有他们才知道,因此“更安全”。但这实际上就像是试图构建永动机的编程等效物。:-)


1
很好的总结。如果有人不相信这很容易被绕过,可以查找CheatEngine,它使得非程序员也能轻松绕过。最好使这个层简单化。 - Kelly
我有同样的问题,我为我的应用程序制作了一个带有到期日期和最后登录日期验证的许可证密钥,但问题是我必须添加私钥以编辑文件以更新最后登录日期,这不是将密钥放入代码的聪明方式。有什么建议吗? - Doicare

16

你信任谁?

我一直认为这个领域太重要了,不能信任第三方来管理应用程序的运行时安全性。一旦该组件被破解,对于所有应用程序都是如此。几年前Discreet选择第三方许可证解决方案后,他们的3ds Max在五分钟内就被破解了...好时光!

认真考虑自己开发以完全控制算法。如果这样做,请考虑在您的密钥中使用以下组件:

  • 许可证名称-您正在许可的客户(如果有)。有助于管理公司部署-让他们感到特别,因为他们在您提供的许可信息中有一个“个性化”的名称。
  • 许可证过期日期
  • 在同一许可下运行的用户数量。这假定您有一种跨站点、类似服务器的方式来跟踪正在运行的实例
  • 功能代码-让您在多个功能和多个产品之间使用相同的许可系统。当然,如果它被破解了,对于所有产品都是如此。

然后对它们进行校验和并添加任何(可逆)加密,使其更难破解。

要创建试用许可证密钥,只需为上述值设置固定值,表示“试用模式”即可。

由于这可能是您的应用程序/公司中最重要的代码,因此除了混淆之外,考虑将解密例程放在本地DLL文件中,并简单地P / Invoke调用它。

我曾经为几家公司采用过通用方法,并取得了巨大成功。或者也许产品不值得破解 ;)


3
FYI,加密始终是可逆的,如果无法读取已加密内容,则将无用。哈希是你可能在想的单向“加密”方式。 - Samuel
“不要自己设计加密方案”这句话来自Bruce Scheier(我不确定),这是正确的方法。您可能想看一下这个答案:http://security.stackexchange.com/questions/2202/lessons-learned-and-misconceptions-regarding-encryption-and-cryptology/2210#2210 - Shadok
你能详细说明一下“..P/Invoke to it”吗?我看了链接页面,但并没有让我更明白 :-/ - MrCalvin

11
如果您询问可以键入的密钥,例如Windows产品密钥,则它们基于某些检查。如果您谈论的是必须复制粘贴的密钥,则它们基于数字签名(私钥加密)。
一个简单的产品密钥逻辑可能是从说产品密钥由四个5位数字组成开始,例如abcde-fghij-kljmo-pqrst,然后继续指定内部关系,例如f+k+p应该等于a,意思是第2、3和4组的第一个数字应该总计为a。这意味着8xxxx-2xxxx-4xxxx-2xxxx是有效的,8xxxx-1xxxx-0xxxx-7xxxx也是有效的。当然,还会有其他关系,包括复杂的关系,例如,如果第一组的第二个数字是奇数,则最后一组的最后一个数字也应该是奇数。这样就会有产品密钥生成器,并且验证产品密钥只需检查是否符合所有规则。
加密通常是使用私钥(==数字签名)加密并转换为Base64的许可证信息字符串。公钥与应用程序一起分发。当Base64字符串到达时,它会通过公钥进行验证(==解密),如果被发现有效,则激活产品。

10
无论是容易破解还是难以破解,我不确定这是否真的有什么区别。
你的应用程序被破解的可能性更多取决于其实用价值,而不是产品密钥处理的强度。
个人认为,用户分为两类:付费用户和非付费用户。付费用户即使在使用最简单的保护措施时也会愿意支付。而不付费的用户会选择等待破解或寻找其他替代品。无论哪种方式,它都不会为您带来更多的收入。

6

我必须承认我会做一些相当疯狂的事情。

  1. 找到CPU瓶颈并将其提取到P/Invokeable DLL文件中。
  2. 作为后置构建操作,使用XOR加密密钥加密DLL文件的一部分。
  3. 选择公共/私有密钥方案,在DLL文件中包含公钥。
  4. 安排解密产品密钥并将两个部分进行XOR运算以得到DLL的加密密钥。
  5. 在DLL的DllMain代码中,禁用保护(PAGE_EXECUTE_READWRITE)并使用密钥进行解密。
  6. 创建一个LicenseCheck()方法,对许可证密钥和参数进行合理性检查,然后对整个DLL文件进行校验和检查,如果存在许可证违规,则抛出异常。还要在此处进行其他初始化。

当他们找到并删除LicenseCheck时,DLL开始分段错误,接下来会发生什么有趣的事情。


那不是需要禁用 DEP 吗? - Rowland Shaw
不设置PAGE_EXECUTE_READWRITE是编写自修改代码的文档正确方式,并仅清除该页面上的NX位。 - Joshua
9
这种通用技术在80年代末非常流行。它的缺点是,“秘密”代码被解密到RAM中,这使得从任何正在运行的软件副本中窃取变得容易。 - Ray Burns

5
如果您想要一个简单的解决方案来创建和验证序列号,请尝试 Ellipter。它使用椭圆曲线加密,并具有“到期日期”功能,因此您可以创建试用版本或限时注册密钥。

5

还有一种选择是Microsoft Software Licensing and Protection(SLP)服务。阅读后,我真的希望能够使用它。

我非常喜欢根据许可证来阻止代码部分的想法。这是.NET最安全的热门功能。即使您不使用它,也是有趣的阅读!

Microsoft® Software Licensing and Protection (SLP) Services是一项软件激活服务,使独立软件供应商(ISV)可以为其客户采用灵活的许可条款。 Microsoft SLP服务采用独特的保护方法,有助于保护您的应用程序和许可信息,从而使您更快地进入市场,同时增加客户的合规性。

注意:这是我发布包含敏感代码(例如有价值的算法)的产品的唯一方式。


对于那些记得它被取消的人:SLP再次重新启动了。 - Michael Olesen

2
另一个用于产品密钥和激活的好用且价格不高的工具是名为InstallKey的产品。请查看www.lomacons.com

2
一种简单的方法是使用全局唯一标识符(GUID)。GUID通常作为128位值存储,并通常显示为32个十六进制数字,其中组之间用连字符分隔,例如{21EC2020-3AEA-4069-A2DD-08002B30309D}。
在C#中使用以下代码:System.Guid.NewGuid()。
getKey = System.Guid.NewGuid().ToString().Substring(0, 8).ToUpper(); //Will generate a random 8 digit hexadecimal string.

_key = Convert.ToString(Regex.Replace(getKey, ".{4}", "$0/")); // And use this to separate every four digits with a "/".

我希望它有所帮助。

1
请查看这个答案: https://dev59.com/CXNA5IYBdhLWcg3wUL9Y#38598174 使用Cryptolens作为许可证服务器的想法。这里有一个逐步示例(使用C#和VB.NET)。我还附加了一个密钥验证的代码片段(使用C#):
var licenseKey = "GEBNC-WZZJD-VJIHG-GCMVD";
var RSAPubKey = "{enter the RSA Public key here}";

var auth = "{access token with permission to access the activate method}";
var result = Key.Activate(token: auth, parameters: new ActivateModel()
{
    Key = licenseKey,
    ProductId = 3349,
    Sign = true,
    MachineCode = Helpers.GetMachineCode()
});

if (result == null || result.Result == ResultType.Error ||
    !result.LicenseKey.HasValidSignature(RSAPubKey).IsValid())
{
    // an error occurred or the key is invalid or it cannot be activated
    // (eg. the limit of activated devices was achieved)
    Console.WriteLine("The license does not work.");
}
else
{
    // everything went fine if we are here!
    Console.WriteLine("The license is valid!");
}

Console.ReadLine();

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