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

发布于 2024-07-11 06:54:57 字数 311 浏览 7 评论 0 原文

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

我需要创建每年更新的产品(或许可证)密钥。 此外,我需要创建一个试用版。

相关:

How can I create a product key for my C# Application?

I need to create a product (or license) key that I update annually. Additionally I need to create one for trial versions.

Related:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

╄→承喏 2024-07-18 06:54:57

您可以执行一些操作,例如创建一条记录,其中包含您想要向应用程序进行身份验证的数据。 这可以包括您想要的任何内容 - 例如要启用的程序功能、到期日期、用户名称(如果您想将其绑定到用户)。 然后使用某种带有固定密钥的加密算法对其进行加密或对其进行散列。 然后您只需在程序中验证它即可。 分发许可证文件(在 Windows 上)的一种方法是将其作为更新注册表的文件提供(用户不必键入它)。

但要小心错误的安全感 - 迟早有人会简单地修补您的程序以跳过该检查,并分发修补后的版本。 或者,他们将制定一个通过所有检查的密钥并分发该密钥,或者回溯时钟等。无论您制定的计划多么复杂,您为此所做的任何事情最终都将通过默默无闻来实现安全,并且他们将永远能够做到这一点。 即使他们不能,也会有人会,并且会分发黑客版本。 即使您提供了加密狗,同样适用 - 如果有人愿意,他们也可以修补该检查。 对代码进行数字签名不会有帮助,他们可以删除该签名,或放弃它。

您可以通过使用技术来阻止程序在调试器等中运行,从而使问题变得复杂一些,但即使这样也不是万无一失的。 所以你应该让它变得足够困难,这样诚实的用户就不会忘记付款。 还要非常小心,不要让您的方案对付费用户造成干扰 - 最好有一些盗版副本,而不是让您的付费客户无法使用他们已付费的产品。

另一种选择是进行在线检查——只需为用户提供唯一的ID,并在线检查该ID应具有哪些功能,并将其缓存一段时间。 但所有同样的警告仍然适用——人们可以避开这样的事情。

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

编辑:我只是想补充一点,不要在这方面投入太多时间,或者认为您的复杂方案会有所不同,并且无法破解的。 只要人们控制程序运行的硬件和操作系统,就不会也不可能。 开发人员一直试图为此提出更复杂的方案,认为如果他们为此开发自己的系统,那么只有他们自己知道,因此“更安全”。 但这实际上相当于尝试构建永动机的编程。 :-)

You can do something like create a record which contains the data you want to authenticate to the application. This could include anything you want - e.g. program features to enable, expiry date, name of the user (if you want to bind it to a user). Then encrypt that using some crypto algorithm with a fixed key or hash it. Then you just verify it within your program. One way to distribute the license file (on windows) is to provide it as a file which updates the registry (saves the user having to type it).

Beware of false sense of security though - sooner or later someone will simply patch your program to skip that check, and distribute the patched version. Or, they will work out a key that passes all checks and distribute that, or backdate the clock, etc. It doesn't matter how convoluted you make your scheme, anything you do for this will ultimately be security through obscurity and they will always be able to this. Even if they can't someone will, and will distribute the hacked version. Same applies even if you supply a dongle - if someone wants to, they can patch out the check for that too. Digitally signing your code won't help, they can remove that signature, or resign it.

You can complicate matters a bit by using techniques to prevent the program running in a debugger etc, but even this is not bullet proof. So you should just make it difficult enough that an honest user will not forget to pay. Also be very careful that your scheme does not become obtrusive to paying users - it's better to have some ripped off copies than for your paying customers not to be able to use what they have paid for.

Another option is to have an online check - just provide the user with a unique ID, and check online as to what capabilities that ID should have, and cache it for some period. All the same caveats apply though - people can get round anything like this.

Consider also the support costs of having to deal with users who have forgotten their key, etc.

edit: I just want to add, don't invest too much time in this or think that somehow your convoluted scheme will be different and uncrackable. It won't, and cannot be as long as people control the hardware and OS your program runs on. Developers have been trying to come up with ever more complex schemes for this, thinking that if they develop their own system for it then it will be known only to them and therefore 'more secure'. But it really is the programming equivalent of trying to build a perpetual motion machine. :-)

