C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++加密与解密算法

C/C++中常用加密与解密算法的实现

作者:微软技术分享

这篇文章主要为大家详细介绍了一些在C++中常用的加密与解密算法,这其中包括Xor异或、BASE64、AES、MD5、SHA256、RSA等,感兴趣的小伙伴可以学习一下

计算机安全和数据隐私是现代应用程序设计中至关重要的方面。为了确保数据的机密性和完整性,常常需要使用加密和解密算法。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加解密

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处理,如下图;

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 的基本加密流程包括以下步骤:

AES 的解密过程与加密过程相似,但使用的是逆操作,如逆字节替代、逆行移位、逆列混淆和逆轮密钥加。

CryptAcquireContext函数,用于获取或创建与加密服务提供程序(CSP)相关联的密码学上下文。这个函数的目的是为了建立与加密服务提供程序相关的密码学上下文,使得后续的加密操作可以在这个上下文中进行。

以下是CryptAcquireContext函数的一般格式:

BOOL CryptAcquireContext(
  HCRYPTPROV *phProv,
  LPCTSTR    pszContainer,
  LPCTSTR    pszProvider,
  DWORD      dwProvType,
  DWORD      dwFlags
);

CryptCreateHash 函数,用于创建一个与密码学上下文相关联的哈希对象。哈希对象可用于计算数据的哈希值,常用于数字签名、数据完整性验证等安全操作。

以下是CryptCreateHash函数的一般格式:

BOOL CryptCreateHash(
  HCRYPTPROV hProv,
  ALG_ID     Algid,
  HCRYPTKEY  hKey,
  DWORD      dwFlags,
  HCRYPTHASH *phHash
);

成功调用该函数后,phHash将包含一个指向新创建的哈希对象的句柄,该对象与指定的密码学上下文和哈希算法相关联。

CryptHashData函数,用于将数据添加到哈希对象中,从而更新哈希值。它常用于在计算数字签名或验证数据完整性时,逐步处理数据块并更新哈希值。

以下是CryptHashData函数的一般格式:

BOOL CryptHashData(
  HCRYPTHASH hHash,
  const BYTE *pbData,
  DWORD      dwDataLen,
  DWORD      dwFlags
);

成功调用后,哈希对象的状态将被更新以反映已添加的数据,从而计算新的哈希值。这使得可以逐步处理大型数据,而不需要将整个数据加载到内存中。

CryptDeriveKey 函数,用于从一个密码导出密钥。这个函数通常用于从用户提供的密码生成对称密钥,这样就可以用于加密或解密数据。

以下是 CryptDeriveKey 函数的一般格式:

BOOL CryptDeriveKey(
  HCRYPTPROV hProv,
  ALG_ID     Algid,
  HCRYPTHASH hBaseData,
  DWORD      dwFlags,
  HCRYPTKEY  *phKey
);

成功调用后,phKey 将包含一个新的密钥句柄,可以用于后续的加密和解密操作。密钥的具体属性(比如大小)由 Algid 参数决定。

CryptEncrypt 函数,用于对数据进行加密。这个函数通常用于加密一个数据块,例如一个文件或一个消息。

以下是 CryptEncrypt 函数的一般格式:

BOOL CryptEncrypt(
  HCRYPTKEY hKey,
  HCRYPTHASH hHash,
  BOOL      Final,
  DWORD     dwFlags,
  BYTE      *pbData,
  DWORD     *pdwDataLen,
  DWORD     dwBufLen
);

成功调用后,pbData 将包含加密后的数据。pdwDataLen 将包含加密后数据的实际大小。

CryptDecrypt 函数,用于对数据进行解密。这个函数通常用于解密一个数据块,例如一个文件或一个消息。

以下是 CryptDecrypt 函数的一般格式:

BOOL CryptDecrypt(
  HCRYPTKEY hKey,
  HCRYPTHASH hHash,
  BOOL      Final,
  DWORD     dwFlags,
  BYTE      *pbData,
  DWORD     *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 是一种不可逆的哈希函数,其核心原理包括以下几步:

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 将包含一个密码学上下文句柄,该句柄用于后续的加密和解密操作。

CryptGetHashParam 函数,用于检索哈希对象的参数。哈希对象是用于计算数据摘要的对象,通常在密码学操作中使用。

以下是 CryptGetHashParam 函数的一般格式:

BOOL CryptGetHashParam(
  HCRYPTHASH hHash,
  DWORD      dwParam,
  BYTE       *pbData,
  DWORD      *pdwDataLen,
  DWORD      dwFlags
);

成功调用后,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算法的密钥生成过程包括以下步骤:

公钥是 (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
);

函数返回一个布尔值,表示是否成功生成密钥。如果成功,返回 TRUE,否则返回 FALSE

CryptExportKey 函数是 Windows Cryptographic API (CryptoAPI) 中的一个函数,用于导出密钥的原始或简单 BLOB 格式。密钥 BLOB 包含密钥的完整信息,以便在不同的系统或进程之间传输密钥。

以下是 CryptExportKey 函数的一般格式:

BOOL CryptExportKey(
  HCRYPTKEY hKey,
  HCRYPTKEY hExpKey,
  DWORD     dwBlobType,
  DWORD     dwFlags,
  BYTE      *pbData,
  DWORD     *pdwDataLen
);

函数返回一个布尔值,表示是否成功导出密钥。如果成功,返回 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
);

函数返回一个布尔值,表示是否成功导入密钥。如果成功,返回 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;
}

运行后生成公钥与私钥,并对字符串加密与解密,如下图所示;

以上就是C/C++中常用加密与解密算法的实现的详细内容,更多关于C++加密与解密算法的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文