我目前正在开发一个产品(用c#开发的),可以免费下载和安装,但版本非常有限。要获得所有功能,用户必须支付许可费并获得密钥。然后,该密钥将被输入应用程序以“解锁”完整版本。

像这样使用许可密钥是很常见的,我想知道:

这个问题通常是怎么解决的? 如何生成密钥以及应用程序如何验证密钥? 我怎样才能避免一个密钥被发布在互联网上,并被其他没有支付许可证的人使用(一个基本上不是“他们的”密钥)。

我想我还应该以某种方式将密钥绑定到应用程序的版本,这样就有可能在功能版本中对新密钥收费。

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


当前回答

警告:你不能阻止用户盗版,只能让诚实的用户更容易做正确的事情。

假设你不想为每个用户做一个特殊的构建,那么:

为产品生成一个密钥 取用户名 使用(例如)SHA1连接用户名、密钥和散列 将SHA1散列解压缩为字母数字字符串。这是个人用户的“产品密钥” 在程序中,执行相同的散列,并与产品密钥进行比较。如果相等,好的。

但是,我再重复一遍:这并不能阻止盗版


我最近读到,这种方法在密码学上不是很可靠。但是这个解决方案已经很弱了(因为软件本身必须在某个地方包含密钥),所以我不认为这个发现会使解决方案失效。

只是觉得我真的应该提一下;如果您打算从这里推导出其他东西,请小心。

其他回答

我是Cryptolens软件授权平台背后的开发人员之一,从14岁开始就一直在开发授权系统。在这个回答中,我根据多年来的经验总结了一些建议。

解决这个问题的最佳方法是设置一个许可密钥服务器,应用程序的每个实例都将调用该服务器来验证许可密钥。

许可证密钥服务器的好处

使用许可密钥服务器的优点是:

您可以随时更新或阻止许可证密钥立即生效。 每个许可密钥可以被锁定到一定数量的机器上(这有助于防止用户在线发布许可密钥供其他人使用)。

注意事项

虽然在线验证许可证可以让您更好地控制应用程序的每个实例,但internet连接并不总是存在(特别是如果您的目标是大型企业),因此我们需要另一种方式来执行许可证密钥验证。

解决方案是始终使用公钥加密系统(如RSA或ECC)对来自服务器的许可密钥响应进行签名(如果计划在嵌入式系统上运行,可能更好)。您的应用程序应该只有用于验证许可密钥响应的公钥。

因此,在没有互联网连接的情况下,您可以使用之前的许可密钥响应。确保在响应中存储日期和机器标识符,并检查它不是太旧(例如。您允许用户离线最多30天,等等),并且许可密钥响应属于正确的设备。

请注意,您应该始终检查许可密钥响应的证书,即使您连接到internet),以确保它在离开服务器后没有被更改(即使您的API到许可密钥服务器使用https,这仍然必须完成)。

保护秘密算法