下壹個目標 2024-07-18 06:54:57

你信任谁?

我一直认为这个领域太重要了,以至于不能信任第三方来管理应用程序的运行时安全性。 一旦某个应用程序的该组件被破解,所有应用程序的该组件也会被破解。 一旦他们采用第三方许可解决方案谨慎 href="https://en.wikipedia.org/wiki/Autodesk_3ds_Max" rel="nofollow noreferrer">3ds Max 年前...美好时光!

说真的,考虑一下自己动手来完全控制你的算法。 如果这样做,请考虑在密钥中使用以下组件:

  • 许可证名称 - 您正在许可的客户端(如果有)的名称。 对于管理公司部署很有用 - 让他们觉得在您提供给他们的许可证信息中拥有“个性化”名称很特别。
  • 许可证到期日期
  • 在同一许可证下运行的用户数量。 这假设您有一种方法以服务器式的方式跟踪整个站点上正在运行的实例
  • 功能代码 - 让您可以跨多个功能和多个产品使用相同的许可系统。 当然,如果一种产品被破解,那么所有产品都会被破解。

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

要制作试用许可证密钥,只需设置上述值(翻译为“试用模式”)即可。

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

我工作过的几家公司为此采用了通用方法并取得了巨大成功。 或者也许这些产品不值得破解;)

Who do you trust?

I've always considered this area too critical to trust a third party to manage the runtime security of your application. Once that component is cracked for one application, it's cracked for all applications. It happened to Discreet in five minutes once they went with a third-party license solution for 3ds Max years ago... Good times!

Seriously, consider rolling your own for having complete control over your algorithm. If you do, consider using components in your key along the lines of:

  • License Name - the name of client (if any) you're licensing. Useful for managing company deployments - make them feel special to have a "personalised" name in the license information you supply them.
  • Date of license expiry
  • Number of users to run under the same license. This assumes you have a way of tracking running instances across a site, in a server-ish way
  • Feature codes - to let you use the same licensing system across multiple features, and across multiple products. Of course if it's cracked for one product it's cracked for all.

Then checksum the hell out of them and add whatever (reversable) encryption you want to it to make it harder to crack.

To make a trial license key, simply have set values for the above values that translate as "trial mode".

And since this is now probably the most important code in your application/company, on top of/instead of obfuscation consider putting the decrypt routines in a native DLL file and simply P/Invoke to it.

Several companies I've worked for have adopted generalised approaches for this with great success. Or maybe the products weren't worth cracking ;)

夢归不見 2024-07-18 06:54:57

如果您询问可以输入的密钥(例如 Windows 产品密钥),那么它们是基于一些检查的。 如果您谈论的是必须复制粘贴的密钥,那么它们是基于数字签名(私钥加密)的。

一个简单的产品密钥逻辑可以首先说明产品密钥由四个 5 位数字组组成,例如 abcde-fghij-kljmo-pqrst,然后继续指定内部关系,例如 f+ k+p 应等于 a,这意味着 2、3 和 4 组的第一位数字总计应为 a。 这意味着 8xxxx-2xxxx-4xxxx-2xxxx 有效,8xxxx-1xxxx-0xxxx-7xxxx 也是有效的。 当然,还会有其他关系,包括复杂的关系,例如,如果第一组的第二位数字是奇数,那么最后一组的最后一位数字也应该是奇数。 这样就会有产品密钥的生成器,并且产品密钥的验证将简单地检查它是否符合所有规则。

加密通常是有关使用私钥加密的许可证信息字符串(==数字签名)并转换为 Base64。 公钥随应用程序一起分发。 当 Base64 字符串到达​​时,将通过公钥对其进行验证(==解密),如果发现有效,则激活产品。

If you are asking about the keys that you can type in, like Windows product keys, then they are based on some checks. If you are talking about the keys that you have to copy paste, then they are based on a digitial signature (private key encryption).

