前述:

这是一则比较蛋疼的前述。

我在http://sxw.ptbird.cn 个税计算的授权版本中提到过,我自己搞了一个C# winform的授权验证方案,为什么要搞这个东西?因为我一开始写软件忽略了授权验证这个东西,真的是忽略了!

因为我本身并不是做C# Winform开发的,所以我也不知道这个授权到底应该怎么弄,网上也没有一个适合我的说法。因此当我把软件写出来之后,就面临是否要进行授权的方案,如果要进行授权,那么应该怎么搞。

根据我自己之前的一些积累和相关内容的查阅,最后我还是根据自己的想法,设计和实现了一套授权验证策略。

我把它称之为postbird_license,典型的C/S架构实现的授权验证服务。

描述:

其实催生它的原因很简单,就是因为我确实不知道软件的授权验证方式应该怎么处理

我想这个问题可能不仅仅我有,或许每个winform的开发者都应该有自己的一套授权验证方式,前提是想做授权,说句大白话就是软件是需要付费才能用滴!

为什么叫做可扩展软件授权验证解决方案,其实很简单,因为这个授权验证的方案,与软件本身并没有特别直接的联系,即使像我一样,一开始忘记了去设计授权,后面也是很容易和轻松地加上去。

因为我不知道是不是所有的授权都是这么设计的,我也不知道这个叫法是否正确。(反正是自己起的名字,任性挡不住)

下面是我的debug的一个分支版本,也是最早的是时候用到的,软件打开后,如果没有授权,则需要第一次授权验证。

c1

只有授权验证通过才能使用软件进入主要的winform。

299d-tmp

实现:

几个关键点:

1、保证本机验证的确定性。(本机验证需要保留授权许可证明,不需要每次验证授权)

2、保证授权的可靠性。

3、保证授权的内容的多变性。

授权验证的流程:

1、判断本机是否已经授权了,若没有授权则进行授权。

2、输入姓名和相关的授权码以及验证码发送验证请求。

3、server进行授权验证的判断。

4、记录本机授权信息。

5、授权结束。

一、本机授权的验证策略(机器标记码)

首先,获取本机的各种码,这一点很关键,在我开源的版本中,获取了Mac地址,获取了CPU序列号,获取了硬盘ID,获取了网卡硬件地址获取了计算机名(计算机名最后没用,万一改名字还要重新授权,比较麻烦,但是也是一个不错的选择)。

为什么要拿到这么多码?

开源的版本中我是采用了txt文本的方式记录了一个机器码,这个机器码是我自己生成的,生成的方式很简单,将各种码通过一些随机的字符进行拼接,拼接后再通过MD5加密,然后进行des的加密成一个机器码的串,写入文本中,并保存在根目录

换句话说,这个授权文件就是"明文"给你了,但是这个明文是根据一种规则混合了时间戳的加密串,想解出来是不可能的,因为到最后我都忘记我是怎么加密的,看代码才知道

c3

打开这个文件实际上就是一串贼长贼长的串,一旦你破坏了,肯定是授权失败的,而这个串的加密是可以根据你自己的方式进行的。

(第一个串是一个机器码,第二个串后面再说)

c4

开源的版本中我用了一个key,而这个key是写死的。

但是在我实际使用的版本中,这个key是通过server的请求,返回的一个加密的串,混合在机器码中再次进行加密。

因此注册是必须联网的!

实际上,机器码最主要的作用在于,规避软件复制黏贴使用

软件到了另一台电脑上,由于机器码不匹配,因此另一台电脑是不能使用的。

同样需要注册授权。

二、本机授权的验证策略(yes标记码)

yes标记码是什么东西?

前面说了已经把一个机器码写进了文档里,为什么要写这样一个机器码,主要就是为了验证本机已经注册,但是仅仅写进去,是不能进行验证的,因为加入了时间戳,无法验证,除非每次联网请求server返回数据库中记录的key,但是这样子比较麻烦,因此我就想了另外一个方式。

弄了一个yes标记码,所谓的yes标记码,就是根据机器码的另一种混合加密。我们都知道des加密解密是需要key的(我还是有认真上密码学的),而这个key和机器码返回的key不是一个key。因此使用一个des加密的key,再根据某种加密规则(我反正是md5和des混着用各种字符串混着用)来讲机器码进行加密,生成一个yes码,也就是上面图片中第二行贼长贼长的字符串,这样的记录下来。

每次软件启动过程,只要读取文件,首先验证机器码是否正确(判断软件是否是注册在本机),将机器码进行规则加密,验证是否与yes码符合一个标准即可判断本机是否注册。

部分代码如下:


