C++使用Openssl進行RSA簽名(sha1)--完整版
阿新 • • 發佈:2019-01-31
研究了一天,網上的程式碼寫著是簽名,實際上是加密,最開始把我弄得迷糊了,後來慢慢理清楚了,就把程式碼記下來,所有的說明都在程式碼註釋裡面,已實際應用於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; }