1. 程式人生 > >C++使用Openssl進行RSA簽名(sha1)--完整版

C++使用Openssl進行RSA簽名(sha1)--完整版

研究了一天,網上的程式碼寫著是簽名,實際上是加密,最開始把我弄得迷糊了,後來慢慢理清楚了,就把程式碼記下來,所有的說明都在程式碼註釋裡面,已實際應用於HTTP請求中,從讀取私鑰檔案、sha1加密、rsa簽名、base64、urlencode轉換、CURL進行HTTP請求完整流程。

先將OPENSSL庫編譯好,並引入標頭檔案:

#include "openssl/sha.h"
#include "openssl/rsa.h"
#include "openssl/rand.h"
#include "openssl/objects.h"
#include "openssl/pem.h"



實現程式碼:


char Dec2HexChar(short int n)
{
	if (0 <= n && n <= 9) {
		return char(short('0') + n);
	}
	else if (10 <= n && n <= 15) {
		return char(short('A') + n - 10);
	}
	else {
		return char(0);
	}
}

short int HexChar2Dec(char c)
{
	if ('0' <= c && c <= '9') {
		return short(c - '0');
	}
	else if ('a' <= c && c <= 'f') {
		return (short(c - 'a') + 10);
	}
	else if ('A' <= c && c <= 'F') {
		return (short(c - 'A') + 10);
	}
	else {
		return -1;
	}
}

std::string EncodeURL(const std::string &URL)
{
	std::string strResult = "";
	for (unsigned int i = 0; i < URL.size(); i++)
	{
		char c = URL[i];
		if (
			('0' <= c && c <= '9') ||
			('a' <= c && c <= 'z') ||
			('A' <= c && c <= 'Z') ||
			c == '.'
			) {
			strResult += c;
		}
		else
		{
			int j = (short int)c;
			if (j < 0)
			{
				j += 256;
			}
			int i1, i0;
			i1 = j / 16;
			i0 = j - i1 * 16;
			strResult += '%';
			strResult += Dec2HexChar(i1);
			strResult += Dec2HexChar(i0);
		}
	}

	return strResult;
}

std::string DecodeURL(const std::string &URL)
{
	std::string result = "";
	for (unsigned int i = 0; i < URL.size(); i++)
	{
		char c = URL[i];
		if (c != '%')
		{
			result += c;
		}
		else
		{
			char c1 = URL[++i];
			char c0 = URL[++i];
			int num = 0;
			num += HexChar2Dec(c1) * 16 + HexChar2Dec(c0);
			result += char(num);
		}
	}

	return result;
}

//--生成GUID  
const char* newGUID()
{
	static char buf[64] = { 0 };
	GUID guid;
	if (S_OK == ::CoCreateGuid(&guid))
	{
		_snprintf(buf, sizeof(buf)
			, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X"
			, guid.Data1
			, guid.Data2
			, guid.Data3
			, guid.Data4[0], guid.Data4[1]
			, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5]
			, guid.Data4[6], guid.Data4[7]
			);
	}
	return (const char*)buf;
}  

//將二進位制流轉換成base64編碼
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char * base64_encode(const unsigned char * bindata, char * base64, int binlength)
{
	int i, j;
	unsigned char current;

	for (i = 0, j = 0; i < binlength; i += 3)
	{
		current = (bindata[i] >> 2);
		current &= (unsigned char)0x3F;
		base64[j++] = base64char[(int)current];

		current = ((unsigned char)(bindata[i] << 4)) & ((unsigned char)0x30);
		if (i + 1 >= binlength)
		{
			base64[j++] = base64char[(int)current];
			base64[j++] = '=';
			base64[j++] = '=';
			break;
		}
		current |= ((unsigned char)(bindata[i + 1] >> 4)) & ((unsigned char)0x0F);
		base64[j++] = base64char[(int)current];

		current = ((unsigned char)(bindata[i + 1] << 2)) & ((unsigned char)0x3C);
		if (i + 2 >= binlength)
		{
			base64[j++] = base64char[(int)current];
			base64[j++] = '=';
			break;
		}
		current |= ((unsigned char)(bindata[i + 2] >> 6)) & ((unsigned char)0x03);
		base64[j++] = base64char[(int)current];

		current = ((unsigned char)bindata[i + 2]) & ((unsigned char)0x3F);
		base64[j++] = base64char[(int)current];
	}
	base64[j] = '\0';
	return base64;
}