//验证license中机器码是否与本机的机器码一致,如果不一致要求注册
//如果文件不存在也要求注册
public bool checkCode()
{
//验证guid和文件是否存在
try
{
if (File.Exists(@""+""+this.file))
{
    StreamReader sr = new StreamReader(this.file, Encoding.Default);
    string line;
    if ((line = sr.ReadLine()) != null)
    {
        string tmpGuid=this.Decrypt(line); //需要解密
        //验证记录的id与当前机器是否一样
        //不一样则需要进行注册 如果一样则验证yes码是否是开启的
        if (tmpGuid.Equals(this.guid))
        {
            //读取第二行
            if ((line = sr.ReadLine()) != null)
            {
                tmpGuid = this.Decrypt(line);//需要解密
                if (tmpGuid.Equals(this.checkStatus))
                {
                    sr.Close();
                    return true;
                }
            }
        }
    }
    sr.Close();
}
return false;
}catch(Exception ex){
return false;
}
           

三、注册授权的验证策略(C/S架构的实现)

上面实现了本机注册的验证,软件注册后,只要在本机使用,不需要再次进行注册授权。

那么如果没有注册授权,则需要联机验证(没有做离线验证,觉得强制要求联机是很有必要的)。

注册授权的过程如下:

1、用户购买软件

2、添加生成用户的授权码和验证码(我用了两个码)

3、用户在线联机请求注册授权

4、server进行验证

5、授权结束

其实注册授权的过程并不是很复杂,关键问题是要开发一个server来服务软件的授权。现在这个server我没有开源,因为我还在用,不过很简单就可以实现。

你可以在server上手工输入姓名和电话,随机生成两个串,记录在数据库中,然后用户在客户端发送post请求,server的链接只要接收参数进行验证就行了,客户端实际上不需要做什么改动

所以我的cs中有一个属性是 url就是请求验证的url。

定义如下:

 private string url = "http://127.0.0.1/sxwTaxCaculation/postbird_license.php/";

就是用来发送请求而已。

其实真正的验证过程还是比较繁琐的,因为首先你要把机器码写进去(注意:真正的机器码是在注册时候才写入的,而验证成功才写入yes码)。

这个过程没什么技术上的难度,可以在cs代码中看。

server上用户授权码和验证码(我加了一个mac地址的上线数量,最多五台):

c5

c6

四、des加密解密

机器码和yes码的验证用到了加密解密,开源的文件主要用的是des。

des加密代码:


// DES加密
private string  Encrypt(string str)
{
    try
    {
        DESCryptoServiceProvider descsp = new DESCryptoServiceProvider();   //实例化加/解密类对象   

        byte[] tmpKey = Encoding.Unicode.GetBytes(this.key.Substring(0,4)); //定义字节数组,用来存储密钥   key必须是八位的  

        byte[] data = Encoding.Unicode.GetBytes(str);//定义字节数组,用来存储要加密的字符串  

        MemoryStream MStream = new MemoryStream(); //实例化内存流对象      

        //使用内存流实例化加密流对象   
        CryptoStream CStream = new CryptoStream(MStream, descsp.CreateEncryptor(tmpKey, tmpKey), CryptoStreamMode.Write);

        CStream.Write(data, 0, data.Length);  //向加密流中写入数据      

        CStream.FlushFinalBlock();              //释放加密流      

        return Convert.ToBase64String(MStream.ToArray());//返回加密后的字符串  
    }catch
    {
        return str;
    }
            
}

des解密代码:


// DES解密
private string Decrypt(string str)
{
try
{
    DESCryptoServiceProvider descsp = new DESCryptoServiceProvider();   //实例化加/解密类对象    

    byte[] tmpKey = Encoding.Unicode.GetBytes(this.key.Substring(0,4)); //定义字节数组,用来存储密钥       key必须是八位的  

    byte[] data = Convert.FromBase64String(str);//定义字节数组,用来存储要解密的字符串  

    MemoryStream MStream = new MemoryStream(); //实例化内存流对象      

    //使用内存流实例化解密流对象       
    CryptoStream CStream = new CryptoStream(MStream, descsp.CreateDecryptor(tmpKey, tmpKey), CryptoStreamMode.Write);

    CStream.Write(data, 0, data.Length);      //向解密流中写入数据     

    CStream.FlushFinalBlock();               //释放解密流      

    return Encoding.Unicode.GetString(MStream.ToArray());       //返回解密后的字符串  
}
catch
{
    return str;
}
}

五、说一下完整的流程

1、打开软件,主要的winform的enable设置为false,判断license是否正确

2、license文件不存在则直接注册,存在进行机器码的匹配,不匹配进行注册, 如果匹配再匹配yes码,成功则进行主winform,否则注册

3、如果需要注册,添加用户,生成验证码和授权码。

4、用户发送请求的时候,首先生成当前机器码,记录到license文件,如果验证成功,根据机器码生成yes码,再次记录,记录完成。

5、开放主winform

c7

六、一些改进

开源的文件里面,并不是特别的完善,因此我在实际使用过程中增加了一些内容,但是我现在还在用,因此无法写出来。

简单说一下:

1、加密方式改变,不仅仅单纯使用md5和des,用了rsa。

2、实际使用中我在license中记录的不仅仅是两行,还记录了别的一些标记,方式文件的篡改。

3、最后我没有用txt文档处理,使用了二进制文件的加密使用,可以考虑一下这样子实现,甚至可以打成dll动态库。

4、授权验证的服务器也就是server可以做成用户自己付款然后自动生成就很好了。

 

七、代码

目前开源的cs托管在git@osc和github上:

git@osc :   https://git.oschina.net/postbird/Postbird_License

github :   https://github.com/postbird/Postbird_License