C#桌面应用程序的许可证

时间:2010-09-02 05:49:41

标签: c# copy-protection

如何向C#桌面应用程序添加许可证?我需要找到一种合适的免费方法来防止未经授权的用户安装我的软件。

6 个答案:

答案 0 :(得分:56)

我可能有点晚了,但是我花了一些时间试图找到一种快速有效的方法来保护一个小C#应用程序,我想分享我的结果。

您似乎可以轻松地使用RSA构建自己的,相当安全的许可系统。

显然,在保护软件方面没有任何防弹措施(这就像保护你的房子免受窃贼:警报,吠叫狗和栅栏使它比它的价值更麻烦,但它们不会阻止某人决定进入)

因此,让它比它的价值更麻烦是软件保护中的关键词:如果你提供1,000,000美元的E.R.P.系统,您可能希望通过网络服务获得非常好的保护(用户为系统支付这么多费用不会让系统不断上网)

但是,如果您只为一个小应用程序收取5至30美元的费用,用户将不会忍受非常严厉的授权。

我认为最简单的系统是对包含产品,用户及其持续时间的详细信息的许可证文件进行数字签名。

这意味着对许可证文件的任何修改都会使数字签名无效。

可以使用SignData方法从DSACryptoServiceProvider类获取数字签名。

需要私钥才能对数据进行签名,并且该密钥的公共部分可用于验证签名:(因此应用程序必须可以访问公钥)

DSAXCryptoServiceProvider具有创建和使用键的方法:

DSACryptoServiceProvider.ToXMLString(bool includePrivate);

返回Public或Public&当前在服务提供程序中的私钥作为XML字符串。

DSACryptoServiceProvider.FromXMLString(String xmlString)

此方法使用从DSACryptoServiceProvider.ToXMLString()获取的现有私钥或公钥设置新的DSACryptoServiceProvider

该系统安全性的唯一缺陷是用户可能会破坏提供自己的公钥。这将允许他们从自己的私钥生成自己的许可文件。

这可以通过另外为应用程序签署所需资源(如包含应用程序的基本逻辑的.dll,甚至是.exe本身)来解决 - 因此,如果更改了公钥,则此附加(隐藏) )签名将失效。

其他改进方法包括隐藏许可条款(使用二进制格式化程序将包含许可条款的数据结构序列化为字节数组,然后使用Convert.ToBase64String()将非常有效地模糊许可条款,甚至如果用户能够替换公钥,他们仍然需要计算数据的表示

我有一个我编写的示例系统,但它太大了,不能完全引用,但这是它的CreateLicense方法:

    /// <summary>
    /// use a private key to generate a secure license file. the private key must match the public key accessible to
    /// the system validating the license.
    /// </summary>
    /// <param name="start">applicable start date for the license file.</param>
    /// <param name="end">applicable end date for the license file</param>
    /// <param name="productName">applicable product name</param>
    /// <param name="userName">user-name</param>
    /// <param name="privateKey">the private key (in XML form)</param>
    /// <returns>secure, public license, validated with the public part of the key</returns>
    public static License CreateLicense(DateTime start, DateTime end, String productName, String userName, String privateKey)
    {
        // create the licence terms:
        LicenseTerms terms = new LicenseTerms()
        {
            StartDate = start,
            EndDate = end,
            ProductName = productName,
            UserName = userName
        };

        // create the crypto-service provider:
        DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();

        // setup the dsa from the private key:
        dsa.FromXmlString(privateKey);

        // get the byte-array of the licence terms:
        byte[] license = terms.GetLicenseData();

        // get the signature:
        byte[] signature = dsa.SignData(license);

        // now create the license object:
        return new License()
        {
            LicenseTerms = Convert.ToBase64String(license),
            Signature = Convert.ToBase64String(signature)
        };
    }

验证方法:

    /// <summary>
    /// validate license file and return the license terms.
    /// </summary>
    /// <param name="license"></param>
    /// <param name="publicKey"></param>
    /// <returns></returns>
    internal static LicenseTerms GetValidTerms(License license, String publicKey)
    {
        // create the crypto-service provider:
        DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();

        // setup the provider from the public key:
        dsa.FromXmlString(publicKey);

        // get the license terms data:
        byte[] terms = Convert.FromBase64String(license.LicenseTerms);

        // get the signature data:
        byte[] signature = Convert.FromBase64String(license.Signature);

        // verify that the license-terms match the signature data
        if (dsa.VerifyData(terms, signature))
            return LicenseTerms.FromString(license.LicenseTerms);
        else
            throw new SecurityException("Signature Not Verified!");
    }

许可条款类:

    /// <summary>
    /// terms of the license agreement: it's not encrypted (but is obscured)
    /// </summary>
    [Serializable]
    internal class LicenseTerms
    {
        /// <summary>
        /// start date of the license agreement.
        /// </summary>
        public DateTime StartDate { get; set; }

        /// <summary>
        /// registered user name for the license agreement.
        /// </summary>
        public String UserName { get; set; }

        /// <summary>
        /// the assembly name of the product that is licensed.
        /// </summary>
        public String ProductName { get; set; }

        /// <summary>
        /// the last date on which the software can be used on this license.
        /// </summary>
        public DateTime EndDate { get; set; }

        /// <summary>
        /// returns the license terms as an obscure (not human readable) string.
        /// </summary>
        /// <returns></returns>
        public String GetLicenseString()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                // create a binary formatter:
                BinaryFormatter bnfmt = new BinaryFormatter();

                // serialize the data to the memory-steam;
                bnfmt.Serialize(ms, this);

                // return a base64 string representation of the binary data:
                return Convert.ToBase64String(ms.GetBuffer());

            }
        }

        /// <summary>
        /// returns a binary representation of the license terms.
        /// </summary>
        /// <returns></returns>
        public byte[] GetLicenseData()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                // create a binary formatter:
                BinaryFormatter bnfmt = new BinaryFormatter();

                // serialize the data to the memory-steam;
                bnfmt.Serialize(ms, this);

                // return a base64 string representation of the binary data:
                return ms.GetBuffer();

            }
        }

        /// <summary>
        /// create a new license-terms object from a string-representation of the binary
        /// serialization of the licence-terms.
        /// </summary>
        /// <param name="licenseTerms"></param>
        /// <returns></returns>
        internal static LicenseTerms FromString(String licenseTerms)
        {

            using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(licenseTerms)))
            {
                // create a binary formatter:
                BinaryFormatter bnfmt = new BinaryFormatter();

                // serialize the data to the memory-steam;
                object value = bnfmt.Deserialize(ms);

                if (value is LicenseTerms)
                    return (LicenseTerms)value;
                else
                    throw new ApplicationException("Invalid Type!");

            }
        }

    }

