当前位置: 首页 > news >正文

C/C++ 常用加密与解密算法

计算机安全和数据隐私是现代应用程序设计中至关重要的方面。为了确保数据的机密性和完整性,常常需要使用加密和解密算法。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;// MD5CalculateHash((BYTE *)szBuf, strlen(szBuf), CALG_MD5, &pHashData, &dwHashDataLength);for (DWORD x = 0; x < dwHashDataLength; x++){printf("%x", pHashData[x]);}printf("\n");// SHA256CalculateHash((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;
}

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

相关文章:

C/C++ 常用加密与解密算法

计算机安全和数据隐私是现代应用程序设计中至关重要的方面。为了确保数据的机密性和完整性&#xff0c;常常需要使用加密和解密算法。C是一种广泛使用的编程语言&#xff0c;提供了许多加密和解密算法的实现。本文将介绍一些在C中常用的加密与解密算法&#xff0c;这其中包括Xo…...

从Qt源码的角度分析Qt对象树与内存管理模式

作者:令狐掌门 技术交流QQ群:675120140 csdn博客:https://mingshiqiang.blog.csdn.net/ 文章目录 一、Qt对象树(Object Tree)和父子关系二、源码角度:QObject的内存管理构造函数析构函数addChild() 和 removeChild()三、C++模拟实现Qt的对象树内存管理模式Qt框架提供了一…...

MySQL与Redis如何保证数据的一致性

文章目录 MySQL与Redis如何保证数据的一致性&#xff1f;不好的方案1. 先写 MySQL&#xff0c;再写 Redis2. 先写 Redis&#xff0c;再写 MySQL3. 先删除 Redis&#xff0c;再写 MySQL 好的方案4. 先删除 Redis&#xff0c;再写 MySQL&#xff0c;再删除 Redis5. 先写 MySQL&am…...

micropython - espnow

espnow这个东西可以很简单的进行多设备近距离互联&#xff0c;连握手都不用注册一下就能发信息 目前8266那个8角的刷20231105的1M的固件可以运行 8266目前没有信号强度功能所以我自己写的类强度返回为0 我写的类实例化后最后注册谁发消息就是给谁而接收端则是什么都接&#xff…...

京东数据采集(京东数据运营):怎样快速获取京东市场大数据?

相信京东平台的很多品牌方们都有做数据分析的需求&#xff0c;但面对多而杂的市场数据&#xff0c;很多运营者都没有思路。单依靠肉眼来看&#xff0c;很多商品的类目、销售成绩、价格分布等运营者也未必清楚。 其实对于京东平台上市场数据的获取&#xff0c;品牌可以直接借助一…...

​重生奇迹mu迷宫攻略​

重生奇迹mu迷宫是一种比较有挑战性的游戏玩法&#xff0c;需要一定的技巧和策略才能完成。以下是一些基本的攻略和技巧&#xff1a; 了解每个迷宫的特点&#xff1a;不同的迷宫有不同的规则和特点&#xff0c;需要根据迷宫的特点来制定合理的策略。在进入迷宫前可以先了解一下…...

[网络] 4. HTTP/1.1 相比 HTTP/1.0 提高了什么性能?

HTTP/1.1 相比 HTTP/1.0 性能上的改进 ● 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。 ● 支持管道&#xff08;pipeline&#xff09;网络传输&#xff0c;只要第一个请求发出去了&#xff0c;不必等其回来&#xff0c;就可以发第二个请求出去&#xff0c;可以减…...

3.1.2 Linux时间子系统 hrtimer示例使用