大多数。net应用程序都可以很容易地进行反向工程(微软提供了一个反汇编程序来获得IL代码,一些商业产品甚至可以检索源代码,例如。c#)。当然,您总是可以混淆代码,但它永远不会是100%安全的。

在大多数情况下,任何软件许可解决方案的目的都是帮助诚实的人变得诚实(即愿意付费的诚实用户在试用期满后不会忘记付费,等等)。

然而,您可能仍然有一些您不想泄露给公众的代码(例如。预测股票价格的算法等)。在这种情况下,唯一的方法是创建一个API端点,您的应用程序将在每次执行方法时调用该端点。它需要互联网连接,但它确保你的秘密代码永远不会被客户端机器执行。

实现

如果你不想自己实现所有的东西,我建议你看看这个教程(Cryptolens的一部分)

生成许可密钥的方法有很多,但真正安全的方法很少。这很遗憾,因为对于公司来说,许可证密钥的价值几乎与真正的现金相同。

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

Only your company should be able to generate license keys for your products, even if someone completely reverse engineers your products (which WILL happen, I speak from experience). Obfuscating the algorithm or hiding an encryption key within your software is really out of the question if you are serious about controlling licensing. If your product is successful, someone will make a key generator in a matter of days from release. A license key should be useable on only one computer (or at least you should be able to control this very tightly) A license key should be short and easy to type or dictate over the phone. You don't want every customer calling the technical support because they don't understand if the key contains a "l" or a "1". Your support department would thank you for this, and you will have lower costs in this area.

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

The answer is simple but technically challenging: digital signatures using public key cryptography. Your license keys should be in fact signed "documents", containing some useful data, signed with your company's private key. The signatures should be part of the license key. The product should validate the license keys with the corresponding public key. This way, even if someone has full access to your product's logic, they cannot generate license keys because they don't have the private key. A license key would look like this: BASE32(CONCAT(DATA, PRIVATE_KEY_ENCRYPTED(HASH(DATA)))) The biggest challenge here is that the classical public key algorithms have large signature sizes. RSA512 has an 1024-bit signature. You don't want your license keys to have hundreds of characters. One of the most powerful approaches is to use elliptic curve cryptography (with careful implementations to avoid the existing patents). ECC keys are like 6 times shorter than RSA keys, for the same strength. You can further reduce the signature sizes using algorithms like the Schnorr digital signature algorithm (patent expired in 2008 - good :) ) This is achievable by product activation (Windows is a good example). Basically, for a customer with a valid license key, you need to generate some "activation data" which is a signed message embedding the computer's hardware id as the signed data. This is usually done over the internet, but only ONCE: the product sends the license key and the computer hardware id to an activation server, and the activation server sends back the signed message (which can also be made short and easy to dictate over the phone). From that moment on, the product does not check the license key at startup, but the activation data, which needs the computer to be the same in order to validate (otherwise, the DATA would be different and the digital signature would not validate). Note that the activation data checking do not require verification over the Internet: it is sufficient to verify the digital signature of the activation data with the public key already embedded in the product. Well, just eliminate redundant characters like "1", "l", "0", "o" from your keys. Split the license key string into groups of characters.

我以前用过Crypkey。这是许多可用的之一。

使用任何许可方案,您只能在一定程度上保护软件。

我通过将我的程序与一个不和谐的服务器连接来解决这个问题,它在一个特定的聊天中检查用户输入的产品密钥是否存在并且仍然有效。通过这种方式获得产品密钥,用户将被迫破解不和谐,这是非常困难的。

我知道这是一个老问题,但我在为我的一个应用程序重新编写许可流程时引用了这个问题。

在阅读了大量的意见并依靠过去的许可代码经验后,我想出了这个过程。

public static class LicenseGenerator
{
    private static string validChars = "ACEFHJKMNPRSTUVWXYZ234579";
    private static Random rnd = new Random(Guid.NewGuid().GetHashCode());

    /// <summary>
    /// Generate a license code
    /// </summary>
    /// <param name="length">length of each phrase</param>
    /// <param name="number">number of phrases separated by a '-'</param>
    /// <returns></returns>
    public static string GetNewCode(int length, int number)
    {
        string license = string.Empty;

        for (int numberOfPhrases = 0; numberOfPhrases < number; numberOfPhrases++)
        {
            license += getPhrase(length);
            if (numberOfPhrases < number)
                license += "-";
        }

        return license.TrimEnd('-');
    }

    /// <summary>
    /// generate a phrase
    /// </summary>
    /// <param name="length">length of phrase</param>
    /// <returns></returns>
    private static string getPhrase(int length)
    { 
        string phrase = string.Empty;

        for (int loop = 0; loop < length; loop++)
        {
            phrase += validChars[rnd.Next(validChars.Length)];
        }

        return phrase;
    }
}

你真的不想提供一个有相似字母的代码;当最终用户输入时,它会造成混乱。像6和G, B和8,L, I和1。当然,如果你真的想要它们,你可以随时把它们加回来……上面的代码将使用“validChars”中的字符生成类似xxxx-xxxx-xxxx-xxxx的许可证。调用GetNewCode(4,4)将返回与上面类似的代码。

我使用Azure函数注册然后验证代码。当我的应用程序注册代码时,它会生成一个加密散列,其中包含安装、设备和/或用户独有的内容。它被提供给注册函数,并与密钥一起存储在Azure的DB中。

验证重新生成密钥,并向其提供许可代码、IP地址(在我的例子中,IP地址不会改变,如果改变了,那么无论如何都需要更新)和重新生成的散列,然后Azure函数在应用程序获得许可时返回。我确实在他们的服务器上存储了一个“临时”密钥,允许应用运行一段时间而不返回。

当然,我的应用程序必须在网络上,它的工作无论如何。

因此,最终结果是一个供最终用户输入的简单密钥,以及在后端管理许可证的简单流程。如果需要,我还可以使许可证无效。