答案 1 :(得分:10)

.NET有很多许可证管理系统(甚至还有一个built-in for licensing controls)。一个快速谷歌围绕“.NET许可证管理器”推出了Open License系统,这是免费的。

我希望你能轻松找到更多。

答案 2 :(得分:2)

我认为有必要为此添加另一个答案,因为公认的答案似乎引用了当前未维护的项目。

我建议您查看Standard.Licensing,它是.Net的免费开放源代码许可库,可与.Net Framework,Mono,.Net Core,.Net Standard和Xamarin一起使用。通过添加对较新平台(特别是.Net Core和.Net Standard)的支持来对旧版Portable.Licensing进行现代化。

Standard.Licensing通过创建一个数字签名的XML文件来工作,该文件包含与您的产品相关的信息,例如产品的类型和有效期。当您检查许可证时,可以验证XML文件未更改的事实,然后您的应用程序可以信任许可证文件中提出的声明。 (请注意,您可能还需要验证the computer's clock is accurate以防止某人更改日期。)

Standard.Licensing使用Elliptic Curve Digital Signature Algorithm (ECDSA)算法对XML文件进行签名,该算法在创建许可证文件时使用一对密钥,一个公共密钥,一个私有密钥。您只需要使用公共密钥来解密和验证许可证。由于不可能仅使用公共密钥来修改许可证文件,因此您可以安全地将公共文件包含在应用程序中,并且无需采取诸如混淆程序集等方法来防止人们看到公共密钥。请注意,这类似于Simon Bridge's answer above中提到的方法。

Standard.Licensing具有可用于创建和验证许可证的流利API。这是他们网站上的片段,显示了如何创建许可证:

var license = License.New()  
    .WithUniqueIdentifier(Guid.NewGuid())  
    .As(LicenseType.Trial)  
    .ExpiresAt(DateTime.Now.AddDays(30))  
    .WithMaximumUtilization(5)  
    .WithProductFeatures(new Dictionary<string, string>  
        {  
            {"Sales Module", "yes"},  
            {"Purchase Module", "yes"},  
            {"Maximum Transactions", "10000"}  
        })  
    .LicensedTo("John Doe", "john.doe@example.com")  
    .CreateAndSignWithPrivateKey(privateKey, passPhrase);

然后在您的应用程序中加载并验证许可证文件:

using Standard.Licensing.Validation;

var license = License.Load(...);
var validationFailures = license.Validate()  
                                .ExpirationDate()  
                                .When(lic => lic.Type == LicenseType.Trial)  
                                .And()  
                                .Signature(publicKey)  
                                .AssertValidLicense();

答案 3 :(得分:1)

从技术上讲,创建有效且安全的许可系统并非易事。如果您打算开发商业软件,我建议为其使用一些商业解决方案。自定义编码的许可系统容易受到攻击。

我在Treek's Licensing Library方面拥有最好的经验。即使对于单个开发人员也很便宜,它的安全性和良好的支持。在比较成本时,TLL比租用自己的开发商来做同样的工作便宜。

此外,您还需要保护您的资源。为此,我们确实使用EAZ Fuscator,但也有免费的可用选项。 EAZ非常好,但是非常昂贵。

答案 4 :(得分:0)

一种方法是推出自己的partial key verification system。 Code Project上有一个VB.NET版本:

http://www.codeproject.com/KB/security/cdkeys.aspx

答案 5 :(得分:0)

警惕您为保护应用程序付出了多少努力;如果非常强大(如每次运行时必须输入密码),可能会浪费时间,如果它很容易被打破或关闭给用户。

有关软件保护(游戏)的有趣文章可以在这里找到: http://www.positech.co.uk/talkingtopirates.html