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.\n";
       abort();
   }
   cout << "DES Encryption/decryption succeeded.\n";

   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:\t" << "Hello World!" << endl << endl;
   strcpy( message, "Hello World!" );
   string encryptedText = RSAEncryptString( pubKey, seed, message );  // RSA 公匙加密
   cout<<"Encrypted Text:\t"<< 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: \n" << strMessage << endl;
       cout << "after modify is: \n" << 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   \n", mac_empty);
	system("pause");
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM