参考:
1,使用 CNG 加密数据 - Win32 apps | Microsoft Learn
2,不记得了
(下文通过cng api演示rsa加密,不做原理性介绍)
相对于aes等对称加密算法,rsa加密算法不可逆性更强。非对称加密在通常情况下,使用公钥对数据进行加密之后,如果没有私钥,基本是不可能实现解密的。在一些勒索程序中,非对称加密会经常出现;这里算是填个坑吧。
下面代码实现:
1,rsa加密
2,公钥/私钥导出
#define RsaKeyLen 2048
HRESULT bthr(BOOL b)
{
return b ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
//确实不需要直接从 BCryptExportKey 或 NCryptExportKey 进行 base64 编码输出,但需要执行额外的步骤 :
//
//使用 BCryptExportKey(或 NCryptExportKey) BCRYPT_RSAFULLPRIVATE_BLOB(但不是 BCRYPT_RSAPRIVATE_BLOB)或 BCRYPT_RSAPUBLIC_BLOB
//对生成的 BCRYPT_RSAKEY_BLOB 进行编码 CNG_RSA_PRIVATE_KEY_BLOB 或 CNG_RSA_PUBLIC_KEY_BLOB 并放入 CRYPT_PRIVATE_KEY_INFO
//使用 PKCS_PRIVATE_KEY_INFO 对 CRYPT_PRIVATE_KEY_INFO 进行编码 >
//调用CryptBinaryToStringA
HRESULT ExportToPem(_In_ BCRYPT_KEY_HANDLE hKey, BOOL bPrivate, _Out_ PSTR* ppsz, _Out_ PULONG pcch)
{
HRESULT hr;
CRYPT_PRIVATE_KEY_INFO PrivateKeyInfo = { 0, {const_cast<PSTR>(szOID_RSA_RSA)} };
CERT_PUBLIC_KEY_INFO PublicKeyInfo = { {const_cast<PSTR>(szOID_RSA_RSA)} };
ULONG cbKey = 0;
PUCHAR pbKey = 0;//really PBCRYPT_RSAKEY_BLOB
PCWSTR pszBlobType;
PCSTR lpszStructType;
if (bPrivate)
{
pszBlobType = BCRYPT_RSAFULLPRIVATE_BLOB;
lpszStructType = CNG_RSA_PRIVATE_KEY_BLOB;
}
else
{
pszBlobType = BCRYPT_RSAPUBLIC_BLOB;
lpszStructType = CNG_RSA_PUBLIC_KEY_BLOB;
}
while (0 <= (hr = BCryptExportKey(hKey, 0, pszBlobType, pbKey, cbKey, &cbKey, 0)))
{
if (pbKey)
{
if (bPrivate)
{
if (0 <= (hr = bthr(CryptEncodeObjectEx(
X509_ASN_ENCODING,
lpszStructType,
pbKey,
CRYPT_ENCODE_ALLOC_FLAG,
0,
&PrivateKeyInfo.PrivateKey.pbData,
&PrivateKeyInfo.PrivateKey.cbData))))
{
hr = bthr(CryptEncodeObjectEx(
X509_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
&PrivateKeyInfo,
CRYPT_ENCODE_ALLOC_FLAG,
0,
&pbKey,
&cbKey));
LocalFree(PrivateKeyInfo.PrivateKey.pbData);
}
}
else
{
//public key
if (0 <= (hr = bthr(CryptEncodeObjectEx(
X509_ASN_ENCODING,
lpszStructType,
pbKey,
CRYPT_ENCODE_ALLOC_FLAG,
0,
&PublicKeyInfo.PublicKey.pbData,
&PublicKeyInfo.PublicKey.cbData))))
{
hr = bthr(CryptEncodeObjectEx(
X509_ASN_ENCODING,
X509_PUBLIC_KEY_INFO,
&PublicKeyInfo,
CRYPT_ENCODE_ALLOC_FLAG,
0,
&pbKey,
&cbKey));
LocalFree(PublicKeyInfo.PublicKey.pbData);
}
}
if (0 <= hr)
{
PSTR psz = 0;
ULONG cch = 0;
while (0 <= (hr = bthr(CryptBinaryToStringA(
pbKey,
cbKey,
CRYPT_STRING_BASE64HEADER, //CRYPT_STRING_BASE64HEADER without the "---------BEGIN CERTIFICATE---------"
psz,
&cch))))
{
if (psz)
{
*ppsz = psz, * pcch = cch;
break;
}
if (!(psz = (PSTR)LocalAlloc(0, cch)))
{
hr = HRESULT_FROM_WIN32(GetLastError());
break;
}
}
LocalFree(pbKey);
}
break;
}
pbKey = (PUCHAR)malloc(cbKey);
}
return hr;
}
BOOL DecryptAnd(BCRYPT_KEY_HANDLE hKey, PUCHAR pbData, ULONG cbData)
{
UCHAR buf[0x1000] = { 0 };
ULONG pbcResult = 0;
NTSTATUS status = 0;
if (!NT_SUCCESS(BCryptDecrypt(
hKey,
pbData,
cbData,
NULL,
NULL,
0,
buf,
0x1000,
&pbcResult,
BCRYPT_PAD_PKCS1)))
{
wprintf(L"BCryptDecrypt fail,error:%d\n", GetLastError());
goto end;
}
//print cipher text
PrintBytes(buf, pbcResult);
end:
return status == 0 ? TRUE : FALSE;
}
BOOL EncryptAnd(BCRYPT_KEY_HANDLE hKey, PUCHAR pbData, ULONG cbData)
{
UCHAR buf[0x1000] = { 0 };
ULONG pbcResult = 0;
NTSTATUS status = 0;
printf("plain text length:%d\n", cbData);
if (!NT_SUCCESS(BCryptEncrypt(
hKey,
pbData,
cbData,
NULL,
NULL,
0,
buf,
0x1000,
&pbcResult,
BCRYPT_PAD_PKCS1)))
{
wprintf(L"BCryptEncrypt fail,error:%d\n", GetLastError());
goto end;
}
//print cipher text
printf("cipher text length:%d\n", pbcResult);
PrintBytes(buf, pbcResult);
printf("begin to decrypt \n");
//begin to decrypt
DecryptAnd(hKey, buf, pbcResult);
end:
return status == 0 ? TRUE : FALSE;
}
void UnsymmetricEncrypt()
{
NTSTATUS status =0;
BCRYPT_ALG_HANDLE hAlgRsa=NULL;
BCRYPT_KEY_HANDLE hRsaKey=NULL;
DWORD rsaPublicKeyLen = (RsaKeyLen) * 2;
UCHAR rsaPublicKey[(RsaKeyLen) * 2] = { 0 };
UCHAR rsaPrivateKey[(RsaKeyLen) * 2] = { 0 };
ULONG cbResult = 0;
PSTR PublicKey = NULL;
PSTR PrivateKey = NULL;
UCHAR plainText[0x20] = "abcdefghijklm";
//opne rsa algorithm provider
if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
&hAlgRsa,
BCRYPT_RSA_ALGORITHM,
NULL,
0)))
{
wprintf(L"BCryptOpenAlgorithmProvider fail,error:%d\n", GetLastError());
goto end;
}
//generate key pair
if (!NT_SUCCESS(status = BCryptGenerateKeyPair(
hAlgRsa,
&hRsaKey,
RsaKeyLen,
0
)))
{
wprintf(L"BCryptGenerateKeyPair fail,error:%d\n", GetLastError());
goto end;
}
//finalize
if (!NT_SUCCESS(status = BCryptFinalizeKeyPair(
hRsaKey,
0)))
{
wprintf(L"BCryptFinalizeKeyPair fail,error:%d\n",GetLastError());
goto end;
}
//encrypt and decrypt
EncryptAnd(hRsaKey, plainText, 13);
printf("\n");
if (!NT_SUCCESS(status = BCryptExportKey(
hRsaKey,
NULL,
BCRYPT_RSAPUBLIC_BLOB,
rsaPublicKey,
rsaPublicKeyLen,
&cbResult,
0)))
{
wprintf(L"BCryptExportKey fail,error:%d\n", GetLastError());
goto end;
}
else
{
//printf public key
//0x52, 0x53, 0x41, 0x31, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
//0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xb4, 0x7a, 0xb0, 0x43, 0xee,
//0x48, 0xbf, 0x53, 0x05, 0x60, 0xe3, 0x0e, 0xd9, 0xe2, 0x52, 0xa3, 0xb2, 0xef, 0x08, 0x20, 0xef,
//0xe4, 0x39, 0x40, 0xb7, 0xf9, 0xf3, 0x72, 0xe5, 0xf7, 0x3c, 0x25, 0x7e, 0x88, 0xe8, 0xe8, 0x9a,
//0x13, 0x67, 0xca, 0x5d, 0x45, 0xd6, 0x33, 0x6f, 0x9d, 0xda, 0xcc, 0xae, 0x07, 0x44, 0x2c, 0x11,
//0x5c, 0x82, 0xdf, 0xe9, 0x87, 0xd9, 0xf0, 0xcd, 0xb7, 0xb2, 0xc1,
printf("rsa public key blob:\n");
PrintBytes(rsaPublicKey, cbResult);
//if (S_OK != ExportToPem(
// hRsaKey,
// TRUE,
// &PrivateKey,
// &cbResult))
//{
// wprintf(L"ExportToPem PrivateKey fail!\n");
// goto end;
//}
if (S_OK != ExportToPem(
hRsaKey,
FALSE,
&PublicKey,
&cbResult))
{
wprintf(L"ExportToPem PrivateKey fail!\n");
goto end;
}
printf("\npublic key in pem:\n%s\n", PublicKey);
//printf("private key in pem:\n%s\n", PrivateKey);
}
wprintf(L"\n UnsymmetricEncrypt success!\n");
end:
if (hAlgRsa)
{
BCryptCloseAlgorithmProvider(hAlgRsa, 0);
}
if (hRsaKey)
{
BCryptDestroyKey(hRsaKey);
}
}
运行结果如下图:
第一次运行
第二次运行
通过运行结果可以看出,调用系统cng api 每次生成的密钥对都不一样,这就很ok。从表面上来看,非对称加密算法比对称加密算法多一个密钥,这就更安全一点。