電腦保安和資料隱私是現代應用程式設計中至關重要的方面。為了確保資料的機密性和完整性,常常需要使用加密和解密演演算法。C++是一種廣泛使用的程式語言,提供了許多加密和解密演演算法的實現。本文將介紹一些在C++中常用的加密與解密演演算法,這其中包括Xor互斥或、BASE64、AES、MD5、SHA256、RSA等。
互斥或(XOR)加密演演算法是一種基於互斥或運算的簡單且常見的加密技術。在互斥或加密中,每個位上的值通過與一個金鑰位進行互斥或運算來改變。這種加密演演算法的簡單性和高效性使得它在某些場景下很有用,尤其是對於簡單的資料加密需求。
互斥或運算是一種邏輯運算,其規則如下:
在互斥或加密中,將明文與金鑰進行逐位互斥或運算。如果明文位和金鑰位相同,則結果為0;如果不同,則結果為1。這個過程是可逆的,即可以通過再次互斥或同樣的金鑰來還原原始明文。
#include <Windows.h>
#include <iostream>
using namespace std;
// 獲取互斥或整數
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;
}
// 互斥或為字串
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[])
{
// 計算加密金鑰
long ref = GetXorKey("lyshark");
std::cout << "計算互斥或金鑰: " << ref << std::endl;
// 執行互斥或加密
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;
}
執行後對特定字串互斥或處理,如下圖;
Base64 是一種常見的編碼和解碼演演算法,用於將二進位制資料轉換成可列印的 ASCII 字串,以及將這樣的字串還原回二進位制資料。Base64 編碼是一種將二進位制資料表示為 ASCII 字元的方式,廣泛應用於資料傳輸和儲存領域。
Base64 編碼基於一組 64 個字元的編碼表,通常包括大寫字母 A-Z、小寫字母 a-z、數位 0-9,以及兩個額外的字元 '+' 和 '/'。這樣的字元集是為了確保編碼後的資料是可列印的,並且在不同系統之間可以被準確傳輸。
編碼的過程如下:
解碼的過程是編碼的逆過程。
#include <iostream>
#include <Windows.h>
// base64 轉換表, 共64個
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',
'+', '/'
};
// 解碼時使用
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的倍數, 那麼需要補成3的倍數
int in_len = 0;
// 需要補齊的字元個數, 這樣只有2, 1, 0(0的話不需要拼接, )
int pad_num = 0;
if (inlen % 3 != 0)
{
pad_num = 3 - inlen % 3;
}
// 拼接後的長度, 實際編碼需要的長度(3的倍數)
in_len = inlen + pad_num;
// 編碼後的長度
int out_len = in_len * 8 / 6;
// 定義指標指向傳出data的首地址
char* p = outdata;
//編碼, 長度為調整後的長度, 3位元組一組
for (int i = 0; i < in_len; i += 3)
{
// 將indata第一個字元向右移動2bit(丟棄2bit)
int value = *indata >> 2;
// 對應base64轉換表的字元
char c = base64_alphabet[value];
// 將對應字元(編碼後字元)賦值給outdata第一位元組
*p = c;
//處理最後一組(最後3位元組)的資料
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)
{
// 編碼後的資料要補兩個 '='
*(p + 1) = base64_alphabet[(int)cmove_bits(*indata, 6, 2)];
*(p + 2) = '=';
*(p + 3) = '=';
}
}
else
{
// 處理正常的3位元組的資料
*(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)
{
// 需要解碼的資料不是4位元組倍數
return ret = -2;
}
int t = 0, x = 0, y = 0, i = 0;
unsigned char c = 0;
int g = 3;
while (indata[x] != 0)
{
// 需要解碼的資料對應的ASCII值對應base64_suffix_map的值
c = base64_suffix_map[indata[x++]];
// 對應的值不在轉碼錶中
if (c == 255)
return -1;
// 對應的值是換行或者回車
if (c == 253)
continue;
if (c == 254)
{
// 對應的值是'='
c = 0; g--;
}
// 將其依次放入一個int型中佔3位元組
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;
}
執行後對特定字串base64處理,如下圖;
高階加密標準(Advanced Encryption Standard,AES)是一種對稱金鑰加密演演算法,廣泛用於保護敏感資料的機密性。AES 是一種塊密碼演演算法,支援不同的金鑰長度(128、192、256 位元),並且在安全性和效能之間取得了很好的平衡。
AES 操作在固定大小的資料塊上進行,每個資料塊大小為 128 位元(16 位元組)。AES 使用稱為輪(rounds)的迭代結構來執行加密和解密。輪數取決於金鑰長度,分別為 10 輪(128 位元金鑰)、12 輪(192 位元金鑰)和 14 輪(256 位元金鑰)。AES可使用16、24或32位元組金鑰(對應128、192和256位),AES分為ECB和CBC模式,處理的資料必須是塊大小16的倍數。
AES 的基本加密流程包括以下步驟:
AES 的解密過程與加密過程相似,但使用的是逆操作,如逆位元組替代、逆行移位、逆列混淆和逆輪金鑰加。
CryptAcquireContext
函數,用於獲取或建立與加密服務提供程式(CSP)相關聯的密碼學上下文。這個函數的目的是為了建立與加密服務提供程式相關的密碼學上下文,使得後續的加密操作可以在這個上下文中進行。
以下是CryptAcquireContext
函數的一般格式:
BOOL CryptAcquireContext(
HCRYPTPROV *phProv,
LPCTSTR pszContainer,
LPCTSTR pszProvider,
DWORD dwProvType,
DWORD dwFlags
);
phProv
: 一個指向HCRYPTPROV
型別的指標,用於接收密碼學上下文的控制程式碼。pszContainer
: 字串,指定與金鑰集關聯的容器名稱。可以為NULL
,表示不使用容器。pszProvider
: 字串,指定要使用的CSP的名稱。如果為NULL
,將使用預設的提供程式。dwProvType
: 指定CSP的型別。例如,PROV_RSA_FULL
表示使用RSA演演算法的提供程式。dwFlags
: 指定標誌,控制函數的行為。例如,CRYPT_VERIFYCONTEXT
表示驗證上下文,而不是嘗試使用特定的金鑰。CryptCreateHash
函數,用於建立一個與密碼學上下文相關聯的雜湊物件。雜湊物件可用於計算資料的雜湊值,常用於數位簽章、資料完整性驗證等安全操作。
以下是CryptCreateHash
函數的一般格式:
BOOL CryptCreateHash(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTKEY hKey,
DWORD dwFlags,
HCRYPTHASH *phHash
);
hProv
: 與雜湊物件關聯的密碼學上下文的控制程式碼。Algid
: 雜湊演演算法的標識,例如CALG_MD5
表示MD5演演算法。hKey
: 與雜湊物件關聯的金鑰。在雜湊計算中,通常不需要金鑰,因此可以將其設為NULL
。dwFlags
: 控制函數的行為的標誌。一般設為0。phHash
: 一個指向HCRYPTHASH
型別的指標,用於接收雜湊物件的控制程式碼。成功呼叫該函數後,phHash
將包含一個指向新建立的雜湊物件的控制程式碼,該物件與指定的密碼學上下文和雜湊演演算法相關聯。
CryptHashData
函數,用於將資料新增到雜湊物件中,從而更新雜湊值。它常用於在計算數位簽章或驗證資料完整性時,逐步處理資料塊並更新雜湊值。
以下是CryptHashData
函數的一般格式:
BOOL CryptHashData(
HCRYPTHASH hHash,
const BYTE *pbData,
DWORD dwDataLen,
DWORD dwFlags
);
hHash
: 指向雜湊物件的控制程式碼。pbData
: 指向包含要新增到雜湊物件的資料的緩衝區的指標。dwDataLen
: 資料緩衝區的位元組數。dwFlags
: 控制函數的行為的標誌。一般設為0。成功呼叫後,雜湊物件的狀態將被更新以反映已新增的資料,從而計算新的雜湊值。這使得可以逐步處理大型資料,而不需要將整個資料載入到記憶體中。
CryptDeriveKey
函數,用於從一個密碼匯出金鑰。這個函數通常用於從使用者提供的密碼生成對稱金鑰,這樣就可以用於加密或解密資料。
以下是 CryptDeriveKey
函數的一般格式:
BOOL CryptDeriveKey(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTHASH hBaseData,
DWORD dwFlags,
HCRYPTKEY *phKey
);
hProv
: 一個有效的 CSP(Cryptographic Service Provider)控制程式碼。Algid
: 金鑰演演算法識別符號,指定要建立的金鑰型別。hBaseData
: 與金鑰生成相關的基本資料的雜湊物件的控制程式碼。可以為 NULL
。dwFlags
: 控制函數的行為的標誌。一般設為 0。phKey
: 指向 HCRYPTKEY 型別的指標,用於接收生成的金鑰的控制程式碼。成功呼叫後,phKey
將包含一個新的金鑰控制程式碼,可以用於後續的加密和解密操作。金鑰的具體屬性(比如大小)由 Algid
引數決定。
CryptEncrypt
函數,用於對資料進行加密。這個函數通常用於加密一個資料塊,例如一個檔案或一個訊息。
以下是 CryptEncrypt
函數的一般格式:
BOOL CryptEncrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwBufLen
);
hKey
: 用於加密資料的金鑰的控制程式碼。hHash
: 控制程式碼,指定一個雜湊物件。對稱演演算法不需要雜湊,因此可以為 NULL
。Final
: 指定是否是最後一個資料塊。如果是最後一個資料塊,將設定為 TRUE
。dwFlags
: 控制函數的行為的標誌。一般設為 0。pbData
: 指向要加密的資料的指標。pdwDataLen
: 指向一個變數,用於輸入資料的大小,輸出加密後資料的大小。dwBufLen
: 緩衝區的大小。成功呼叫後,pbData
將包含加密後的資料。pdwDataLen
將包含加密後資料的實際大小。
CryptDecrypt
函數,用於對資料進行解密。這個函數通常用於解密一個資料塊,例如一個檔案或一個訊息。
以下是 CryptDecrypt
函數的一般格式:
BOOL CryptDecrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen
);
hKey
: 用於解密資料的金鑰的控制程式碼。hHash
: 控制程式碼,指定一個雜湊物件。對稱演演算法不需要雜湊,因此可以為 NULL
。Final
: 指定是否是最後一個資料塊。如果是最後一個資料塊,將設定為 TRUE
。dwFlags
: 控制函數的行為的標誌。一般設為 0。pbData
: 指向要解密的資料的指標。pdwDataLen
: 指向一個變數,用於輸入解密前資料的大小,輸出解密後資料的大小。成功呼叫後,pbData
將包含解密後的資料。pdwDataLen
將包含解密後資料的實際大小。
#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;
}
上述程式碼執行,實現對特定字串hello lyshark
加密,並使用密碼123321
,如下圖所示;
MD5(Message Digest Algorithm 5)是一種廣泛使用的雜湊函數,常用於生成資料的數位簽章。MD5 產生的雜湊值(摘要)通常是一個 128 位的十六進位制數位,通常表示為 32 個字元。儘管 MD5 在過去廣泛用於校驗檔案完整性和生成密碼雜湊,但由於其容易受到碰撞攻擊的影響,現在已被更安全的雜湊演演算法如 SHA-256 取代。
MD5 是一種不可逆的雜湊函數,其核心原理包括以下幾步:
MD5 的核心操作主要包括四輪迴圈,每輪迴圈包含 16 次操作。這些操作涉及位運算、邏輯運算和模運算等,以及對緩衝區內容的不斷更新。
SHA-256(Secure Hash Algorithm 256-bit)是 SHA-2 家族中的一員,是一種廣泛使用的密碼雜湊函數。SHA-256 生成的雜湊值長度為 256 位,通常以 64 個字元的十六進位制字串表示。SHA-256 在密碼學和資料完整性驗證中得到廣泛應用,被認為是一種安全可靠的雜湊演演算法。
SHA-256 的基本原理與 MD5 類似,但具有更復雜的設計和更長的輸出長度。其核心過程包括以下幾個步驟:
SHA-256 的核心操作包括四輪迴圈,每輪迴圈包含 64 次操作。這些操作包括位運算、邏輯運算、模運算等,以及對緩衝區內容的不斷更新。
CryptAcquireContext
函數,用於獲取密碼學上下文控制程式碼。這個函數通常是在進行加密和解密操作之前呼叫的第一步。
以下是 CryptAcquireContext
函數的一般格式:
BOOL CryptAcquireContext(
HCRYPTPROV *phProv,
LPCTSTR pszContainer,
LPCTSTR pszProvider,
DWORD dwProvType,
DWORD dwFlags
);
phProv
: 用於接收密碼學上下文控制程式碼的指標。pszContainer
: 指定金鑰容器的名稱。可以為 NULL
。pszProvider
: 指定加密服務提供者的名稱。可以為 NULL
。dwProvType
: 指定提供者型別。常見的型別包括 PROV_RSA_FULL
、PROV_RSA_AES
等。dwFlags
: 控制函數的行為的標誌。通常為 0。成功呼叫後,phProv
將包含一個密碼學上下文控制程式碼,該控制程式碼用於後續的加密和解密操作。
CryptGetHashParam
函數,用於檢索雜湊物件的引數。雜湊物件是用於計算資料摘要的物件,通常在密碼學操作中使用。
以下是 CryptGetHashParam
函數的一般格式:
BOOL CryptGetHashParam(
HCRYPTHASH hHash,
DWORD dwParam,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwFlags
);
hHash
: 雜湊物件的控制程式碼。dwParam
: 指定要檢索的引數型別。常見的引數型別包括 HP_HASHVAL
(獲取雜湊值)和 HP_HASHSIZE
(獲取雜湊值的大小)等。pbData
: 用於接收引數資料的緩衝區。pdwDataLen
: 用於指定輸入緩衝區的大小,並在成功呼叫後包含實際返回的資料長度。dwFlags
: 控制函數的行為的標誌。通常為 0。成功呼叫後,pbData
緩衝區中將包含請求的引數資料。
CryptDestroyHash
函數,用於銷燬雜湊物件。雜湊物件是在進行雜湊計算時建立的物件,使用完畢後需要通過 CryptDestroyHash
來釋放相關資源。
以下是 CryptDestroyHash
函數的一般格式:
BOOL CryptDestroyHash(
HCRYPTHASH hHash
);
hHash
: 要銷燬的雜湊物件的控制程式碼。函數返回一個布林值,表示是否成功銷燬雜湊物件。如果成功,返回 TRUE
,否則返回 FALSE
。
CryptReleaseContext
函數,用於釋放密碼學上下文。密碼學上下文是在進行加密或雜湊操作時所建立的,使用完畢後需要通過 CryptReleaseContext
來釋放相關資源。
以下是 CryptReleaseContext
函數的一般格式:
BOOL CryptReleaseContext(
HCRYPTPROV hProv,
DWORD dwFlags
);
hProv
: 要釋放的密碼學上下文的控制程式碼。dwFlags
: 一組標誌,通常可以設定為零。函數返回一個布林值,表示是否成功釋放密碼學上下文。如果成功,返回 TRUE
,否則返回 FALSE
。
這兩個演演算法都是單向加密演演算法,其可以將一段任意字串壓縮為一個唯一常數。
#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;
}
上述程式碼執行後,則可以計算出hello lyshark
字串的md5
以及sha256
摘要資訊,如下所示;
RSA(Rivest–Shamir–Adleman)是一種非對稱加密演演算法,於1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩米爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)三位密碼學家提出。RSA演演算法基於兩個大素數的乘積的難解性問題,它廣泛用於安全通訊和數位簽章等領域。
RSA演演算法涉及到兩個金鑰:公鑰和私鑰。其中,公鑰用於加密,私鑰用於解密。其基本原理建立在兩個數論問題上:
RSA演演算法的金鑰生成過程包括以下步驟:
公鑰是 (n, e),私鑰是 (n, d)。
加密和解密過程如下:
RSA演演算法的安全性基於大整數分解問題的困難性,即在已知 n 的情況下,要找到 p 和 q 的乘積。當 n 非常大時,這一過程變得非常耗時,使得RSA演演算法在當前的計算資源下被廣泛應用於加密通訊和數位簽章。
CryptGenKey
是 Windows Cryptographic API (CryptoAPI) 中的一個函數,用於生成金鑰。該函數允許應用程式生成對稱金鑰、非對稱金鑰對以及用於雜湊的金鑰。
以下是 CryptGenKey
函數的一般格式:
BOOL CryptGenKey(
HCRYPTPROV hProv,
ALG_ID Algid,
DWORD dwFlags,
HCRYPTKEY *phKey
);
hProv
: 用於生成金鑰的密碼學服務提供者 (CSP) 的控制程式碼。Algid
: 標識要生成的金鑰型別,可以是對稱金鑰演演算法、非對稱金鑰演演算法或用於雜湊的金鑰演演算法。dwFlags
: 控制金鑰生成的標誌。對於不同的金鑰型別,可能有不同的標誌。phKey
: 生成的金鑰的控制程式碼。函數返回一個布林值,表示是否成功生成金鑰。如果成功,返回 TRUE
,否則返回 FALSE
。
CryptExportKey
函數是 Windows Cryptographic API (CryptoAPI) 中的一個函數,用於匯出金鑰的原始或簡單 BLOB 格式。金鑰 BLOB 包含金鑰的完整資訊,以便在不同的系統或程序之間傳輸金鑰。
以下是 CryptExportKey
函數的一般格式:
BOOL CryptExportKey(
HCRYPTKEY hKey,
HCRYPTKEY hExpKey,
DWORD dwBlobType,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen
);
hKey
: 要匯出的金鑰的控制程式碼。hExpKey
: 匯出金鑰的密碼學服務提供者 (CSP) 控制程式碼。通常,使用與 hKey
相同的 CSP。dwBlobType
: 匯出的 BLOB 型別,可以是簡單 BLOB 或原始 BLOB。dwFlags
: 匯出操作的標誌。pbData
: 用於接收匯出的金鑰 BLOB 的緩衝區。pdwDataLen
: 指向儲存金鑰 BLOB 大小的變數的指標。在呼叫函數之前,應將其設定為緩衝區的大小;在呼叫函數後,它將包含實際寫入緩衝區的位元組數。函數返回一個布林值,表示是否成功匯出金鑰。如果成功,返回 TRUE
,否則返回 FALSE
。
CryptImportKey
函數是 Windows Cryptographic API (CryptoAPI) 中的一個函數,用於匯入金鑰的原始或簡單 BLOB 格式。該函數通常與 CryptExportKey
函數一起使用,用於在不同的系統或程序之間傳輸金鑰。
以下是 CryptImportKey
函數的一般格式:
BOOL CryptImportKey(
HCRYPTPROV hProv,
const BYTE *pbData,
DWORD dwDataLen,
HCRYPTKEY hPubKey,
DWORD dwFlags,
HCRYPTKEY *phKey
);
hProv
: 金鑰將與之關聯的密碼學服務提供者 (CSP) 的控制程式碼。pbData
: 包含要匯入的金鑰 BLOB 的緩衝區的指標。dwDataLen
: 金鑰 BLOB 的長度(以位元組為單位)。hPubKey
: 用於解密金鑰 BLOB 的公鑰的控制程式碼。dwFlags
: 匯入金鑰的標誌。phKey
: 指向匯入的金鑰的控制程式碼的指標。函數返回一個布林值,表示是否成功匯入金鑰。如果成功,返回 TRUE
,否則返回 FALSE
。
RSA演演算法包括公鑰與私鑰兩部,加密時會先使用RSA生成公鑰與私鑰,然後在進行加密。
#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;
}
// 公鑰加密資料
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;
}
// 私鑰解密資料
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);
// 輸出加密前原始資料
printf("加密前原始資料: ");
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;
}
執行後生成公鑰與私鑰,並對字串加密與解密,如下圖所示;