文章目录 结构体定义接口初始化启动修改取消示例示例1示例2示例3结构体定义 struct hrtimer {struct timerqueue_node node;ktime_t _softexpires;enum hrtimer_restart...

04 _ 系统设计目标(二):系统怎样做到高可用?

这里将探讨高并发系统设计的第二个目标——高可用性。 高可用性&#xff08;High Availability&#xff0c;HA&#xff09;是你在系统设计时经常会听到的一个名词&#xff0c;它指的是系统具备较高的无故障运行的能力。 我们在很多开源组件的文档中看到的HA方案就是提升组件可…...

Android相机性能提高50%

文章目录 应用举例&#xff08;可以不看这一part&#xff0c;直接跳过看具体怎么做&#xff09;&#xff1a;Snapchat 通过 Camera2 Extensions API 将新相机功能的集成速度提高了 50%**Camera2 扩展 API 可以访问高级功能更多设备上的更多机会 正文&#xff1a;开始使用扩展架…...

STM32F103C8T6第5天:独立看门狗、窗口看门狗、dma实验

1. 独立看门狗IWDG介绍&#xff08;341.45&#xff09; 什么是看门狗&#xff1f; 在由单片机构成的微型计算机系统中&#xff0c;由于单片机的工作常常会受到来自外界电磁场的干扰&#xff0c;造成程序的跑飞&#xff0c;而陷入死循环&#xff0c;程序的正常运行被打断&#…...

QT已有项目导入工程时注意事项

文章目录 从qt其他版本上开发的工程导入另一qt版本时 从qt其他版本上开发的工程导入另一qt版本时 这里以之前在qt5.12.2上开发的项目为例&#xff0c;现在到在qt6.5.3上运行。 不能直接导入IDE上&#xff0c;否则会报各种莫名奇妙的错误。 首先要把扩展名位.pro.user文件 删掉…...

Django视图层

一、请求与响应 视图函数 视图函数&#xff0c;简称视图&#xff0c;属于Django的视图层&#xff0c;默认定义在views.py文件中&#xff0c;是用来处理web请求信息以及返回响应信息的函数&#xff0c;所以研究视图函数只需熟练掌握两个对象即可&#xff1a;请求对象(HttpRequ…...

我在electron中集成了自己的ai大模型

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、大模型选择二、获取key三、调用api四、调用ai模型api时&#xff0c;解决跨域总结 前言 最近单位把gpt、文心一言、通义千问、星火等等等等你能想到的ai大模型都给禁掉了&#xff0c;简直丧心病狂。 不知道有多少感同…...

oracle rac环境归档日志清除

文章目录 一、处理步骤1、使用终端登录上服务器查看磁盘使用状态2、使用恢复备份管理工具RMAN删除归档日志 二、详细操作步骤三、定时任务自动清归档日志1、编写删除脚本4、测试脚本运行情况5、设置定时任务每周执行一次&#xff0c;并测试运行效果 昨天单位的所有系统都连不上…...

【数据结构】树与二叉树(廿六):树删除指定结点及其子树(算法DS)

文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲3. 搜索指定数据域的结点4. 删除结点及其左右子树a. 逻辑删除与物理删除b. 算法DSTc. 算法解析d. 代码实现递归释放树算法DS e. 算法测试 5. 代码整合…...

交叉编译 和 软硬链接 的初识(面试重点)

目录 交叉编译的初认识Q&A Q1: 编译是什么&#xff1f; Q2: 交叉编译是什么&#xff1f; Q3: 为什么要交叉编译 Q3.1&#xff1a;树莓派相对于C51大得多&#xff0c;可以集成编译器比如gcc&#xff0c;那么树莓派就不需要交叉编译了吗&#xff1f; Q4: 什么是宿主机和…...

Docker attach 命令

docker attach&#xff1a;连接到正在运行中的容器。 语法 docker attach [OPTIONS] CONTAINER要attach上去的容器必须正在运行&#xff0c;可以同时连接上同一个container来共享屏幕&#xff08;与screen命令的attach类似&#xff09;。 官方文档中说attach后可以通过CTRL-…...

Keil5个性化设置及常用快捷键

Keil5个性化设置及常用快捷键 1.概述 这篇文章是Keil工具介绍的第三篇文章&#xff0c;主要介绍下Keil5优化配置&#xff0c;以及工作中常用的快捷键提高开发效率。 第一篇&#xff1a;《安装嵌入式单片机开发环境Keil5MDK以及整合C51开发环境》https://blog.csdn.net/m0_380…...

rtsp点播异常出现‘circluar_buffer_size‘ option was set but it is xx

先说现象: 我使用potplay播放器来点播rtsp码流的时候可以点播成功&#xff0c;同事使用vlc和FFplay来点播rtsp码流的时候异常。 排查思路: 1.开始怀疑是oss账号问题&#xff0c;因为ts切片数据是保存在oss中的&#xff0c;我使用的是自己的oss账号&#xff0c;同事使用的是公司…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...

ubuntu22.04有线网络无法连接,图标也没了

今天突然无法有线网络无法连接任何设备&#xff0c;并且图标都没了 错误案例 往上一顿搜索&#xff0c;试了很多博客都不行&#xff0c;比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动&#xff0c;重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

微服务通信安全:深入解析mTLS的原理与实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言&#xff1a;微服务时代的通信安全挑战 随着云原生和微服务架构的普及&#xff0c;服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...