A simple product key logic could be to start with saying that the product key consists of four 5-digit groups, like abcde-fghij-kljmo-pqrst, and then go on to specify internal relationships like f+k+p should equal a, meaning the first digits of the 2, 3 and 4 group should total to a. This means that 8xxxx-2xxxx-4xxxx-2xxxx is valid, so is 8xxxx-1xxxx-0xxxx-7xxxx. Of course, there would be other relationships as well, including complex relations like, if the second digit of the first group is odd, then the last digit of the last group should be odd too. This way there would be generators for product keys and verification of product keys would simply check if it matches all the rules.

Encryption are normally the string of information about the license encrypted using a private key (== digitally signed) and converted to Base64. The public key is distributed with the application. When the Base64 string arrives, it is verified (==decrypted) by the public key and if found valid, the product is activated.

花间憩 2024-07-18 06:54:57

无论它是微不足道的还是难以破解的,我不确定它是否真的有很大的不同。

您的应用程序被破解的可能性与其实用性成正比,而不是与产品密钥处理的强度成正比。

就我个人而言,我认为有两类用户。 那些付钱的人。 那些不这样做的人。 那些这样做的人很可能会在采取最微不足道的保护措施的情况下这样做。 那些不这样做的人会等待裂缝或寻找其他地方。 不管怎样,它不会给你带来更多的钱。

Whether it's trivial or hard to crack, I'm not sure that it really makes much of a difference.

The likelihood of your app being cracked is far more proportional to its usefulness rather than the strength of the product key handling.

Personally, I think there are two classes of user. Those who pay. Those who don't. The ones that do will likely do so with even the most trivial protection. Those who don't will wait for a crack or look elsewhere. Either way, it won't get you any more money.

不顾 2024-07-18 06:54:57

我不得不承认我会做一些相当疯狂的事情。

  1. 找到 CPU 瓶颈并将其提取到 P/Invokeable DLL 文件。
  2. 作为构建后操作,使用 XOR 加密 DLL 文件的一部分
    加密密钥。
  3. 选择公钥/私钥方案,将公钥包含在 DLL 文件中
  4. ,以便解密产品密钥并对两者进行异或
    将其减半得到 DLL 的加密密钥。
  5. 在DLL的DllMain代码中,禁用保护(PAGE_EXECUTE_READWRITE)
    并用密钥解密。
  6. 创建一个 LicenseCheck() 方法来对
    许可证密钥和参数,然后对整个 DLL 文件进行校验和,抛出
    任何一方都违反许可证。 哦,还有一些其他的初始化
    这里。

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

I have to admit I'd do something rather insane.

  1. Find a CPU bottleneck and extract it to a P/Invokeable DLL file.
  2. As a post build action, encrypt part of the DLL file with an XOR
    encryption key.
  3. Select a public/private key scheme, include public key in the DLL file
  4. Arrange so that decrypting the product key and XORing the two
    halves together results in the encryption key for the DLL.
  5. In the DLL's DllMain code, disable protection (PAGE_EXECUTE_READWRITE)
    and decrypt it with the key.
  6. Make a LicenseCheck() method that makes a sanity check of the
    license key and parameters, then checksums entire DLL file, throwing
    license violation on either. Oh, and do some other initialization
    here.

When they find and remove the LicenseCheck, what fun will follow
when the DLL starts segmentation faulting.

猫卆 2024-07-18 06:54:57

还有Microsoft 软件许可和保护 (SLP) 服务选项。 读完后我真的希望我能使用它。

我真的很喜欢根据许可证阻止部分代码的想法。 热门内容,对于 .NET 来说是最安全的。 即使您不使用它,读起来也很有趣!

Microsoft® 软件许可和
保护 (SLP) 服务是
软件激活服务
使独立软件供应商
(ISV) 采用灵活的许可
为他们的客户提供的条款。 微软
SLP 服务采用独特的
有助于保护的保护方法
您的申请和许可
信息让您能够到达
市场增长速度更快
客户合规性。

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

There is the option Microsoft Software Licensing and Protection (SLP) Services as well. After reading about it I really wish I could use it.

