zoukankan      html  css  js  c++  java
  • Crypto++ 开源加密使用笔记(1)(DES、AES、RSA、SHA-256)

    参考 https://www.cnblogs.com/liaocheng/p/4264719.html

    1.写在前面

    软件开发如何给开发的软件增加加密狗限制,避免软件被恶意拷贝,是常常想到的问题。
    经过思考,实现方式为没个软件开启之后只有一次授权的资格,授权码绑定的电脑网卡的物理地址等。
    当软件被拷贝到其他地,则软件已经是授权状态,会检测mac地址等时候一致。而这些数据需要通过加密,避免被破译。

    2.常见加密方式了解

    总结了一点预备知识:

    关于几个算法的介绍,网上各大百科都有,笔者不再详细Ctrl+C/V了。不过在写代码之前,即使复制修改人家代码之前,也有必要了解一下几个算法(除了名称之外)的使用流程(不是算法具体的实现,汗!)。

    2.1对称加密:(AES、DES)

    相对于与非对称加密而言,加密、解密用的密匙相同。就像日常生活中的钥匙,开门和锁门都是同一把

    详见:http://baike.baidu.com/view/119320.htm

    2.2非对称加密:(RSA)

    相对于上述的对称加密而言,加密、解密用的密匙不同,有公匙和私匙之分。

    详见:http://baike.baidu.com/view/554866.htm

    2.3 散列算法:(SHA系列,我们熟悉的MD5等)

    用途:验证信息有没有被修改。

    原理:对长度大的信息进行提炼(通过一个Hash函数),提炼过后的信息长度小很多,得到的是一个固定长度的值(Hash值)。对于两个信息量很大的文件通过比较这两个值,就知道这两个文件是否完全一致(另外一个文件有没有被修改)。从而避免了把两个文件中的信息进行逐字逐句的比对,减少时间开销。

    形象地描述:鬼泣3里面维吉尔跟但丁除了发型之外都很像。怎么区分两个双生子?比较他们的DNA就好比是比较信息量很大的文件,然而直接看发型就好比是看Hash值。一眼就看出来了。

    注:以上是笔者对几个概念的,非常不严格的,非常主观的,概括的描述,想要详细了解,可以:

    http://wenku.baidu.com/view/4fb8e0791711cc7931b716aa.html

    几个算法的介绍,选择,比较。

    2.4基于cryto++ 算法的比较

    2.4.1 对称加密算法 DES

    #include <iostream>
    #include <des.h>
    
    #pragma comment( lib, "cryptlib.lib" )
    
    using namespace std;
    using namespace CryptoPP;
    
    int main( void )
    {
       //主要是打印一些基本信息,方便调试:
       cout << "DES Parameters: " << endl;
       cout << "Algorithm name : " << DES::StaticAlgorithmName() << endl;
    
       unsigned char key[ DES::DEFAULT_KEYLENGTH ];
       unsigned char input[ DES::BLOCKSIZE ] = "12345";
       unsigned char output[ DES::BLOCKSIZE ];
       unsigned char txt[ DES::BLOCKSIZE ];
    
       cout << "input is: " << input << endl;
    
       //可以理解成首先构造一个加密器
       DESEncryption encryption_DES;
    
       //回忆一下之前的背景,对称加密算法需要一个密匙。加密和解密都会用到。
       //因此,设置密匙。
       encryption_DES.SetKey( key, DES::KEYLENGTH );
       //进行加密
       encryption_DES.ProcessBlock( input, output );
    
       //显示结果
       //for和for之后的cout可有可无,主要为了运行的时候看加密结果
       //把字符串的长度写成一个常量其实并不被推荐。
       //不过笔者事先知道字符串长,为了方便调试,就直接写下。
       //这里主要是把output也就是加密后的内容,以十六进制的整数形式输出。
       for( int i = 0; i < 5; i++ )
       {
           cout << hex << (int)output[ i ] << ends;
       }
       cout << endl;
    
       //构造一个加密器
       DESDecryption decryption_DES;
    
       //由于对称加密算法的加密和解密都是同一个密匙,
       //因此解密的时候设置的密匙也是刚才在加密时设置好的key
       decryption_DES.SetKey( key, DES::KEYLENGTH );
       //进行解密,把结果写到txt中
       //decryption_DES.ProcessAndXorBlock( output, xorBlock, txt );
       decryption_DES.ProcessBlock( output, txt );
    
       //以上,加密,解密还原过程已经结束了。以下是为了验证:
       //加密前的明文和解密后的译文是否相等。
       if ( memcmp( input, txt, 5 ) != 0 )
       {
           cerr << "DES Encryption/decryption failed.
    ";
           abort();
       }
       cout << "DES Encryption/decryption succeeded.
    ";
    
       return 0;
    }
    

    回想一下以上代码的编写过程,就可以发现,进行DES加密,流程大概是:
    数据准备;
    构造加密器;
    设置加密密匙;
    加密数据;
    显示(非必要);
    设置解密密匙(跟加密密匙是同一个key);
    解密数据;
    验证与显示(非必要);
    由此可见,主要函数的调用流程就是这样。但是文档没有详细讲,笔者当时打开下载回来的源文件时,就傻了眼。
    猜想:
    AES和以后的算法,是不是都是按照这些基本的套路呢?

    2.4.2 对称加密算法-AES

    在实际运用的时候,从代码上看,AES跟DES非常相像。但是值得注意一点的是,AES取代了DES成为21世纪的加密标准。是因为以其密匙长度和高安全性获得了先天优势。虽然界面上看上去没多大区别,但是破解难度远远大于DES。详细情况,在之前的URL有提及过

    #include <iostream>
     #include <aes.h>
     
     #pragma comment( lib, "cryptlib.lib" )
     using namespace std; 
     using namespace CryptoPP;
    int main()
    {
    //AES中使用的固定参数是以类AES中定义的enum数据类型出现的,而不是成员函数或变量
    //因此需要用::符号来索引
    cout << "AES Parameters: " << endl;
    cout << "Algorithm name : " << AES::StaticAlgorithmName() << endl; 
    
    //Crypto++库中一般用字节数来表示长度,而不是常用的字节数
    cout << "Block size : " << AES::BLOCKSIZE * 8 << endl;
    cout << "Min key length : " << AES::MIN_KEYLENGTH * 8 << endl;
    cout << "Max key length : " << AES::MAX_KEYLENGTH * 8 << endl;
    
    //AES中只包含一些固定的数据,而加密解密的功能由AESEncryption和AESDecryption来完成
    //加密过程
    AESEncryption aesEncryptor; //加密器 
    
    unsigned char aesKey[AES::DEFAULT_KEYLENGTH]; //密钥
    unsigned char inBlock[AES::BLOCKSIZE] = "123456789"; //要加密的数据块
    unsigned char outBlock[AES::BLOCKSIZE]; //加密后的密文块
    unsigned char xorBlock[AES::BLOCKSIZE]; //必须设定为全零
    
    memset( xorBlock, 0, AES::BLOCKSIZE ); //置零
    
    aesEncryptor.SetKey( aesKey, AES::DEFAULT_KEYLENGTH ); //设定加密密钥
    aesEncryptor.ProcessAndXorBlock( inBlock, xorBlock, outBlock ); //加密
    
    //以16进制显示加密后的数据
    for( int i=0; i<16; i++ ) {
    cout << hex << (int)outBlock[i] << " ";
    }
    cout << endl;
    
    //解密
    AESDecryption aesDecryptor;
    unsigned char plainText[AES::BLOCKSIZE];
    
    aesDecryptor.SetKey( aesKey, AES::DEFAULT_KEYLENGTH );
    //细心的朋友注意到这里的函数不是之前在DES中出现过的:ProcessBlock,
    //而是多了一个Xor。其实,ProcessAndXorBlock也有DES版本。用法跟AES版本差不多。
    //笔者分别在两份代码中列出这两个函数,有兴趣的朋友可以自己研究一下有何差异。
    aesDecryptor.ProcessAndXorBlock( outBlock, xorBlock, plainText );
    
    
    for( int i=0; i<16; i++ ) 
    { 
    cout << plainText[i]; 
    }
    cout << endl;
    
    return 0;
    
    }
    

    2.4.3 非对称加密算法

    其实,笔者在一开始并没有接到“了解RSA”的要求。不过由于笔者很粗心,在看AES的时候只记得A和S两个字母,Google的时候就误打误撞Google了一个RSA。其实RSA方面的资料还是挺多的,因此它事实上是笔者第一个编译运行成功的Crypto++库中算法的应用实例。

       由以下代码可以看出,其实RSA也离不开:数据准备、设置密匙(注意,有公匙和私匙)、加密解密这样的套路。至于如何产生密匙,有兴趣的朋友可以到Crypto++的主页上下载源文件研究。作为入门和了解阶段,笔者觉得:只需要用起来即可。
    
    //version at Crypto++ 5.60
    #include "randpool.h"
    #include "rsa.h"
    #include "hex.h"
    #include "files.h"
    #include <iostream>
    
    using namespace std;
    using namespace CryptoPP;
    
    #pragma comment(lib, "cryptlib.lib")
    
    
    //------------------------
    
    // 函数声明
    
    //------------------------
    
    void GenerateRSAKey( unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed  );
    string RSAEncryptString( const char *pubFilename, const char *seed, const char *message );
    string RSADecryptString( const char *privFilename, const char *ciphertext );
    RandomPool & GlobalRNG();
    
    //------------------------
    // 主程序
    //------------------------
    
    void main( void )
    
    {
       char priKey[ 128 ] = { 0 };
       char pubKey[ 128 ] = { 0 };
       char seed[ 1024 ]  = { 0 };
    
       // 生成 RSA 密钥对
       strcpy( priKey, "pri" );  // 生成的私钥文件
       strcpy( pubKey, "pub" );  // 生成的公钥文件
       strcpy( seed, "seed" );
       GenerateRSAKey( 1024, priKey, pubKey, seed );
    
       // RSA 加解密
       char message[ 1024 ] = { 0 };
       cout<< "Origin Text:	" << "Hello World!" << endl << endl;
       strcpy( message, "Hello World!" );
       string encryptedText = RSAEncryptString( pubKey, seed, message );  // RSA 公匙加密
       cout<<"Encrypted Text:	"<< encryptedText << endl << endl;
       string decryptedText = RSADecryptString( priKey, encryptedText.c_str() );  // RSA 私匙解密
    }
    
    
    
    //------------------------
    
    // 生成RSA密钥对
    
    //------------------------
    
    void GenerateRSAKey(unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed)
    {
       RandomPool randPool;
       randPool.Put((byte *)seed, strlen(seed));
    
       RSAES_OAEP_SHA_Decryptor priv(randPool, keyLength);
       HexEncoder privFile(new FileSink(privFilename));
    
       priv.DEREncode(privFile);
       privFile.MessageEnd();
    
       RSAES_OAEP_SHA_Encryptor pub(priv);
       HexEncoder pubFile(new FileSink(pubFilename));
       pub.DEREncode(pubFile);
    
       pubFile.MessageEnd();
    
       return ;
    }
    
    
    
    //------------------------
    
    // RSA加密
    
    //------------------------
    
    string RSAEncryptString( const char *pubFilename, const char *seed, const char *message )
    {
       FileSource pubFile( pubFilename, true, new HexDecoder );
       RSAES_OAEP_SHA_Encryptor pub( pubFile );
    
       RandomPool randPool;
       randPool.Put( (byte *)seed, strlen(seed) );
    
       string result;
       StringSource( message, true, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(result))) );
    
       return result;
    }
    
    
    
    //------------------------
    // RSA解密
    //------------------------
    
    string RSADecryptString( const char *privFilename, const char *ciphertext )
    {
       FileSource privFile( privFilename, true, new HexDecoder );
       RSAES_OAEP_SHA_Decryptor priv(privFile);
    
       string result;
       StringSource( ciphertext, true, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(result))) );
    
       return result;
    }
    
    
    
    //------------------------
    
    // 定义全局的随机数池
    
    //------------------------
    
    RandomPool & GlobalRNG()
    {
       static RandomPool randomPool;
       return randomPool;
    }
    

    2.4.4 散列算法

    SHA-256主要是用来求一大段信息的Hash值,跟之前三个用于加密、解密的算法有所不同。用到SHA的场合,多半是为了校验文件。
    请注意,笔者在实现的时候,稍微修改了一下两个子函数的实现,以满足笔者的需求。因此会与上述URL中的代码有差异。

    //http://hi.baidu.com/magic475/blog/item/19b37a8c1fa15a14b21bbaeb.html
    #include <iostream>
    #include <string.h>
    
    #include "sha.h"
    #include "secblock.h"
    #include "modes.h"
    #include "hex.h"
    
    #pragma comment( lib, "cryptlib.lib")
    
    using namespace std;
    using namespace CryptoPP;
    
    void CalculateDigest(string &Digest, const string &Message);
    bool VerifyDigest(const string &Digest, const string &Message);
    
    int main( void )
    {
       //main函数中注释掉的,关于strMessage2的代码,其实是笔者模拟了一下
       //通过求Hash值来对“大”量数据进行校验的这个功能的运用。
       //注释之后并不影响这段代码表达的思想和流程。
       string strMessage( "Hello world" );
       string strDigest;
       //string strMessage2( "hello world" ); //只是第一个字母不同
       //string strDigest2;
    
       CalculateDigest( strDigest, strMessage );  //计算Hash值并打印一些debug信息
       cout << "the size of Digest is: " << strDigest.size() << endl;
       cout << "Digest is: " << strDigest << endl;
    
       //CalculateDigest( strDigest2, strMessage2 );
       //why put this function here will affect the Verify function?
       //作者在写代码的过程中遇到的上述问题。
       //如果把这行代码的注释取消,那么之后的运行结果就不是预料中的一样:
       //即使strDigest也无法对应strMessage,笔者不知道为什么,希望高手指出,谢谢!
    
       bool bIsSuccess = false;
       bIsSuccess = VerifyDigest( strDigest, strMessage );
       //通过校验,看看strDigest是否对应原来的message
       if( bIsSuccess )
       {
           cout << "sussessive verify" << endl;
           cout << "origin string is: " << strMessage << endl << endl;
       }
       else
       {
           cout << "fail!" << endl;
       }
    
       //通过strDigest2与strMessage进行校验,要是相等,
       //就证明strDigest2是对应的strMessage2跟strMessage1相等。
       //否则,像这个程序中的例子一样,两个message是不相等的
       /*CalculateDigest( strDigest2, strMessage2 );
       bIsSuccess = VerifyDigest( strDigest2, strMessage );
       if( !bIsSuccess )
       {
           cout << "success! the tiny modification is discovered~" << endl;
           cout << "the origin message is: 
    " << strMessage << endl;
           cout << "after modify is: 
    " << strMessage2 << endl;
       }*/
       return 0;
    }
    
    
    //基于某些原因,以下两个子函数的实现跟原来参考代码中的实现有所区别,
    //详细原因,笔者在CalculateDigest函数的注释中写明
    void CalculateDigest(string &Digest, const string &Message)
    {
       SHA256 sha256;
       int DigestSize = sha256.DigestSize();
       char* byDigest;
       char* strDigest;
    
       byDigest = new char[ DigestSize ];
       strDigest = new char[ DigestSize * 2 + 1 ];
    
       sha256.CalculateDigest((byte*)byDigest, (const byte *)Message.c_str(), Message.size());
       memset(strDigest, 0, sizeof(strDigest));
       //uCharToHex(strDigest, byDigest, DigestSize);
       //参考的代码中有以上这么一行,但是貌似不是什么库函数。
       //原作者大概是想把Hash值转换成16进制数保存到一个string buffer中,
       //然后在主程序中输出,方便debug的时候对照查看。
       //但是这并不影响计算Hash值的行为。
       //因此笔者注释掉了这行代码,并且修改了一下这个函数和后面的VerifyDigest函数,
       //略去原作者这部分的意图,继续我们的程序执行。
    
       Digest = byDigest;
    
       delete []byDigest;
       byDigest = NULL;
       delete []strDigest;
       strDigest = NULL;
    
       return;
    }
    
    bool VerifyDigest(const string &Digest, const string &Message)
    {
       bool Result;
       SHA256 sha256;
       char* byDigest;
    
       byDigest = new char[ sha256.DigestSize() ];
       strcpy( byDigest, Digest.c_str() );
    
       //HexTouChar(byDigest, Digest.c_str(), Digest.size());
       //为何注释掉,请参看CalculateDigest函数的注释
       Result = sha256.VerifyDigest( (byte*)byDigest, (const byte *)Message.c_str(), Message.size() );
    
       delete []byDigest;
       byDigest = NULL;
       return Result;
    }
    

    3.获取设备mac物理地址程序

    如下,选择AES对称加密对于写入数据进行加密写入,以及解密读入即可

    
    /*
    ============================================================================================================ =
    时间:2020 - 12 - 29
    作者:Allen.ye
    作用:get_mac_address.cpp 获取当前无线网卡对应的mac 地址
    文件:get_mac_address.cpp
    软件版本:VS2017_X64 release
    ============================================================================================================ =
    */
    
    
    #ifndef _CRT_SECURE_NO_WARNINGS
    #define _CRT_SECURE_NO_WARNINGS
    
    #endif // !_CRT_SECURE_NO_WARNINGS
    
    
    //#include "stdafx.h"
    #include <windows.h>
    #include <wincon.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    #include <Nb30.h>
    
    #include <fstream>
    #include <iostream>
    #include <string>
    #pragma comment(lib,"netapi32.lib")
    
    
    
    int GetMac(char * mac)
    {
    	NCB ncb;
    	typedef struct _ASTAT_
    	{
    		ADAPTER_STATUS   adapt;
    		NAME_BUFFER   NameBuff[30];
    	}ASTAT, *PASTAT;
    
    	ASTAT Adapter;
    
    	typedef struct _LANA_ENUM
    	{
    		UCHAR   length;
    		UCHAR   lana[MAX_LANA];
    	}LANA_ENUM;
    
    	LANA_ENUM lana_enum;
    	UCHAR uRetCode;
    	memset(&ncb, 0, sizeof(ncb));
    	memset(&lana_enum, 0, sizeof(lana_enum));
    	ncb.ncb_command = NCBENUM;
    	ncb.ncb_buffer = (unsigned char *)&lana_enum;
    	ncb.ncb_length = sizeof(LANA_ENUM);
    	uRetCode = Netbios(&ncb);
    
    	if (uRetCode != NRC_GOODRET)
    		return uRetCode;
    
    	for (int lana = 0; lana < lana_enum.length; lana++)
    	{
    		ncb.ncb_command = NCBRESET;
    		ncb.ncb_lana_num = lana_enum.lana[lana];
    		uRetCode = Netbios(&ncb);
    		if (uRetCode == NRC_GOODRET)
    			break;
    	}
    
    	if (uRetCode != NRC_GOODRET)
    		return uRetCode;
    
    	memset(&ncb, 0, sizeof(ncb));
    	ncb.ncb_command = NCBASTAT;
    	ncb.ncb_lana_num = lana_enum.lana[0];
    	strcpy((char*)ncb.ncb_callname, "*");
    	ncb.ncb_buffer = (unsigned char *)&Adapter;
    	ncb.ncb_length = sizeof(Adapter);
    	uRetCode = Netbios(&ncb);
    
    	if (uRetCode != NRC_GOODRET)
    		return uRetCode;
    
    	sprintf(mac, "%02X-%02X-%02X-%02X-%02X-%02X",
    		Adapter.adapt.adapter_address[0],
    		Adapter.adapt.adapter_address[1],
    		Adapter.adapt.adapter_address[2],
    		Adapter.adapt.adapter_address[3],
    		Adapter.adapt.adapter_address[4],
    		Adapter.adapt.adapter_address[5]);
    
    	return 0;
    }
    
    
    
    int main(int argc, char* argv[])
    {
    	//1.未授权的钥匙
    	int is_authorized = 0;
    	char   mac_empty[200] = "asghjkkklll";//电脑mac地
    
    	std::string key_file_name = "key.txt";
    
    
    	std::ofstream outfile(key_file_name);
    	std::ifstream infile(key_file_name);
    
    	//写入文件夹
    	std::fstream file(key_file_name, std::ios::out);//文件夹清空
    	outfile << std::to_string(is_authorized) << std::endl;
    	outfile << mac_empty << std::endl;
    
    	char   mac_empty1[200] = "000000000";//电脑mac地
    	int is_authorized1 = 0;
    	if (!infile.is_open())
    	{
    		std::cout << "文件夹已经打开!" << std::endl;
    		return -1;
    	}
    
    
    	//2.读取密钥信息
    	std::string mac_name;
    	std::string is_authorized2_str;
    	if (!infile.is_open())
    	{
    		std::cout << "文件夹已经打开!" << std::endl;
    		return -1;
    	}
    	std::getline(infile, is_authorized2_str);
    	std::getline(infile, mac_name);
    	const std::string  is_authorized11 = is_authorized2_str;
    	int  is_authorized3 = std::stoi(is_authorized11);
    
    
    
    
    
    
    	//3.判定如果软件还未授权,则授权
    	if (is_authorized3 == 0)
    	{
    		GetMac(mac_empty);
    		is_authorized = 1;
    		
    		std::fstream file(key_file_name, std::ios::out);//文件夹清空
    		outfile << is_authorized << std::endl;
    		outfile << mac_empty << std::endl;
    
    	
    		printf("授权成功!/n");
    	}
    
    
    	//4.再次读取授权状态
    	if (!infile.is_open())
    	{
    		std::cout << "文件夹已经打开!" << std::endl;
    		return -1;
    	}
    	std::getline(infile, is_authorized2_str);
    	std::getline(infile, mac_name);
    	const std::string  is_authorized12 = is_authorized2_str;
        is_authorized3 = std::stoi(is_authorized12);
    
    	if (is_authorized3 == 1)
    	{	
    		char   mac_real[200] = "1111111";//电脑mac地
    		GetMac(mac_real);
    		if (strcmp(mac_real, mac_name.c_str()) == 0)
    		{
    			std::cout << "mac 地址正确!" << std::endl;
    		}
    		else
    		{
    			std::cout << "mac 地址错误,软件未授权!" << std::endl;
    		}
    	}
    
    
    
    
    	printf("The Mac Address is : %s   
    ", mac_empty);
    	system("pause");
    	return 0;
    }
    
  • 相关阅读:
    2019-04-02 cast and covert
    2019-04-01 为什么零售业务流行起来了?
    2019-04-01 银行的零售业务和对公业务
    服务器推送更新
    webpack 大概
    webpack
    react Hooks
    react 表单受控和非受控
    eslint规则
    react系列笔记:第三记-redux-saga
  • 原文地址:https://www.cnblogs.com/codeAndlearn/p/14298231.html
Copyright © 2011-2022 走看看