C/C++ 常用加密與解密演算法

来源:https://www.cnblogs.com/LyShark/archive/2023/11/26/17856691.html
-Advertisement-
Play Games

電腦安全和數據隱私是現代應用程式設計中至關重要的方面。為了確保數據的機密性和完整性,常常需要使用加密和解密演算法。C++是一種廣泛使用的編程語言,提供了許多加密和解密演算法的實現。本文將介紹一些在C++中常用的加密與解密演算法,這其中包括Xor異或、BASE64、AES、MD5、SHA256、RSA等。 ...


電腦安全和數據隱私是現代應用程式設計中至關重要的方面。為了確保數據的機密性和完整性,常常需要使用加密和解密演算法。C++是一種廣泛使用的編程語言,提供了許多加密和解密演算法的實現。本文將介紹一些在C++中常用的加密與解密演算法,這其中包括Xor異或、BASE64、AES、MD5、SHA256、RSA等。

異或加解密

異或(XOR)加密演算法是一種基於異或運算的簡單且常見的加密技術。在異或加密中,每個位上的值通過與一個密鑰位進行異或運算來改變。這種加密演算法的簡單性和高效性使得它在某些場景下很有用,尤其是對於簡單的數據加密需求。

異或運算是一種邏輯運算,其規則如下:

  • 0 XOR 0 = 0
  • 0 XOR 1 = 1
  • 1 XOR 0 = 1
  • 1 XOR 1 = 0

在異或加密中,將明文與密鑰進行逐位異或運算。如果明文位和密鑰位相同,則結果為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加解密

Base64 是一種常見的編碼和解碼演算法,用於將二進位數據轉換成可列印的 ASCII 字元串,以及將這樣的字元串還原回二進位數據。Base64 編碼是一種將二進位數據表示為 ASCII 字元的方式,廣泛應用於數據傳輸和存儲領域。

Base64 編碼基於一組 64 個字元的編碼表,通常包括大寫字母 A-Z、小寫字母 a-z、數字 0-9,以及兩個額外的字元 '+' 和 '/'。這樣的字元集是為了確保編碼後的數據是可列印的,並且在不同系統之間可以被準確傳輸。

編碼的過程如下:

  1. 將待編碼的數據劃分為每 3 個位元組一組(24 位)。
  2. 將每組 3 個位元組拆分成 4 個 6 位的塊。
  3. 每個 6 位的塊對應編碼表中的一個字元。
  4. 如果數據長度不是 3 的倍數,使用 '=' 字元進行填充。

解碼的過程是編碼的逆過程。

#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處理,如下圖;

AES對稱加解密

高級加密標準(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 的基本加密流程包括以下步驟:

  1. 密鑰擴展(Key Expansion): 根據輸入密鑰生成輪密鑰,用於後續的輪函數。
  2. 初始輪(Initial Round): 將明文與第一輪密鑰進行逐位元組的異或操作。
  3. 輪運算(Rounds): 重覆執行一系列輪函數,每輪包括四個操作:位元組替代、行移位、列混淆和輪密鑰加。
  4. 最終輪(Final Round): 在最後一輪中,省略列混淆步驟。

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/SHA256單向加解密

MD5(Message Digest Algorithm 5)是一種廣泛使用的哈希函數,常用於生成數據的數字簽名。MD5 產生的哈希值(摘要)通常是一個 128 位的十六進位數字,通常表示為 32 個字元。儘管 MD5 在過去廣泛用於校驗文件完整性和生成密碼散列,但由於其容易受到碰撞攻擊的影響,現在已被更安全的哈希演算法如 SHA-256 取代。

MD5 是一種不可逆的哈希函數,其核心原理包括以下幾步:

  1. 填充: 對輸入數據進行填充,使其長度滿足 512 位的倍數,併在數據尾部附加原始數據長度的二進位表示。
  2. 初始化: 初始化 128 位的緩衝區,用於存儲中間計算結果。
  3. 處理塊: 將填充後的數據按照 512 位的塊進行劃分,每個塊進行一系列的運算,更新緩衝區。
  4. 輸出: 將最終得到的緩衝區內容作為 MD5 的輸出。

MD5 的核心操作主要包括四輪迴圈,每輪迴圈包含 16 次操作。這些操作涉及位運算、邏輯運算和模運算等,以及對緩衝區內容的不斷更新。

SHA-256(Secure Hash Algorithm 256-bit)是 SHA-2 家族中的一員,是一種廣泛使用的密碼哈希函數。SHA-256 生成的哈希值長度為 256 位,通常以 64 個字元的十六進位字元串表示。SHA-256 在密碼學和數據完整性驗證中得到廣泛應用,被認為是一種安全可靠的哈希演算法。

SHA-256 的基本原理與 MD5 類似,但具有更複雜的設計和更長的輸出長度。其核心過程包括以下幾個步驟:

  1. 填充: 將輸入數據填充到滿足 512 位塊大小的倍數,併在數據尾部添加原始數據長度的二進位表示。
  2. 初始化: 初始化 256 位的緩衝區,用於存儲中間計算結果。
  3. 處理塊: 將填充後的數據按照 512 位的塊進行劃分,每個塊進行一系列的運算,更新緩衝區。
  4. 輸出: 將最終得到的緩衝區內容作為 SHA-256 的輸出。

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_FULLPROV_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對稱加解密

RSA(Rivest–Shamir–Adleman)是一種非對稱加密演算法,於1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩米爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)三位密碼學家提出。RSA演算法基於兩個大素數的乘積的難解性問題,它廣泛用於安全通信和數字簽名等領域。

