伴随着.net开发技术的成熟,软件开发进入控件化时代。越来越多的公司投入软件开发领域,企图控制软件产业链的上游:提供解决方案。其中一部分公司就是提供控件。控件对外就象是一个黑盒子,借助于方法,属性和事件,开发人员即可轻易的开发出专业的应用程序,与此同时,软件的保护方法也不断出现。在共享软件时代,软件开发人员开发好程序,然后设计一个序列号生成算法,对正式许可的用户发布序列号,生成注册文件。在应用程序启动时检查注册文件,如果有则继续运行,否则中止运行或是提供部分功能。
.net 技术的出现,也提供了相应的软件保护解决方案。本文详细讨论.net 技术的软件保护方案与实现。我们可以采用各种方式来生产许可,授权用户使用我们开发的控件。常见的方式如下:
注册表的验证方式实现起来容易,但是也很容易发生验证异常。如果用户装有注册表监控程序,跟踪对注册表的每一项改动,则会很容易定位到键值,进而分析和利用,此外,这种方式对XCOPY也有影响,不方便部署应用程序;许可文件根据用户的硬件信息,比如硬盘序列号,或是MAC地址,邮件,生成一个许可文件,放在控件可以找到的地方,在运行时找到该许可文件,如果找不到,则阻止控件的运行;远程Web服务需用客户端能联网,在控件运行时向服务器发出查询许可的命令,如果找不到,则停止运行;如果用户不联网,这种方式有局限性,用户根本不能在脱机的情况下尝试运行该控件。
需要理解的对象:LicenseProvider
MSDN中的解释是:提供 abstract 基类以便实现许可证提供程序。从 LicenseProvider 继承时,必须重写 GetLicense 方法。我们可以理解为一个许可证的提供程序,在控件需要验证时,提供许可授权,如果不提供许可,则不能使用该控件,抛出异常。这是个抽象类,不允许实例化,我们从该类派生,并且重写GetLicense方法。
public class FileLicenseProvider : System.ComponentModel.LicenseProvider { public override License GetLicense(LicenseContext context, Type type, object instance, bool allowExceptions) { if (context.UsageMode == LicenseUsageMode.Designtime) { // 开发人员设计时不需要许可,直接颁发许可证 return new FileLicense(this, "The App"); } else { string licenseFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, type.FullName+".lic"); if (File.Exists(licenseFile)) return new FileLicense(this, "The App"); else return null; } } } |
通过覆盖GetLicense方法,我们获取需要验证的控件的类型信息,然后在当前应用程序目录下查找以控件的lic文件,如找到,颁发许可,否则返回null值。
License
MSDN中的解释为:为所有许可证提供 abstract 基类。向控件的特定实例授予许可证。通俗的理解就是许可,给控件颁发的许可,表示控件已经授权过了,可以被使用。下面的代码可以帮助您理解许可的含义。
public class FileLicense : System.ComponentModel.License { //许可验证提供程序 private FileLicenseProvider owner; private string key; public FileLicense(FileLicenseProvider owner, string key) { this.owner = owner; this.key = key; } //许可Key,通俗的理解是序列号。 public override string LicenseKey { get { return key; } } public override void Dispose() { } } |
FileLicense会应用到控件上,如果返回给控件的FileLicense为空,则控件会抛出异常。
应用许可提供程序
开发一个控件,如何应用许可验证呢?
如下代码所示,我们开发一个MyControl的控件,同时给它加上LicenseProvider特性(attribute),这样在该控件运行时,会用FileLicenseProvider来实行许可验证。
[LicenseProvider(typeof(FileLicenseProvider))] public class MyControl : WebControl |
此外,我们还需要在控件的构造方法中进一步调用验证方式:
public MyControl() { try { license = LicenseManager.Validate(typeof(MyControl), this); } catch { HttpContext.Current.Response.Write("MyControl控件未授权,请联系程序开发商"); } } |
如果该控件获取正确的授权,则会继续运行,否则因得不到许可而不能继续生成。 LicenseManager类会找到FileLicenseProvider类的GetLicense方法,来获取许可,经过各种验证方式(注册表,许可文件)来提供许可实例,如果验证失败,则会抛出LicenseException,表示无法找到许可。
许可验证服务器
在注册表验证方式中,我们需要在控件的安装程序中嵌入脚本,在注册表中写入相应的键值;在本地许可文件验证方式下,我们需要根据序列号生产相应的许可文件;在远程Web服务的验证方式下,我们还需要设计一个远程的验证服务器,接受客户端的验证请求。 下面我们来讨论一下如何设计这个服务器应用程序。
Web服务是跨平台的通用协议,借助于SOAP(简单对象访问协议),我们能接受各种平台的验证请求。
借助于ASP.NET Web服务,我们可以轻易的实现这个服务器端应用。 打开Visual Studio 2008,新建ASP.NET Web Service Application
把新建的服务名称改LicenseService.asmx,然后在该服务中提供验证方法
[WebMethod] public bool ValidateLicense(string mac) { bool ret = Validate(mac); return ret; } |
Validate方法会去查找数据库记录,返回结果,提供给客户端。 为此,我们还需要设计数据库。
这里,我们采用验证客户端MAC地址的方式,如果能找到该地址,则把Enabled设为1,否则找不到,表示该MAC地址未授权,返回false给许可验证提供程序。SQL 脚本如下:
CREATE TABLE [dbo].[LicenseUser]( [MAC] [varchar](50) COLLATE Chinese_PRC_CI_AS NOT NULL, [Enabled] [smallint] NULL, CONSTRAINT [PK_LicenseUser] PRIMARY KEY CLUSTERED ( [MAC] ASC )WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] |
应用程序
由于开发的是Web控件,我们设计一个Web站点,来测试我们的控件保护方案。右击解决方案,添加现有站点(Web site)
把我们的控件从工具栏中拖动到一个Web页面中,生成应用程序,然后F5,调试运行。页面的源代码如下:
<uc:MyControl ID="MyControl1" runat="server" />
另外,在Web.config中添加如下代码,您可以不必在每一个页面中都加入控件引用。
<pages> <controls> <add tagPrefix="uc" namespace="HTSystem" assembly="HTSystem.Component, Version=1.1.0.0, Culture=neutral, PublicKeyToken=c2c8c99b69b1086e"/> </controls> </pages> |
您可能也注意到了,我们给自己写的组件MyControl添加了版本和强命名元数据,这样可以保护我们的组件。
总结
本文试图给您提供几种常用的许可验证的方式。文中详细讨论了许可文件的验证方案,分析了如何借助Web服务技术,设计远程验证服务器,另外,您还应该了解注册表的方式的验证方式。 您可以通过阅读源代码,详细了解这三种方式的实现。