C/C++中常用加密與解密算法的實(shí)現(xiàn)
計(jì)算機(jī)安全和數(shù)據(jù)隱私是現(xiàn)代應(yīng)用程序設(shè)計(jì)中至關(guān)重要的方面。為了確保數(shù)據(jù)的機(jī)密性和完整性,常常需要使用加密和解密算法。C++是一種廣泛使用的編程語言,提供了許多加密和解密算法的實(shí)現(xiàn)。本文將介紹一些在C++中常用的加密與解密算法,這其中包括Xor異或、BASE64、AES、MD5、SHA256、RSA等。
異或加解密
異或(XOR)加密算法是一種基于異或運(yùn)算的簡單且常見的加密技術(shù)。在異或加密中,每個(gè)位上的值通過與一個(gè)密鑰位進(jìn)行異或運(yùn)算來改變。這種加密算法的簡單性和高效性使得它在某些場景下很有用,尤其是對(duì)于簡單的數(shù)據(jù)加密需求。
異或運(yùn)算是一種邏輯運(yùn)算,其規(guī)則如下:
- 0 XOR 0 = 0
- 0 XOR 1 = 1
- 1 XOR 0 = 1
- 1 XOR 1 = 0
在異或加密中,將明文與密鑰進(jìn)行逐位異或運(yùn)算。如果明文位和密鑰位相同,則結(jié)果為0;如果不同,則結(jié)果為1。這個(gè)過程是可逆的,即可以通過再次異或同樣的密鑰來還原原始明文。
#include <Windows.h> #include <iostream> using namespace std; // 獲取異或整數(shù) long GetXorKey(const char* StrPasswd) { char cCode[32] = { 0 }; strcpy(cCode, StrPasswd); DWORD Xor_Key = 0; for (unsigned int x = 0; x < strlen(cCode); x++) { Xor_Key = Xor_Key * 4 + cCode[x]; } return Xor_Key; } // 異或?yàn)樽址? std::string XorEncrypt(std::string content, std::string secretKey) { for (UINT i = 0; i < content.length(); i++) { content[i] ^= secretKey[i % secretKey.length()]; } return content; } int main(int argc, char* argv[]) { // 計(jì)算加密密鑰 long ref = GetXorKey("lyshark"); std::cout << "計(jì)算異或密鑰: " << ref << std::endl; // 執(zhí)行異或加密 char szBuffer[1024] = "hello lyshark"; for (int x = 0; x < strlen(szBuffer); x++) { szBuffer[x] = szBuffer[x] ^ ref; std::cout << "加密后: " << szBuffer[x] << std::endl; } // 直接異或字符串 std::string xor_string = "hello lyshark"; std::cout << "加密后: " << XorEncrypt(xor_string, "lyshark").c_str() << std::endl; system("pause"); return 0; }
運(yùn)行后對(duì)特定字符串異或處理,如下圖;
BASE64加解密
Base64 是一種常見的編碼和解碼算法,用于將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成可打印的 ASCII 字符串,以及將這樣的字符串還原回二進(jìn)制數(shù)據(jù)。Base64 編碼是一種將二進(jìn)制數(shù)據(jù)表示為 ASCII 字符的方式,廣泛應(yīng)用于數(shù)據(jù)傳輸和存儲(chǔ)領(lǐng)域。
Base64 編碼基于一組 64 個(gè)字符的編碼表,通常包括大寫字母 A-Z、小寫字母 a-z、數(shù)字 0-9,以及兩個(gè)額外的字符 '+' 和 '/'。這樣的字符集是為了確保編碼后的數(shù)據(jù)是可打印的,并且在不同系統(tǒng)之間可以被準(zhǔn)確傳輸。
編碼的過程如下:
- 將待編碼的數(shù)據(jù)劃分為每 3 個(gè)字節(jié)一組(24 位)。
- 將每組 3 個(gè)字節(jié)拆分成 4 個(gè) 6 位的塊。
- 每個(gè) 6 位的塊對(duì)應(yīng)編碼表中的一個(gè)字符。
- 如果數(shù)據(jù)長度不是 3 的倍數(shù),使用 '=' 字符進(jìn)行填充。
解碼的過程是編碼的逆過程。
#include <iostream> #include <Windows.h> // base64 轉(zhuǎn)換表, 共64個(gè) static const char base64_alphabet[] ={ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; // 解碼時(shí)使用 static const unsigned char base64_suffix_map[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; static char cmove_bits(unsigned char src, unsigned lnum, unsigned rnum) { src <<= lnum; // src = src << lnum; src >>= rnum; // src = src >> rnum; return src; } int base64_encode(const char* indata, int inlen, char* outdata, int* outlen) { int ret = 0; if (indata == NULL || inlen == 0) { return ret = -1; } // 源字符串長度, 如果in_len不是3的倍數(shù), 那么需要補(bǔ)成3的倍數(shù) int in_len = 0; // 需要補(bǔ)齊的字符個(gè)數(shù), 這樣只有2, 1, 0(0的話不需要拼接, ) int pad_num = 0; if (inlen % 3 != 0) { pad_num = 3 - inlen % 3; } // 拼接后的長度, 實(shí)際編碼需要的長度(3的倍數(shù)) in_len = inlen + pad_num; // 編碼后的長度 int out_len = in_len * 8 / 6; // 定義指針指向傳出data的首地址 char* p = outdata; //編碼, 長度為調(diào)整后的長度, 3字節(jié)一組 for (int i = 0; i < in_len; i += 3) { // 將indata第一個(gè)字符向右移動(dòng)2bit(丟棄2bit) int value = *indata >> 2; // 對(duì)應(yīng)base64轉(zhuǎn)換表的字符 char c = base64_alphabet[value]; // 將對(duì)應(yīng)字符(編碼后字符)賦值給outdata第一字節(jié) *p = c; //處理最后一組(最后3字節(jié))的數(shù)據(jù) if (i == inlen + pad_num - 3 && pad_num != 0) { if (pad_num == 1) { *(p + 1) = base64_alphabet[(int)(cmove_bits(*indata, 6, 2) + cmove_bits(*(indata + 1), 0, 4))]; *(p + 2) = base64_alphabet[(int)cmove_bits(*(indata + 1), 4, 2)]; *(p + 3) = '='; } else if (pad_num == 2) { // 編碼后的數(shù)據(jù)要補(bǔ)兩個(gè) '=' *(p + 1) = base64_alphabet[(int)cmove_bits(*indata, 6, 2)]; *(p + 2) = '='; *(p + 3) = '='; } } else { // 處理正常的3字節(jié)的數(shù)據(jù) *(p + 1) = base64_alphabet[cmove_bits(*indata, 6, 2) + cmove_bits(*(indata + 1), 0, 4)]; *(p + 2) = base64_alphabet[cmove_bits(*(indata + 1), 4, 2) + cmove_bits(*(indata + 2), 0, 6)]; *(p + 3) = base64_alphabet[*(indata + 2) & 0x3f]; } p += 4; indata += 3; } if (outlen != NULL) { *outlen = out_len; } return ret; } int base64_decode(const char* indata, int inlen, char* outdata, int* outlen) { int ret = 0; if (indata == NULL || inlen <= 0 || outdata == NULL || outlen == NULL) { return ret = -1; } if (inlen % 4 != 0) { // 需要解碼的數(shù)據(jù)不是4字節(jié)倍數(shù) return ret = -2; } int t = 0, x = 0, y = 0, i = 0; unsigned char c = 0; int g = 3; while (indata[x] != 0) { // 需要解碼的數(shù)據(jù)對(duì)應(yīng)的ASCII值對(duì)應(yīng)base64_suffix_map的值 c = base64_suffix_map[indata[x++]]; // 對(duì)應(yīng)的值不在轉(zhuǎn)碼表中 if (c == 255) return -1; // 對(duì)應(yīng)的值是換行或者回車 if (c == 253) continue; if (c == 254) { // 對(duì)應(yīng)的值是'=' c = 0; g--; } // 將其依次放入一個(gè)int型中占3字節(jié) t = (t << 6) | c; if (++y == 4) { outdata[i++] = (unsigned char)((t >> 16) & 0xff); if (g > 1) outdata[i++] = (unsigned char)((t >> 8) & 0xff); if (g > 2) outdata[i++] = (unsigned char)(t & 0xff); y = t = 0; } } if (outlen != NULL) { *outlen = i; } return ret; } int main(int argc, char* argv[]) { char str1[] = "hello lyshark"; char str3[30] = { 0 }; char str2[30] = { 0 }; int len = 0; base64_encode(str1, (int)strlen(str1), str2, &len); printf("加密后: %s 長度: %d\n", str2, len); base64_decode(str2, (int)strlen(str2), str3, &len); printf("解密后: %s 長度: %d\n", str3, len); system("pause"); return 0; }
運(yùn)行后對(duì)特定字符串base64處理,如下圖;
AES對(duì)稱加解密
高級(jí)加密標(biāo)準(zhǔn)(Advanced Encryption Standard,AES)是一種對(duì)稱密鑰加密算法,廣泛用于保護(hù)敏感數(shù)據(jù)的機(jī)密性。AES 是一種塊密碼算法,支持不同的密鑰長度(128、192、256 比特),并且在安全性和性能之間取得了很好的平衡。
AES 操作在固定大小的數(shù)據(jù)塊上進(jìn)行,每個(gè)數(shù)據(jù)塊大小為 128 比特(16 字節(jié))。AES 使用稱為輪(rounds)的迭代結(jié)構(gòu)來執(zhí)行加密和解密。輪數(shù)取決于密鑰長度,分別為 10 輪(128 比特密鑰)、12 輪(192 比特密鑰)和 14 輪(256 比特密鑰)。AES可使用16、24或32字節(jié)密鑰(對(duì)應(yīng)128、192和256位),AES分為ECB和CBC模式,處理的數(shù)據(jù)必須是塊大小16的倍數(shù)。
AES 的基本加密流程包括以下步驟:
- 密鑰擴(kuò)展(Key Expansion): 根據(jù)輸入密鑰生成輪密鑰,用于后續(xù)的輪函數(shù)。
- 初始輪(Initial Round): 將明文與第一輪密鑰進(jìn)行逐字節(jié)的異或操作。
- 輪運(yùn)算(Rounds): 重復(fù)執(zhí)行一系列輪函數(shù),每輪包括四個(gè)操作:字節(jié)替代、行移位、列混淆和輪密鑰加。
- 最終輪(Final Round): 在最后一輪中,省略列混淆步驟。
AES 的解密過程與加密過程相似,但使用的是逆操作,如逆字節(jié)替代、逆行移位、逆列混淆和逆輪密鑰加。
CryptAcquireContext
函數(shù),用于獲取或創(chuàng)建與加密服務(wù)提供程序(CSP)相關(guān)聯(lián)的密碼學(xué)上下文。這個(gè)函數(shù)的目的是為了建立與加密服務(wù)提供程序相關(guān)的密碼學(xué)上下文,使得后續(xù)的加密操作可以在這個(gè)上下文中進(jìn)行。
以下是CryptAcquireContext
函數(shù)的一般格式:
BOOL CryptAcquireContext( HCRYPTPROV *phProv, LPCTSTR pszContainer, LPCTSTR pszProvider, DWORD dwProvType, DWORD dwFlags );
phProv
: 一個(gè)指向HCRYPTPROV
類型的指針,用于接收密碼學(xué)上下文的句柄。pszContainer
: 字符串,指定與密鑰集關(guān)聯(lián)的容器名稱??梢詾?code>NULL,表示不使用容器。pszProvider
: 字符串,指定要使用的CSP的名稱。如果為NULL
,將使用默認(rèn)的提供程序。dwProvType
: 指定CSP的類型。例如,PROV_RSA_FULL
表示使用RSA算法的提供程序。dwFlags
: 指定標(biāo)志,控制函數(shù)的行為。例如,CRYPT_VERIFYCONTEXT
表示驗(yàn)證上下文,而不是嘗試使用特定的密鑰。
CryptCreateHash
函數(shù),用于創(chuàng)建一個(gè)與密碼學(xué)上下文相關(guān)聯(lián)的哈希對(duì)象。哈希對(duì)象可用于計(jì)算數(shù)據(jù)的哈希值,常用于數(shù)字簽名、數(shù)據(jù)完整性驗(yàn)證等安全操作。
以下是CryptCreateHash
函數(shù)的一般格式:
BOOL CryptCreateHash( HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash );
hProv
: 與哈希對(duì)象關(guān)聯(lián)的密碼學(xué)上下文的句柄。Algid
: 哈希算法的標(biāo)識(shí),例如CALG_MD5
表示MD5算法。hKey
: 與哈希對(duì)象關(guān)聯(lián)的密鑰。在哈希計(jì)算中,通常不需要密鑰,因此可以將其設(shè)為NULL
。dwFlags
: 控制函數(shù)的行為的標(biāo)志。一般設(shè)為0。phHash
: 一個(gè)指向HCRYPTHASH
類型的指針,用于接收哈希對(duì)象的句柄。
成功調(diào)用該函數(shù)后,phHash
將包含一個(gè)指向新創(chuàng)建的哈希對(duì)象的句柄,該對(duì)象與指定的密碼學(xué)上下文和哈希算法相關(guān)聯(lián)。
CryptHashData
函數(shù),用于將數(shù)據(jù)添加到哈希對(duì)象中,從而更新哈希值。它常用于在計(jì)算數(shù)字簽名或驗(yàn)證數(shù)據(jù)完整性時(shí),逐步處理數(shù)據(jù)塊并更新哈希值。
以下是CryptHashData
函數(shù)的一般格式:
BOOL CryptHashData( HCRYPTHASH hHash, const BYTE *pbData, DWORD dwDataLen, DWORD dwFlags );
hHash
: 指向哈希對(duì)象的句柄。pbData
: 指向包含要添加到哈希對(duì)象的數(shù)據(jù)的緩沖區(qū)的指針。dwDataLen
: 數(shù)據(jù)緩沖區(qū)的字節(jié)數(shù)。dwFlags
: 控制函數(shù)的行為的標(biāo)志。一般設(shè)為0。
成功調(diào)用后,哈希對(duì)象的狀態(tài)將被更新以反映已添加的數(shù)據(jù),從而計(jì)算新的哈希值。這使得可以逐步處理大型數(shù)據(jù),而不需要將整個(gè)數(shù)據(jù)加載到內(nèi)存中。
CryptDeriveKey
函數(shù),用于從一個(gè)密碼導(dǎo)出密鑰。這個(gè)函數(shù)通常用于從用戶提供的密碼生成對(duì)稱密鑰,這樣就可以用于加密或解密數(shù)據(jù)。
以下是 CryptDeriveKey
函數(shù)的一般格式:
BOOL CryptDeriveKey( HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData, DWORD dwFlags, HCRYPTKEY *phKey );
hProv
: 一個(gè)有效的 CSP(Cryptographic Service Provider)句柄。Algid
: 密鑰算法標(biāo)識(shí)符,指定要?jiǎng)?chuàng)建的密鑰類型。hBaseData
: 與密鑰生成相關(guān)的基本數(shù)據(jù)的哈希對(duì)象的句柄??梢詾?nbsp;NULL
。dwFlags
: 控制函數(shù)的行為的標(biāo)志。一般設(shè)為 0。phKey
: 指向 HCRYPTKEY 類型的指針,用于接收生成的密鑰的句柄。
成功調(diào)用后,phKey
將包含一個(gè)新的密鑰句柄,可以用于后續(xù)的加密和解密操作。密鑰的具體屬性(比如大?。┯?nbsp;Algid
參數(shù)決定。
CryptEncrypt
函數(shù),用于對(duì)數(shù)據(jù)進(jìn)行加密。這個(gè)函數(shù)通常用于加密一個(gè)數(shù)據(jù)塊,例如一個(gè)文件或一個(gè)消息。
以下是 CryptEncrypt
函數(shù)的一般格式:
BOOL CryptEncrypt( HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen );
hKey
: 用于加密數(shù)據(jù)的密鑰的句柄。hHash
: 句柄,指定一個(gè)哈希對(duì)象。對(duì)稱算法不需要哈希,因此可以為NULL
。Final
: 指定是否是最后一個(gè)數(shù)據(jù)塊。如果是最后一個(gè)數(shù)據(jù)塊,將設(shè)置為TRUE
。dwFlags
: 控制函數(shù)的行為的標(biāo)志。一般設(shè)為 0。pbData
: 指向要加密的數(shù)據(jù)的指針。pdwDataLen
: 指向一個(gè)變量,用于輸入數(shù)據(jù)的大小,輸出加密后數(shù)據(jù)的大小。dwBufLen
: 緩沖區(qū)的大小。
成功調(diào)用后,pbData
將包含加密后的數(shù)據(jù)。pdwDataLen
將包含加密后數(shù)據(jù)的實(shí)際大小。
CryptDecrypt
函數(shù),用于對(duì)數(shù)據(jù)進(jìn)行解密。這個(gè)函數(shù)通常用于解密一個(gè)數(shù)據(jù)塊,例如一個(gè)文件或一個(gè)消息。
以下是 CryptDecrypt
函數(shù)的一般格式:
BOOL CryptDecrypt( HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen );
hKey
: 用于解密數(shù)據(jù)的密鑰的句柄。hHash
: 句柄,指定一個(gè)哈希對(duì)象。對(duì)稱算法不需要哈希,因此可以為NULL
。Final
: 指定是否是最后一個(gè)數(shù)據(jù)塊。如果是最后一個(gè)數(shù)據(jù)塊,將設(shè)置為TRUE
。dwFlags
: 控制函數(shù)的行為的標(biāo)志。一般設(shè)為 0。pbData
: 指向要解密的數(shù)據(jù)的指針。pdwDataLen
: 指向一個(gè)變量,用于輸入解密前數(shù)據(jù)的大小,輸出解密后數(shù)據(jù)的大小。
成功調(diào)用后,pbData
將包含解密后的數(shù)據(jù)。pdwDataLen
將包含解密后數(shù)據(jù)的實(shí)際大小。
#include <stdio.h> #include <Windows.h> // AES加密 BOOL AesEncrypt(BYTE* pPassword, BYTE* pData, DWORD& dwDataLength, DWORD dwBufferLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTHASH hCryptHash = NULL; HCRYPTKEY hCryptKey = NULL; DWORD dwPasswordLength = strlen((char*)pPassword); do { bRet = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT); bRet = CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash); bRet = CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0); bRet = CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey); bRet = CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength); } while (FALSE); if (hCryptKey || hCryptHash || hCryptProv) { CryptDestroyKey(hCryptKey); CryptDestroyHash(hCryptHash); CryptReleaseContext(hCryptProv, 0); } return bRet; } // AES解密 BOOL AesDecrypt(BYTE* pPassword, BYTE* pData, DWORD& dwDataLength, DWORD dwBufferLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTHASH hCryptHash = NULL; HCRYPTKEY hCryptKey = NULL; DWORD dwPasswordLength = strlen((char*)pPassword); do { bRet = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT); bRet = CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash); bRet = CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0); bRet = CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey); bRet = CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength); } while (FALSE); if (hCryptKey || hCryptHash || hCryptProv) { CryptDestroyKey(hCryptKey); CryptDestroyHash(hCryptHash); CryptReleaseContext(hCryptProv, 0); } return bRet; } int main(int argc, char* argv[]) { BYTE pData[MAX_PATH] = { 0 }; DWORD dwDataLength = 0; char* Msg = (char *)"hello lyshark"; strcpy((char*)pData, Msg); dwDataLength = 1 + ::strlen((char*)pData); // AES 加密 AesEncrypt((BYTE*)"123321", pData, dwDataLength, MAX_PATH); printf("AES 加密長度: %d 加密后: %s \n", dwDataLength, pData); // AES 解密 AesDecrypt((BYTE*)"123321", pData, dwDataLength, MAX_PATH); printf("AES 解密長度: %d 解密后: %s \n", dwDataLength, pData); system("pause"); return 0; }
上述代碼運(yùn)行,實(shí)現(xiàn)對(duì)特定字符串hello lyshark
加密,并使用密碼123321
,如下圖所示;
MD5/SHA256單向加解密
MD5(Message Digest Algorithm 5)是一種廣泛使用的哈希函數(shù),常用于生成數(shù)據(jù)的數(shù)字簽名。MD5 產(chǎn)生的哈希值(摘要)通常是一個(gè) 128 位的十六進(jìn)制數(shù)字,通常表示為 32 個(gè)字符。盡管 MD5 在過去廣泛用于校驗(yàn)文件完整性和生成密碼散列,但由于其容易受到碰撞攻擊的影響,現(xiàn)在已被更安全的哈希算法如 SHA-256 取代。
MD5 是一種不可逆的哈希函數(shù),其核心原理包括以下幾步:
- 填充: 對(duì)輸入數(shù)據(jù)進(jìn)行填充,使其長度滿足 512 位的倍數(shù),并在數(shù)據(jù)尾部附加原始數(shù)據(jù)長度的二進(jìn)制表示。
- 初始化: 初始化 128 位的緩沖區(qū),用于存儲(chǔ)中間計(jì)算結(jié)果。
- 處理塊: 將填充后的數(shù)據(jù)按照 512 位的塊進(jìn)行劃分,每個(gè)塊進(jìn)行一系列的運(yùn)算,更新緩沖區(qū)。
- 輸出: 將最終得到的緩沖區(qū)內(nèi)容作為 MD5 的輸出。
MD5 的核心操作主要包括四輪循環(huán),每輪循環(huán)包含 16 次操作。這些操作涉及位運(yùn)算、邏輯運(yùn)算和模運(yùn)算等,以及對(duì)緩沖區(qū)內(nèi)容的不斷更新。
SHA-256(Secure Hash Algorithm 256-bit)是 SHA-2 家族中的一員,是一種廣泛使用的密碼哈希函數(shù)。SHA-256 生成的哈希值長度為 256 位,通常以 64 個(gè)字符的十六進(jìn)制字符串表示。SHA-256 在密碼學(xué)和數(shù)據(jù)完整性驗(yàn)證中得到廣泛應(yīng)用,被認(rèn)為是一種安全可靠的哈希算法。
SHA-256 的基本原理與 MD5 類似,但具有更復(fù)雜的設(shè)計(jì)和更長的輸出長度。其核心過程包括以下幾個(gè)步驟:
- 填充: 將輸入數(shù)據(jù)填充到滿足 512 位塊大小的倍數(shù),并在數(shù)據(jù)尾部添加原始數(shù)據(jù)長度的二進(jìn)制表示。
- 初始化: 初始化 256 位的緩沖區(qū),用于存儲(chǔ)中間計(jì)算結(jié)果。
- 處理塊: 將填充后的數(shù)據(jù)按照 512 位的塊進(jìn)行劃分,每個(gè)塊進(jìn)行一系列的運(yùn)算,更新緩沖區(qū)。
- 輸出: 將最終得到的緩沖區(qū)內(nèi)容作為 SHA-256 的輸出。
SHA-256 的核心操作包括四輪循環(huán),每輪循環(huán)包含 64 次操作。這些操作包括位運(yùn)算、邏輯運(yùn)算、模運(yùn)算等,以及對(duì)緩沖區(qū)內(nèi)容的不斷更新。
CryptAcquireContext
函數(shù),用于獲取密碼學(xué)上下文句柄。這個(gè)函數(shù)通常是在進(jìn)行加密和解密操作之前調(diào)用的第一步。
以下是 CryptAcquireContext
函數(shù)的一般格式:
BOOL CryptAcquireContext( HCRYPTPROV *phProv, LPCTSTR pszContainer, LPCTSTR pszProvider, DWORD dwProvType, DWORD dwFlags );
phProv
: 用于接收密碼學(xué)上下文句柄的指針。pszContainer
: 指定密鑰容器的名稱??梢詾?nbsp;NULL
。pszProvider
: 指定加密服務(wù)提供者的名稱。可以為NULL
。dwProvType
: 指定提供者類型。常見的類型包括PROV_RSA_FULL
、PROV_RSA_AES
等。dwFlags
: 控制函數(shù)的行為的標(biāo)志。通常為 0。
成功調(diào)用后,phProv
將包含一個(gè)密碼學(xué)上下文句柄,該句柄用于后續(xù)的加密和解密操作。
CryptGetHashParam
函數(shù),用于檢索哈希對(duì)象的參數(shù)。哈希對(duì)象是用于計(jì)算數(shù)據(jù)摘要的對(duì)象,通常在密碼學(xué)操作中使用。
以下是 CryptGetHashParam
函數(shù)的一般格式:
BOOL CryptGetHashParam( HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags );
hHash
: 哈希對(duì)象的句柄。dwParam
: 指定要檢索的參數(shù)類型。常見的參數(shù)類型包括HP_HASHVAL
(獲取哈希值)和HP_HASHSIZE
(獲取哈希值的大小)等。pbData
: 用于接收參數(shù)數(shù)據(jù)的緩沖區(qū)。pdwDataLen
: 用于指定輸入緩沖區(qū)的大小,并在成功調(diào)用后包含實(shí)際返回的數(shù)據(jù)長度。dwFlags
: 控制函數(shù)的行為的標(biāo)志。通常為 0。
成功調(diào)用后,pbData
緩沖區(qū)中將包含請(qǐng)求的參數(shù)數(shù)據(jù)。
CryptDestroyHash
函數(shù),用于銷毀哈希對(duì)象。哈希對(duì)象是在進(jìn)行哈希計(jì)算時(shí)創(chuàng)建的對(duì)象,使用完畢后需要通過 CryptDestroyHash
來釋放相關(guān)資源。
以下是 CryptDestroyHash
函數(shù)的一般格式:
BOOL CryptDestroyHash( HCRYPTHASH hHash );
hHash
: 要銷毀的哈希對(duì)象的句柄。
函數(shù)返回一個(gè)布爾值,表示是否成功銷毀哈希對(duì)象。如果成功,返回 TRUE
,否則返回 FALSE
。
CryptReleaseContext
函數(shù),用于釋放密碼學(xué)上下文。密碼學(xué)上下文是在進(jìn)行加密或哈希操作時(shí)所創(chuàng)建的,使用完畢后需要通過 CryptReleaseContext
來釋放相關(guān)資源。
以下是 CryptReleaseContext
函數(shù)的一般格式:
BOOL CryptReleaseContext( HCRYPTPROV hProv, DWORD dwFlags );
hProv
: 要釋放的密碼學(xué)上下文的句柄。
dwFlags
: 一組標(biāo)志,通常可以設(shè)置為零。
函數(shù)返回一個(gè)布爾值,表示是否成功釋放密碼學(xué)上下文。如果成功,返回 TRUE
,否則返回 FALSE
。
這兩個(gè)算法都是單向加密算法,其可以將一段任意字符串壓縮為一個(gè)唯一常數(shù)。
#include <stdio.h> #include <Windows.h> BOOL CalculateHash(BYTE* pData, DWORD dwDataLength, ALG_ID algHashType, BYTE** ppHashData, DWORD* pdwHashDataLength) { HCRYPTPROV hCryptProv = NULL; HCRYPTHASH hCryptHash = NULL; BYTE* pHashData = NULL; DWORD dwHashDataLength = 0; DWORD dwTemp = 0; BOOL bRet = FALSE; do { bRet = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT); bRet = CryptCreateHash(hCryptProv, algHashType, NULL, NULL, &hCryptHash); bRet = CryptHashData(hCryptHash, pData, dwDataLength, 0); dwTemp = sizeof(dwHashDataLength); bRet = CryptGetHashParam(hCryptHash, HP_HASHSIZE, (BYTE*)(&dwHashDataLength), &dwTemp, 0); pHashData = new BYTE[dwHashDataLength]; RtlZeroMemory(pHashData, dwHashDataLength); bRet = ::CryptGetHashParam(hCryptHash, HP_HASHVAL, pHashData, &dwHashDataLength, 0); *ppHashData = pHashData; *pdwHashDataLength = dwHashDataLength; } while (FALSE); if (FALSE == bRet) { if (pHashData) { delete[]pHashData; pHashData = NULL; } } if (hCryptHash || hCryptProv) { CryptDestroyHash(hCryptHash); CryptReleaseContext(hCryptProv, 0); } return bRet; } int main(int argc, char* argv[]) { char szBuf[1024] = "hello lyshark"; BYTE* pHashData = NULL; DWORD dwHashDataLength = 0; // MD5 CalculateHash((BYTE *)szBuf, strlen(szBuf), CALG_MD5, &pHashData, &dwHashDataLength); for (DWORD x = 0; x < dwHashDataLength; x++) { printf("%x", pHashData[x]); } printf("\n"); // SHA256 CalculateHash((BYTE *)szBuf, strlen(szBuf), CALG_SHA_256, &pHashData, &dwHashDataLength); for (DWORD x = 0; x < dwHashDataLength; x++) { printf("%x", pHashData[x]); } delete[]pHashData; pHashData = NULL; system("pause"); return 0; }
上述代碼運(yùn)行后,則可以計(jì)算出hello lyshark
字符串的md5
以及sha256
摘要信息,如下所示;
RSA對(duì)稱加解密
RSA(Rivest–Shamir–Adleman)是一種非對(duì)稱加密算法,于1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩米爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)三位密碼學(xué)家提出。RSA算法基于兩個(gè)大素?cái)?shù)的乘積的難解性問題,它廣泛用于安全通信和數(shù)字簽名等領(lǐng)域。
RSA算法涉及到兩個(gè)密鑰:公鑰和私鑰。其中,公鑰用于加密,私鑰用于解密。其基本原理建立在兩個(gè)數(shù)論問題上:
- 大整數(shù)分解問題: 將一個(gè)大合數(shù)分解為兩個(gè)質(zhì)數(shù)的乘積的難度。
- 歐拉函數(shù)和模反演問題: 利用歐拉函數(shù)和模反演性質(zhì),確保僅有私鑰的持有者能夠有效地解密。
RSA算法的密鑰生成過程包括以下步驟:
- 選擇兩個(gè)大素?cái)?shù) p 和 q。
- 計(jì)算 n = pq,n 稱為模數(shù)。
- 計(jì)算歐拉函數(shù) φ(n) = (p-1)(q-1)。
- 選擇公鑰 e,滿足 1 < e < φ(n),且 e 與 φ(n) 互質(zhì)。
- 計(jì)算私鑰 d,使得 de ≡ 1 (mod φ(n))。
公鑰是 (n, e),私鑰是 (n, d)。
加密和解密過程如下:
RSA算法的安全性基于大整數(shù)分解問題的困難性,即在已知 n 的情況下,要找到 p 和 q 的乘積。當(dāng) n 非常大時(shí),這一過程變得非常耗時(shí),使得RSA算法在當(dāng)前的計(jì)算資源下被廣泛應(yīng)用于加密通信和數(shù)字簽名。
CryptGenKey
是 Windows Cryptographic API (CryptoAPI) 中的一個(gè)函數(shù),用于生成密鑰。該函數(shù)允許應(yīng)用程序生成對(duì)稱密鑰、非對(duì)稱密鑰對(duì)以及用于哈希的密鑰。
以下是 CryptGenKey
函數(shù)的一般格式:
BOOL CryptGenKey( HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYPTKEY *phKey );
hProv
: 用于生成密鑰的密碼學(xué)服務(wù)提供者 (CSP) 的句柄。Algid
: 標(biāo)識(shí)要生成的密鑰類型,可以是對(duì)稱密鑰算法、非對(duì)稱密鑰算法或用于哈希的密鑰算法。dwFlags
: 控制密鑰生成的標(biāo)志。對(duì)于不同的密鑰類型,可能有不同的標(biāo)志。phKey
: 生成的密鑰的句柄。
函數(shù)返回一個(gè)布爾值,表示是否成功生成密鑰。如果成功,返回 TRUE
,否則返回 FALSE
。
CryptExportKey
函數(shù)是 Windows Cryptographic API (CryptoAPI) 中的一個(gè)函數(shù),用于導(dǎo)出密鑰的原始或簡單 BLOB 格式。密鑰 BLOB 包含密鑰的完整信息,以便在不同的系統(tǒng)或進(jìn)程之間傳輸密鑰。
以下是 CryptExportKey
函數(shù)的一般格式:
BOOL CryptExportKey( HCRYPTKEY hKey, HCRYPTKEY hExpKey, DWORD dwBlobType, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen );
hKey
: 要導(dǎo)出的密鑰的句柄。hExpKey
: 導(dǎo)出密鑰的密碼學(xué)服務(wù)提供者 (CSP) 句柄。通常,使用與hKey
相同的 CSP。dwBlobType
: 導(dǎo)出的 BLOB 類型,可以是簡單 BLOB 或原始 BLOB。dwFlags
: 導(dǎo)出操作的標(biāo)志。pbData
: 用于接收導(dǎo)出的密鑰 BLOB 的緩沖區(qū)。pdwDataLen
: 指向存儲(chǔ)密鑰 BLOB 大小的變量的指針。在調(diào)用函數(shù)之前,應(yīng)將其設(shè)置為緩沖區(qū)的大?。辉谡{(diào)用函數(shù)后,它將包含實(shí)際寫入緩沖區(qū)的字節(jié)數(shù)。
函數(shù)返回一個(gè)布爾值,表示是否成功導(dǎo)出密鑰。如果成功,返回 TRUE
,否則返回 FALSE
。
CryptImportKey
函數(shù)是 Windows Cryptographic API (CryptoAPI) 中的一個(gè)函數(shù),用于導(dǎo)入密鑰的原始或簡單 BLOB 格式。該函數(shù)通常與 CryptExportKey
函數(shù)一起使用,用于在不同的系統(tǒng)或進(jìn)程之間傳輸密鑰。
以下是 CryptImportKey
函數(shù)的一般格式:
BOOL CryptImportKey( HCRYPTPROV hProv, const BYTE *pbData, DWORD dwDataLen, HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY *phKey );
hProv
: 密鑰將與之關(guān)聯(lián)的密碼學(xué)服務(wù)提供者 (CSP) 的句柄。pbData
: 包含要導(dǎo)入的密鑰 BLOB 的緩沖區(qū)的指針。dwDataLen
: 密鑰 BLOB 的長度(以字節(jié)為單位)。hPubKey
: 用于解密密鑰 BLOB 的公鑰的句柄。dwFlags
: 導(dǎo)入密鑰的標(biāo)志。phKey
: 指向?qū)氲拿荑€的句柄的指針。
函數(shù)返回一個(gè)布爾值,表示是否成功導(dǎo)入密鑰。如果成功,返回 TRUE
,否則返回 FALSE
。
RSA算法包括公鑰與私鑰兩部,加密時(shí)會(huì)先使用RSA生成公鑰與私鑰,然后在進(jìn)行加密。
#include <iostream> #include <Windows.h> using namespace std; // 生成公鑰和私鑰 BOOL GenerateKey(BYTE **ppPublicKey, DWORD *pdwPublicKeyLength, BYTE **ppPrivateKey, DWORD *pdwPrivateKeyLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTKEY hCryptKey = NULL; BYTE *pPublicKey = NULL; DWORD dwPublicKeyLength = 0; BYTE *pPrivateKey = NULL; DWORD dwPrivateKeyLength = 0; do { bRet = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0); if (FALSE == bRet) break; bRet = CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hCryptKey); if (FALSE == bRet) break; bRet = CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLength); if (FALSE == bRet) break; pPublicKey = new BYTE[dwPublicKeyLength]; RtlZeroMemory(pPublicKey, dwPublicKeyLength); bRet = CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, pPublicKey, &dwPublicKeyLength); if (FALSE == bRet) break; bRet = CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLength); if (FALSE == bRet) break; pPrivateKey = new BYTE[dwPrivateKeyLength]; RtlZeroMemory(pPrivateKey, dwPrivateKeyLength); bRet = CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, pPrivateKey, &dwPrivateKeyLength); if (FALSE == bRet) break; *ppPublicKey = pPublicKey; *pdwPublicKeyLength = dwPublicKeyLength; *ppPrivateKey = pPrivateKey; *pdwPrivateKeyLength = dwPrivateKeyLength; } while (FALSE); if (hCryptKey) CryptDestroyKey(hCryptKey); if (hCryptProv) CryptReleaseContext(hCryptProv, 0); return bRet; } // 公鑰加密數(shù)據(jù) BOOL RsaEncrypt(BYTE *pPublicKey, DWORD dwPublicKeyLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTKEY hCryptKey = NULL; do { bRet = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0); if (FALSE == bRet) break; bRet = CryptImportKey(hCryptProv, pPublicKey, dwPublicKeyLength, NULL, 0, &hCryptKey); if (FALSE == bRet) break; bRet = CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength); if (FALSE == bRet) break; } while (FALSE); if (hCryptKey) CryptDestroyKey(hCryptKey); if (hCryptProv) CryptReleaseContext(hCryptProv, 0); return bRet; } // 私鑰解密數(shù)據(jù) BOOL RsaDecrypt(BYTE *pPrivateKey, DWORD dwProvateKeyLength, BYTE *pData, DWORD &dwDataLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTKEY hCryptKey = NULL; do { bRet = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0); if (FALSE == bRet) break; bRet = CryptImportKey(hCryptProv, pPrivateKey, dwProvateKeyLength, NULL, 0, &hCryptKey); if (FALSE == bRet) break; bRet = CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength); if (FALSE == bRet) break; } while (FALSE); if (hCryptKey) CryptDestroyKey(hCryptKey); if (hCryptProv) CryptReleaseContext(hCryptProv, 0); return bRet; } int main(int argc, char * argv[]) { BYTE *pPublicKey = NULL; DWORD dwPublicKeyLength = 0; BYTE *pPrivateKey = NULL; DWORD dwPrivateKeyLength = 0; BYTE *pData = NULL; DWORD dwDataLength = 0; DWORD dwBufferLength = 4096; pData = new BYTE[dwBufferLength]; RtlZeroMemory(pData, dwBufferLength); lstrcpy((char *)pData, "hello lyshark"); dwDataLength = 1 + lstrlen((char *)pData); // 輸出加密前原始數(shù)據(jù) printf("加密前原始數(shù)據(jù): "); for (int i = 0; i < dwDataLength; i++) printf("%x", pData[i]); printf("\n\n"); // 生成公鑰和私鑰 GenerateKey(&pPublicKey, &dwPublicKeyLength, &pPrivateKey, &dwPrivateKeyLength); printf("公鑰: "); for (int i = 0; i < dwPublicKeyLength; i++) printf("%.2x", pPublicKey[i]); printf("\n\n"); printf("私鑰: "); for (int i = 0; i < dwPrivateKeyLength; i++) printf("%.2x", pPrivateKey[i]); printf("\n\n"); // 使用公鑰加密 RsaEncrypt(pPublicKey, dwPublicKeyLength, pData, dwDataLength, dwBufferLength); printf("公鑰加密: "); for (int i = 0; i < dwDataLength; i++) printf("%x", pData[i]); printf("\n\n"); // 使用私鑰解密 RsaDecrypt(pPrivateKey, dwPrivateKeyLength, pData, dwDataLength); printf("私鑰解密: "); for (int i = 0; i < dwDataLength; i++) printf("%x", pData[i]); printf("\n\n"); delete[]pData; delete[]pPrivateKey; delete[]pPublicKey; system("pause"); return 0; }
運(yùn)行后生成公鑰與私鑰,并對(duì)字符串加密與解密,如下圖所示;
以上就是C/C++中常用加密與解密算法的實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于C++加密與解密算法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Matlab實(shí)現(xiàn)繪制有氣泡感的網(wǎng)絡(luò)圖
這篇文章主要介紹了如何利用Matlab實(shí)現(xiàn)繪制有氣泡感的網(wǎng)絡(luò)圖,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Matlab有一定的幫助,需要的可以參考一下2023-02-02C++11顯示類型轉(zhuǎn)換的優(yōu)點(diǎn)
這篇文章主要介紹了C++11顯示類型轉(zhuǎn)換的優(yōu)點(diǎn),幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下2020-08-08詳解C++值多態(tài)中的傳統(tǒng)多態(tài)與類型擦除
值多態(tài)是一種介于傳統(tǒng)多態(tài)與類型擦除之間的多態(tài)實(shí)現(xiàn)方式,借鑒了值語義,保留了繼承,在單繼承的適用范圍內(nèi),程序和程序員都能從中受益。這篇文章主要介紹了C++值多態(tài)中的傳統(tǒng)多態(tài)與類型擦除,需要的朋友可以參考下2020-04-04總結(jié)了24個(gè)C++的大坑,你能躲過幾個(gè)
這篇文章主要介紹了總結(jié)了24個(gè)C++的大坑,你能躲過幾個(gè),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2021-05-05VS2017+Qt5+Opencv3.4調(diào)用攝像頭拍照并存儲(chǔ)
本文主要介紹了VS2017+Qt5+Opencv3.4調(diào)用攝像頭拍照并存儲(chǔ),實(shí)現(xiàn)了視頻,拍照,保存這三個(gè)功能。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05