RSA演算法涉及到兩個密鑰:公鑰和私鑰。其中,公鑰用於加密,私鑰用於解密。其基本原理建立在兩個數論問題上:

  • 大整數分解問題: 將一個大合數分解為兩個質數的乘積的難度。
  • 歐拉函數和模反演問題: 利用歐拉函數和模反演性質,確保僅有私鑰的持有者能夠有效地解密。

RSA演算法的密鑰生成過程包括以下步驟:

  1. 選擇兩個大素數 p 和 q。
  2. 計算 n = pq,n 稱為模數。
  3. 計算歐拉函數 φ(n) = (p-1)(q-1)。
  4. 選擇公鑰 e,滿足 1 < e < φ(n),且 e 與 φ(n) 互質。
  5. 計算私鑰 d,使得 de ≡ 1 (mod φ(n))。

公鑰是 (n, e),私鑰是 (n, d)。

加密和解密過程如下:

RSA演算法的安全性基於大整數分解問題的困難性,即在已知 n 的情況下,要找到 pq 的乘積。當 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;
}

運行後生成公鑰與私鑰,並對字元串加密與解密,如下圖所示;

文章出處:https://www.cnblogs.com/LyShark/p/17856691.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在導入包方面,兩者都使用import語句,但是Python沒有類路徑的概念,直接使用文件名來導入模塊。我們還討論了自定義模塊的創建和使用,以及在不同目錄下如何組織代碼結構。此外,我們介紹了一些常用的Python模塊,包括time、datetime、json、os和sys,它們在開發中非常實用。希望今... ...
  • 前言 玩SpringCloud之前最好懂SpringBoot,別搞撐死駱駝的事。Servlet整一下變成Spring;SSM封裝、加入東西就變為SpringBoot;SpringBoot再封裝、加入東西就變為SpringCloud 架構的演進 單體應用架構 單體架構:表示層、業務邏輯層和數據訪問層即 ...
  • 在今天的課上,我們深入討論了封裝、反射和單例模式這幾個重要的概念。我不想過多地贅述它們的細節,但是請大家務必記住它們的基本語法規則,因為這也是面向對象章節的結束。我希望大家能夠牢牢掌握這些知識點,為未來的學習打下堅實的基礎。 ...
  • 在Web應用中,表單處理是一個基本而常見的任務。Python的WTForms庫通過提供表單的結構、驗證和渲染等功能,簡化了表單的處理流程。與此同時,Flask的擴展Flask-WTF更進一步地整合了WTForms,為開發者提供了更便捷、靈活的表單處理方式。Flask-WTF是建立在WTForms之上... ...
  • 1 簡介 谷歌文檔是一種協作文檔編輯服務。 協作文檔編輯服務可以通過兩種方式設計: 設計為C/S架構的集中式設施,為所有用戶提供文檔編輯服務 使用點對點技術設計,以便在單個文檔上協作 大多數商業解決方案側重於客戶端服務體繫結構,以實現更精細的控制。因此,我們將關註使用客戶端服務體繫結構設計服務。讓我 ...
  • Jinja2,由Flask框架的創作者開發,是一款功能豐富的模板引擎,以其完整的Unicode支持、靈活性、高效性和安全性而備受推崇。最初受Django模板引擎啟發,Jinja2為Flask提供了強大的模板支持,後來也成為其他項目的首選。在本文中,我們將深入探討Jinja2的特性、語法以及如何在Fl... ...
  • 類應該是被封裝的,類的用戶通過介面使用類提供的功能,而不必關心類的內部如何實現。然而,C++標準庫容器 std::vector 的實現滲透到了介面中來。對於以下代碼: const int pushNum = 10; std::vector<int> v = { 1,2,3 }; int* p = & ...
  • Bug1 自定義被限流的後續操作@SentinelResource(blockHandler = "blockHandler"),其中blockHandler處理函數不執行 前置條件 //定義的資源 @GetMapping("/resource") @SentinelResource(value = ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...