I really like the idea of blocking parts of code based on the license. Hot stuff, and the most secure for .NET. Interesting read even if you don't use it!

Microsoft® Software Licensing and
Protection (SLP) Services is a
software activation service that
enables independent software vendors
(ISVs) to adopt flexible licensing
terms for their customers. Microsoft
SLP Services employs a unique
protection method that helps safeguard
your application and licensing
information allowing you to get to
market faster while increasing
customer compliance.

Note: This is the only way I would release a product with sensitive code (such as a valuable algorithm).

饮惑 2024-07-18 06:54:57

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

If you want a simple solution just to create and verify serial numbers, try Ellipter. It uses elliptic curves cryptography and has an "Expiration Date" feature so you can create trial verisons or time-limited registration keys.

dawn曙光 2024-07-18 06:54:57

另一种用于产品密钥和激活的廉价工具是名为 InstallKey 的产品。 看看 www.lomacons.com

Another good inexpensive tool for product keys and activations is a product called InstallKey. Take a look at www.lomacons.com

木格 2024-07-18 06:54:57

一种简单的方法是使用全局唯一标识符 (GUID)。 GUID 通常存储为 128 位值,并且通常显示为 32 个十六进制数字,各组之间用连字符分隔,例如 {21EC2020-3AEA-4069-A2DD-08002B30309D}

通过 System.Guid.NewGuid() 在 C# 中使用以下代码。

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 "/".

我希望它有帮助。

One simple method is using a Globally Unique Identifier (GUID). GUIDs are usually stored as 128-bit values and are commonly displayed as 32 hexadecimal digits with groups separated by hyphens, such as {21EC2020-3AEA-4069-A2DD-08002B30309D}.

Use the following code in C# by 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 "/".

I hope it helps.

尘世孤行 2024-07-18 06:54:57

诀窍是拥有一个只有您知道的算法(以便可以在另一端对其进行解码)。

有一些简单的事情,例如“选择一个质数并为其添加一个幻数”,也

有更复杂的选项,例如对一组二进制数据(可能包括唯一标识符、版本号等)使用非对称加密并分发加密的数据数据作为关键。

也可能值得阅读这个问题的回复

The trick is to have an algorithm that only you know (such that it could be decoded at the other end).

There are simple things like, "Pick a prime number and add a magic number to it"

More convoluted options such as using asymmetric encryption of a set of binary data (that could include a unique identifier, version numbers, etc) and distribute the encrypted data as the key.

Might also be worth reading the responses to this question as well

疾风者 2024-07-18 06:54:57

有一些可用的工具和 API。 但是,我认为您不会免费找到一个;)

例如,有 OLicense 套件:
http://www.olicense.de/index.php?lang=en

There are some tools and API's available for it. However, I do not think you'll find one for free ;)

There is for instance the OLicense suite:
http://www.olicense.de/index.php?lang=en

花开半夏魅人心 2024-07-18 06:54:57

请检查这个答案: https://stackoverflow.com/a/38598174/1275924

这个想法是使用 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();

Please check this answer: https://stackoverflow.com/a/38598174/1275924