//簽名
bool EncryptWithPrivateKey(const unsigned char * pData, char *pSignOut)
{
	RSA * rsa_pri_key = NULL;
	FILE * pFile = NULL;

	//獲取路徑
	TCHAR szWorkDir[MAX_PATH] = TEXT("");
	CWHService::GetWorkDirectory(szWorkDir, CountArray(szWorkDir));

	//構造路徑
	TCHAR szrsa_private_keyFile[MAX_PATH] = TEXT("");
	_sntprintf(szrsa_private_keyFile, CountArray(szrsa_private_keyFile), 
		TEXT("%s\\IniConfig\\rsa_private_key.pem"), szWorkDir);

	if (NULL != (pFile = _wfopen(szrsa_private_keyFile, TEXT("r"))))
	{
		//讀取私鑰檔案
		rsa_pri_key = PEM_read_RSAPrivateKey(pFile, NULL, NULL, NULL);
		fclose(pFile);
		if (rsa_pri_key == NULL)
		{
			LOGException("讀取私鑰檔案內容失敗!");
			return false;
		}

		//先將源串SHA1演算法加密, 後面+1是給結束符留的位置
		unsigned char szSha1Data[SHA_DIGEST_LENGTH+1] = { 0 };
		ZeroMemory(szSha1Data, sizeof(szSha1Data)); 
		SHA_CTX c; 
		if (!SHA1_Init(&c))
		{
			LOGException("初始化sha1演算法失敗!");
			return false;
		}
		SHA1_Update(&c, pData, strlen((char*)pData));
		SHA1_Final(szSha1Data, &c);
		OPENSSL_cleanse(&c, sizeof(c));
		 
		//RSA簽名
		unsigned char szTBSign[512] = { 0 };
		ZeroMemory(szTBSign, sizeof(szTBSign));
		unsigned int nLen = 0;
		int r = RSA_sign(NID_sha1, szSha1Data, SHA_DIGEST_LENGTH, szTBSign, &nLen, rsa_pri_key);
		RSA_free(rsa_pri_key); 

		if (1 == r)
		{
			//簽名成功,轉換成base64編碼
			base64_encode(szTBSign, pSignOut, nLen);
			return true;
		}
		return false; 
	}
	else
	{
		LOGException("讀取私鑰檔案失敗!");
	}
	return false;
}


借用前輩CURL的HTTP請求程式碼:

.h標頭檔案

	/**
	* @brief HTTP POST請求
	* @param strUrl 輸入引數,請求的Url地址,如:http://www.baidu.com
	* @param strPost 輸入引數,使用如下格式para1=val1¶2=val2&…
	* @param strResponse 輸出引數,返回的內容
	* @return 返回是否Post成功
	*/
	int Post(const std::string & strUrl, const std::string & strPost, std::string & strResponse);

	/**
	* @brief HTTP GET請求
	* @param strUrl 輸入引數,請求的Url地址,如:http://www.baidu.com
	* @param strResponse 輸出引數,返回的內容
	* @return 返回是否Post成功
	*/
	int Get(const std::string & strUrl, std::string & strResponse);

	/**
	* @brief HTTPS POST請求,無證書版本
	* @param strUrl 輸入引數,請求的Url地址,如:https://www.alipay.com
	* @param strPost 輸入引數,使用如下格式para1=val1¶2=val2&…
	* @param strResponse 輸出引數,返回的內容
	* @param pCaPath 輸入引數,為CA證書的路徑.如果輸入為NULL,則不驗證伺服器端證書的有效性.
	* @return 返回是否Post成功
	*/
	int Posts(const std::string & strUrl, const std::string & strPost, std::string & strResponse, const char * pCaPath = NULL);

	/**
	* @brief HTTPS GET請求,無證書版本
	* @param strUrl 輸入引數,請求的Url地址,如:https://www.alipay.com
	* @param strResponse 輸出引數,返回的內容
	* @param pCaPath 輸入引數,為CA證書的路徑.如果輸入為NULL,則不驗證伺服器端證書的有效性.
	* @return 返回是否Post成功
	*/
	int Gets(const std::string & strUrl, std::string & strResponse, const char * pCaPath = NULL);

.cpp檔案
int CAsyncEngineHttpSink::Post(const std::string & strUrl, const std::string & strPost, std::string & strResponse)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if (NULL == curl)
	{
		return CURLE_FAILED_INIT;
	} 
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_POST, 1);
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	return res;
}

int CAsyncEngineHttpSink::Get(const std::string & strUrl, std::string & strResponse)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if (NULL == curl)
	{
		return CURLE_FAILED_INIT;
	}
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	/**
	* 當多個執行緒都使用超時處理的時候,同時主執行緒中有sleep或是wait等操作。
	* 如果不設定這個選項,libcurl將會發訊號打斷這個wait從而導致程式退出。
	*/
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	return res;
}

int CAsyncEngineHttpSink::Posts(const std::string & strUrl, const std::string & strPost, std::string & strResponse, const char * pCaPath)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if (NULL == curl)
	{
		return CURLE_FAILED_INIT;
	} 
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_POST, 1);
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	if (NULL == pCaPath)
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		//預設情況就是PEM,所以無需設定,另外支援DER 
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
	}
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	return res;
}

int CAsyncEngineHttpSink::Gets(const std::string & strUrl, std::string & strResponse, const char * pCaPath)
{
	CURLcode res;
	CURL* curl = curl_easy_init();
	if (NULL == curl)
	{
		return CURLE_FAILED_INIT;
	} 
	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	if (NULL == pCaPath)
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
	}
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	return res;
}