License系统设计(一)

https://blog.csdn.net/Sagittarius_Warrior/article/details/53501282

 本系列文章主要介绍我近期设计的一个软件License系统。

一、软件需求

    假设M公司要发布一款软硬件一体的产品,名为“OfficeDevice”。这个OfficeDevice的上层是一个PC,下层带一些嵌入式设备,而PC上运行一个叫“Office Kits”的软件,这个软件包含三个功能模块:Word、PPT和Excel。

    现在要设计一个License系统,需满足以下两点需求:

1,机器绑定

    Office Kits在启动后,需要验证License,这个License的某个信息字段要与机器的序列号匹配,如果不匹配,则判License无效。机器的序列号可以是主板编号,当然,OfficeDevice的序列号,来自与它的某个嵌入式设备。

2,模块权限

    M公司需要将Word、PPT和Excel三个模块分开卖,License的某个信息字段要包含模块权限的信息,Office Kits在启动后,需要验证这个信息,验证后只激活符合权限的模块。

3,试用期

    M公司会发布试用License,试用期一到,License失效。Office Kits的Word、PPT和Excel三个模块不能工作。


二、产品设计

    从上面的软件需求可知,这个License系统可以分成两个部分,一个是License文件生成软件,一个是解密模块。

1,License Generator

    License Generator提供一GUI,根据M公司的配置,生成一个License文件。界面设计如下:

 

    如上图,左边是配置项,右边第一组控件,主要是预览待加密的字符串,第二组控件执行加密过程,并生成加密文件,文件名是“序列号+后缀”,第三组控件,主要是对比,看解密后的字符串是否与明文一致。


注:关于序列号,有很多产品直接选用主机的硬盘序列号。这里我通过google搜到了一个不错的获取硬盘序列号的源码,分享如下:

http://blog.163.com/jinfd@126/blog/static/6233227720133218314327/

    这个需要在管理员模式下才能读硬盘序列号。

http://www.cnblogs.com/xiongpq/p/3953694.html

    这个在非管理员模式下也可以读硬盘序列号。


2,解密模块

    解密模块我们以导出库的形式,嵌入到Office Kits软件中,Office Kits软件启动后,会加License文件读入,并解密成字符串,然后校验相应的信息字段。最后,根据校验结果,关闭或激活相应的功能。

    解密模块最好以“.lib”静态库的方式嵌入,在Office Kits软件目录下,并不能直接看到解密模块文件,这样更加难以破解。不过,我后面设计的还是采用“.DLL”的方式嵌入的,主要是为了复用。但是,我还是采用了一些技巧,防止API拦截的破解方式。


三、加解密模块

    后面开始从代码实现的角度进行讲解。首先需要介绍的就是加解密功能,采用的是Crypto++开源加密算法库,并将它分装成一个DLL。

    关于Crypto++库的用法,可以参考我的上一篇博文:

        http://blog.csdn.net/sagittarius_warrior/article/details/53408803

    关于封装,我主要参考了这篇博文:https://my.oschina.net/u/566591/blog/168421

封装过程:

1,VS新建win32 DLL工程:AES_DLL.sln

2,添加导出函数和接口类

    #ifndef AES_DLL_H
    #define AES_DLL_H
     
    #ifdef AES_IMPORT
    #define AES_API extern "C" __declspec(dllexport)
    #else
    #define AES_API extern "C" __declspec(dllimport)
    #endif
     
    #include <string>
    using std::string;
     
    class IEncryptor
    {
    public:
        virtual void SetKey(unsigned char * key, unsigned char * iv, int length) = 0;
     
        // encrypt the plainText and generate a file, the plainText should be a string with '\0' end
        virtual bool EncryptString2File(const char * plainText, const char * outFilename) = 0;
     
        // decyrpt the file and output the recover, you may calculate the size of the file and allocate the space for recover
        virtual bool DecryptFile2String(const char * decFilename, char * recoverText) = 0;
    };
     
    AES_API IEncryptor* CreateEncryptor();
    AES_API void ReleaseEncryptor(IEncryptor* pEncryptor);
     
    typedef IEncryptor* (*pfnCreateEncryptor)();
    typedef void (*pfnReleaseEncryptor)(IEncryptor*);
     
    #endif    // AES_DLL_H


2,导出函数的实现

    #include "stdafx.h"
    #include "easyaes.h"
    #include "encryptor.h"
    #define AES_IMPORT
     
    IEncryptor* CreateEncryptor()
    {
        return static_cast<IEncryptor *>(new EasyAES());
    }
     
    void ReleaseEncryptor(IEncryptor* pEncryptor)
    {
        EasyAES * pEasyAES = dynamic_cast<EasyAES *>(pEncryptor);
        if (pEasyAES)
        {
            delete pEasyAES;
            pEasyAES = NULL;
        }
    }


3,实现类

    class EasyAES : public IEncryptor
    {
    public:    // for export interface class
        void SetKey(byte * key, byte * iv, int length);
     
        // encrypt the plainText and generate a file, the plainText should be a string with '\0' end
        bool EncryptString2File(const char * plainText, const char * outFilename);
     
        // decyrpt the file and output the recover, you may calculate the size of the file and allocate the space for recover
        bool DecryptFile2String(const char * decFilename, char * recoverText);


   鉴于篇幅,这里只列出部分


4,具体实现

     bool EasyAES::EncryptString2File(const char * plainText, const char * outFilename)
     {
         // check if the string 'plainText' is empty.
         if (0 == strlen(plainText))
         {
             cout << "The input string is empty ! " << endl;
             return false;
         }
     
         // string is not empty, the encrypt the file
         CBC_Mode<AES>::Encryption aesEncryptor(key, key_length, iv);
     
         StringSource(plainText, true,
             new StreamTransformationFilter(aesEncryptor,
                 new FileSink(outFilename)));
     
         return true;
     }
     bool EasyAES::DecryptFile2String(const char * decFilename, char * recoverText)
     {
         // check if the file 'decFilename' exists!
         if (_access(decFilename, 0) == -1)
         {
             cout << "The file " << decFilename << " is not exist! " << endl;
             return false;
         }
     
         // exist , then decrypt the file
         string buffer;
         CBC_Mode<AES>::Decryption aesDecryptor(key, key_length, iv);
         FileSource(decFilename, true,
             new StreamTransformationFilter(aesDecryptor,
                 new StringSink(buffer)));
         memcpy(recoverText, buffer.c_str(), buffer.size());
         return true;
     }


    具体实现部分,都是crypto++库的内容,有有兴趣的直接研究它就行。


---------------------
作者:Sagittarius_Warrior
来源:CSDN
原文:https://blog.csdn.net/Sagittarius_Warrior/article/details/53501282
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值