The idea is to use Cryptolens as the license server. Here's a step-by-step example (in C# and VB.NET). I've also attached a code snippet for key verification below (in 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();
你的心境我的脸 2024-07-18 06:54:57

您可以查看LicenseSpot。 它提供:

  • 免费许可组件
  • 在线激活
  • API,用于集成您的应用程序和在线商店
  • 序列号生成
  • 撤销许可证
  • 订阅管理

You can check LicenseSpot. It provides:

  • Free Licensing Component
  • Online Activation
  • API to integrate your app and online store
  • Serial number generation
  • Revoke licenses
  • Subscription Management
淡淡離愁欲言轉身 2024-07-18 06:54:57

我将借鉴 @frankodwyer 的精彩答案,并更深入地研究基于在线的许可。 我是 Keygen 的创始人,这是一个为开发人员构建的许可 REST API。

由于您提到您的应用程序需要两种“类型”的许可证,即“完整版”和“试用版”,我们可以简化这一点并使用功能许可证模型,您可以在其中许可特定功能您的应用程序(在本例中,有一个“完整”功能集和一个“试用”功能集)。

首先,我们可以创建 2 个许可证类型(在注册机中称为策略),每当用户注册帐户时,您都可以生成一个“试用”许可证供他们开始使用(“试用”许可证实现我们的“试用”功能政策),您可以使用它在应用程序内进行各种检查,例如用户是否可以使用Trial-Feature-A试用功能-B

在此基础上,每当用户购买您的应用(无论您使用的是 PayPal、Stripe 等),您都可以生成一个实现“完整”功能策略的许可证,并将其与用户的应用程序关联起来。 帐户。 现在,在您的应用程序中,您可以检查用户是否拥有可以执行 Pro-Feature-X 和 Pro-Feature-Y 操作的“完整”许可证(通过执行类似 代码>user.HasLicenseFor(FEATURE_POLICY_ID))。

我提到允许您的用户创建用户帐户 - 我的意思是什么? 我已在 a 中详细介绍了这一点其他几个答案,但快速概述一下为什么我认为这是验证和识别用户的更好方法:

  1. 用户帐户允许您关联多个许可证多台计算机< /em> 面向单个用户,让您深入了解客户的行为并提示他们进行“应用内购买”,即购买“完整”版本(类似于如移动应用程序)。
  2. 我们不应该要求客户输入很长的许可证密钥,这些密钥输入起来既繁琐又难以跟踪,即很容易丢失。 (尝试在 Twitter 上搜索“丢失的许可证密钥”!)
  3. 客户习惯使用电子邮件/密码; 我认为我们应该做人们习惯做的事情,这样我们才能提供良好的用户体验(UX)。

当然,如果您不想想要处理用户帐户并且您希望您的用户输入许可证密钥,那完全没问题(以及 Keygen 也支持这样做)。 我只是提供另一种方法来处理许可方面的问题,并希望为您的客户提供良好的用户体验。

最后,由于您还提到您希望每年更新这些许可证,因此您可以在策略上设置一个持续时间,以便“完整”许可证将在一年后到期,而“试用”许可证将在最后两周到期,要求您的用户在到期后购买新的许可证。

我可以深入研究,将机器与用户相关联以及类似的事情,但我想我应该尽量简短地回答这个问题,并专注于向用户简单地许可功能。

I'm going to piggyback a bit on @frankodwyer's great answer and dig a little deeper into online-based licensing. I'm the founder of Keygen, a licensing REST API built for developers.

Since you mentioned wanting 2 "types" of licenses for your application, i.e. a "full version" and a "trial version", we can simplify that and use a feature license model where you license specific features of your application (in this case, there's a "full" feature-set and a "trial" feature-set).

To start off, we could create 2 license types (called policies in Keygen) and whenever a user registers an account you can generate a "trial" license for them to start out (the "trial" license implements our "trial" feature policy), which you can use to do various checks within the app e.g. can user use Trial-Feature-A and Trial-Feature-B.

And building on that, whenever a user purchases your app (whether you're using PayPal, Stripe, etc.), you can generate a license implementing the "full" feature policy and associate it with the user's account. Now within your app you can check if the user has a "full" license that can do Pro-Feature-X and Pro-Feature-Y (by doing something like user.HasLicenseFor(FEATURE_POLICY_ID)).

I mentioned allowing your users to create user accounts—what do I mean by that? I've gone into this in detail in a couple other answers, but a quick rundown as to why I think this is a superior way to authenticate and identify your users:

  1. User accounts let you associate multiple licenses and multiple machines to a single user, giving you insight into your customer's behavior and to prompt them for "in-app purchases" i.e. purchasing your "full" version (kind of like mobile apps).
  2. We shouldn't require our customers to input long license keys, which are both tedious to input and hard to keep track of i.e. they get lost easily. (Try searching "lost license key" on Twitter!)
  3. Customers are accustomed to using an email/password; I think we should do what people are used to doing so that we can provide a good user experience (UX).

Of course, if you don't want to handle user accounts and you want your users to input license keys, that's completely fine (and Keygen supports doing that as well). I'm just offering another way to go about handling that aspect of licensing and hopefully provide a nice UX for your customers.

Finally since you also mentioned that you want to update these licenses annually, you can set a duration on your policies so that "full" licenses will expire after a year and "trial" licenses last say 2 weeks, requiring that your users purchase a new license after expiration.

I could dig in more, getting into associating machines with users and things like that, but I thought I'd try to keep this answer short and focus on simply licensing features to your users.

魂归处 2024-07-18 06:54:57

您可以使用 HMAC 来完成此操作。 使用 HMAC,当您对某些内容进行哈希处理时,您还可以使用密钥,这可以帮助您验证消息/序列密钥的完整性和真实性。 这意味着您验证许可证密钥是否真实,并且是您使用秘密字符串生成的。

HMAC 很酷,因为它超级简单。 它不像公钥加密。 它是对称的,这意味着您所需要做的就是生成一个秘密字符串,并且该密钥既可用于生成哈希值,也可用于稍后验证它们。 这正是您创建产品密钥时所需要的。

首先,您需要生成一个密钥:

string secretKey = "mySecr3tKey!"

然后您将生成一个序列号/产品密钥。 对于密钥,我将把它分成 5 个字符的组。 序列密钥的长度为 15 个字符,HMAC 的长度也为 15 个字符。 我们将把它们结合在一起。

为了生成序列密钥,我将使用 GUID,如下所示:

var serialKey = Guid.NewGuid().ToString().ToUpper().Replace("-", "").Substring(0, 15);
// Example Output = 9F1264CB63D54C9

获得序列密钥后,您将使用只有您知道的密钥生成序列的 HMAC 哈希值。 像这样:

var hmacToStore = CalculateHmac(serialKey, secretKey);
// Example Output = EE12A29DA5F2500

现在我们有了序列号/产品密钥和 HMAC,我们将它们组合起来创建完整的序列号。 例如:

Generated Serial Key = 9F1264CB63D54C9
Generated HMAC       = EE12A29DA5F2500
Full Serial Key      = 9F126-4CB63-D54C9-EE12A-29DA5-F2500

现在,当用户输入完整的序列号时,为了验证密钥,您可以从 HMAC 中分离序列号。 然后使用我们的秘密私钥从序列号生成一个新的 HMAC。 如果生成的 HMAC 与许可证密钥中的 HMAC 匹配,则您知道其有效。

var isValid = ValidateLicense(userLicense, secretKey);

以下是演示上述内容的完整代码清单:

using System;
using System.Security.Cryptography;
using System.Text;
                
public class Program
{
    public static void Main()
    {
        //var base64Secret = CreatePrivateKey();
        var base64Secret = "ADmDcqb8KQB/OE1/6VM+kJra6UE0VBcw3uRz4dN268Y7M/UsiSz0FuGSsfNkasmDQFAvJIUUEdB2EBzxtrd0tQ==";

        var licenseKey = CreateLicense(base64Secret);

        var isValid = ValidateLicense(licenseKey, base64Secret);
                    
        Console.WriteLine($"Is Licsense Valid = {isValid}");
    }
    
    static string CreatePrivateKey()
    {
        byte[] secretKey = GenerateRandomCryptographicBytes(64);

        // Convert key to base64 so can easily store it on the database.
        // This key should be kept private.
        var base64Secret = Convert.ToBase64String(secretKey );
        
        Console.WriteLine($"Private Key = {base64Secret}");

        return base64Secret;
    }
    
    static string CreateLicense(string secretKey)
    {
        var licenseKey = Guid.NewGuid().ToString().ToUpper().Replace("-", "").Substring(0, 15);

        Console.WriteLine($"licenseKey = {licenseKey}");

        // Generate a Hmac license using secretkey
        var storedHmacOnDB = CalculateHmac(licenseKey, secretKey).ToUpper();
        var HMACTruncated = storedHmacOnDB.Substring(0, 15);

        Console.WriteLine($"HMAC = {HMACTruncated}");

        var licenseAndHMAC = InsertHyphen($"{licenseKey}{HMACTruncated}");

        Console.WriteLine($"Final User Licsense = {licenseAndHMAC}");

        return licenseAndHMAC;
    }
    
    static bool ValidateLicense(string licenseKey, string secretKey)
    {
        var tmp = licenseKey.Split('-');

        var license = $"{tmp[0]}{tmp[1]}{tmp[2]}";
        var licenseHmac = $"{tmp[3]}{tmp[4]}{tmp[5]}";

        string calculatedHmac = CalculateHmac(license, secretKey);
        var HMACTruncated = calculatedHmac.ToUpper().Substring(0, 15);

        bool isValid = licenseHmac.Equals(HMACTruncated, StringComparison.OrdinalIgnoreCase);

        return isValid;
    }
    
    static string InsertHyphen(string input, int everyNthChar = 5)
    {
        var sb = new StringBuilder();
        for (int i = 0; i < input.Length; i++)
        {
            sb.Append(input[i]);
            if ((i + 1) % everyNthChar == 0 && i != input.Length - 1)
            {
                sb.Append("-");
            }
        }
        return sb.ToString();
    }
    
    static byte[] GenerateRandomCryptographicBytes(int keyLength)
    {
        byte[] key = new byte[64];
        using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(key);
        }
        return key;
    }
    
    static string CalculateHmac(string data, string hashKeyBase64)
    {
        var byteArray = Convert.FromBase64String(hashKeyBase64);
        return CalculateHmac(data, byteArray);
    }

    static string CalculateHmac(string data, byte[] hashKey)
    {
        var hmac = new HMACMD5(hashKey);
        byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
        }
    }
}

上面的代码将生成一个 15 个字符的序列号、一个 15 个字符的 HMAC,然后将它们组合成一个 30 个字符的最终序列号密钥,如下所示:

Serial = A273C22C9FD84DF
HMAC = 301E028243A25B2
Final Serial = A273C-22C9F-D84DF-301E0-28243-A25B2

如果将序列号传递给 ValidateLicense 函数,则会根据您的密钥告诉您其是否有效

var isValid = ValidateLicense("19460-63CC4-BB45F-6F6E6-4A983-DC0B1", base64Secret);

理想情况下,验证函数和您的密钥应该位于服务器端。 客户端应用程序应调用 API 并将产品密钥传递给服务器进行验证。 如果您将秘密存储在应用程序中,恶意用户可能会尝试反汇编代码以窃取您的私钥。

You can use HMAC to accomplish this. With HMAC, when you hash something you also use a secret key and this helps you verify the integrity and authenticity of the message/serial key. It means you verify that a license key is authentic and one that you have generated with your secret string.

HMAC is cool because its super simple. Its not like public key crypto. Its symetric which means all you need to do is generate one secret string and that key can be used to both generate hashes and also validate them later on. This is exactly what you need when creating product keys.

To get started, first you need to generate a secret key:

string secretKey = "mySecr3tKey!"

Then you would generate a serial/product key. For the key, I am going to split it into groups of 5 characters. The serial key will be 15 characters in length and the HMAC will also be 15 characters in length. We will combine them together.

To generate the serial key, I will use a GUID Like this:

var serialKey = Guid.NewGuid().ToString().ToUpper().Replace("-", "").Substring(0, 15);
// Example Output = 9F1264CB63D54C9

Once you have the serial key, you would then generate an HMAC hash of the serial using the secret key that is known only to you. Like this:

var hmacToStore = CalculateHmac(serialKey, secretKey);
// Example Output = EE12A29DA5F2500

Now we have the serial/product key and the HMAC we would combine them to create the full serial. For example:

Generated Serial Key = 9F1264CB63D54C9
Generated HMAC       = EE12A29DA5F2500
Full Serial Key      = 9F126-4CB63-D54C9-EE12A-29DA5-F2500

Now, when the user enters the full serial key, to validate the key you can split the serial number from the HMAC. Then using our secret private key generate a new HMAC from the serial number. If the generated HMAC matches the HMAC from the license key, you know its valid.

var isValid = ValidateLicense(userLicense, secretKey);

Here's the full code listing that demonstrates the above:

using System;
using System.Security.Cryptography;
using System.Text;
                
public class Program
{
    public static void Main()
    {
        //var base64Secret = CreatePrivateKey();
        var base64Secret = "ADmDcqb8KQB/OE1/6VM+kJra6UE0VBcw3uRz4dN268Y7M/UsiSz0FuGSsfNkasmDQFAvJIUUEdB2EBzxtrd0tQ==";

        var licenseKey = CreateLicense(base64Secret);

        var isValid = ValidateLicense(licenseKey, base64Secret);
                    
        Console.WriteLine(
quot;Is Licsense Valid = {isValid}");
    }
    
    static string CreatePrivateKey()
    {
        byte[] secretKey = GenerateRandomCryptographicBytes(64);

        // Convert key to base64 so can easily store it on the database.
        // This key should be kept private.
        var base64Secret = Convert.ToBase64String(secretKey );
        
        Console.WriteLine(
quot;Private Key = {base64Secret}");

        return base64Secret;
    }
    
    static string CreateLicense(string secretKey)
    {
        var licenseKey = Guid.NewGuid().ToString().ToUpper().Replace("-", "").Substring(0, 15);

        Console.WriteLine(
quot;licenseKey = {licenseKey}");

        // Generate a Hmac license using secretkey
        var storedHmacOnDB = CalculateHmac(licenseKey, secretKey).ToUpper();
        var HMACTruncated = storedHmacOnDB.Substring(0, 15);

        Console.WriteLine(
quot;HMAC = {HMACTruncated}");

        var licenseAndHMAC = InsertHyphen(
quot;{licenseKey}{HMACTruncated}");

        Console.WriteLine(
quot;Final User Licsense = {licenseAndHMAC}");

        return licenseAndHMAC;
    }
    
    static bool ValidateLicense(string licenseKey, string secretKey)
    {
        var tmp = licenseKey.Split('-');

        var license = 
quot;{tmp[0]}{tmp[1]}{tmp[2]}";
        var licenseHmac = 
quot;{tmp[3]}{tmp[4]}{tmp[5]}";

        string calculatedHmac = CalculateHmac(license, secretKey);
        var HMACTruncated = calculatedHmac.ToUpper().Substring(0, 15);

        bool isValid = licenseHmac.Equals(HMACTruncated, StringComparison.OrdinalIgnoreCase);

        return isValid;
    }
    
    static string InsertHyphen(string input, int everyNthChar = 5)
    {
        var sb = new StringBuilder();
        for (int i = 0; i < input.Length; i++)
        {
            sb.Append(input[i]);
            if ((i + 1) % everyNthChar == 0 && i != input.Length - 1)
            {
                sb.Append("-");
            }
        }
        return sb.ToString();
    }
    
    static byte[] GenerateRandomCryptographicBytes(int keyLength)
    {
        byte[] key = new byte[64];
        using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(key);
        }
        return key;
    }
    
    static string CalculateHmac(string data, string hashKeyBase64)
    {
        var byteArray = Convert.FromBase64String(hashKeyBase64);
        return CalculateHmac(data, byteArray);
    }

    static string CalculateHmac(string data, byte[] hashKey)
    {
        var hmac = new HMACMD5(hashKey);
        byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
        }
    }
}

The above code will generate a 15 character serial, a 15 character HMAC and then combine them into a 30 character final serial key, like this:

Serial = A273C22C9FD84DF
HMAC = 301E028243A25B2
Final Serial = A273C-22C9F-D84DF-301E0-28243-A25B2

If you pass the serial through to the ValidateLicense function it will tell you if its valid or not based on your secretKey

var isValid = ValidateLicense("19460-63CC4-BB45F-6F6E6-4A983-DC0B1", base64Secret);

Ideally, the validate function and your secret key should be server side. The client app should call an API and pass the product key to the server to validate. If your stored the secret in your app, a nefarious user could attempt to disassemble the code to try to steal your private key.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文