我试图想出如何使用 MS CryptoAPI 创建会话密钥并传输到远程,以便他们可以加密通过专用网络发送的数据(所以不用担心中间人,只是数据包嗅探器)。还需要支持Windows XP。我想我已经弄清楚了大部分,并建立了一个类来做我需要的事情。但是,当我将 2604 字节的明文加密为 2608 字节的密文,然后在导入会话密钥的同一台机器(“机器 A”)上对其进行解密时,它工作正常,但是在另一台机器上(实际创建会话密钥的机器) , "Machine B") 解密该数据不能正确解密前 16 个字节(第一个块)。
我所做的是让机器 A 将公钥发送到机器 B,机器 B 创建一个随机会话密钥,然后将其导出并将其发送到机器 A,机器 A 导入该密钥。
Machine A Machine B
------------------------------------------ -------------------------------------------
Initialize(); Initialize();
CreateRandomSessionKey();
ExportPublicKey();
ExportSessionKey();
ImportSessionKey();
CalcSizeForInPlaceEncryption(4604);
[create 4608 byte buffer with plaintext]
EncryptData(); *In Place*
DecryptData(); *New Buffer*
DecryptData(); *In Place*
[Decrypted data matches.] [Decrypted data first 16 bytes invalid.]
[Input of encrypted data matchines Machine A]
这是我曾经这样做的课程:
头文件:
#include <wincrypt.h>
class CMSCryptoAPI
{
protected:
HCRYPTPROV m_hCryptProv=NULL; // crypto provider
HCRYPTKEY m_hSessionKey=NULL; // the symmetric key (session key)
DWORD m_dwSessionKeyBlockSize=0; // block size for encryption
HCRYPTKEY CreateExchangeKey();
bool ExportKey(HCRYPTKEY hkey, HCRYPTKEY hexpkey, DWORD blobtype, BYTE** pblob, DWORD* blobsize);
DWORD GetSessionKeyBlockSize();
public:
virtual ~CMSCryptoAPI();
// initialize before using class object
bool Initialize();
// uninitialize the initialization
bool Uninitialize();
bool DestroySessionKey();
bool CreateRandomSessionKey();
bool ExportSessionKey(const BYTE* publickeyblob, DWORD publickeyblobsize, BYTE** pblob, DWORD* blobsize);
bool ImportSessionKey(const BYTE* pkeyblob, DWORD keyblobsize);
bool ExportPublicKey(BYTE** pblob, DWORD* blobsize);
bool EncryptData(BYTE* ppaintext, DWORD plaintextsize, BYTE** pciphertext, DWORD* ciphertextsize);
bool DecryptData(BYTE* pciphertext, DWORD ciphertextsize, BYTE** ppaintext, DWORD *plaintextsize);
bool SetIV(BYTE* pbiv=NULL);
DWORD CalcSizeForInPlaceEncryption(DWORD plaintextsize);
};
源文件:
// Link with the Advapi32.lib file.
#pragma comment (lib, "advapi32")
#define KEYLENGTHSHIFT 16
#define KEYLENGTH 4096
//-------------------------------------------------------------------------
// Purpose: Destructor
//
// Input:
//
// Output:
//
// Notes:
//
CMSCryptoAPI::~CMSCryptoAPI()
{
Uninitialize();
}
//-------------------------------------------------------------------------
// Purpose: Initialize the object
//
// Input: na
//
// Output: true/false restult
//
// Notes: must be called first before using any of the other functions
//
bool CMSCryptoAPI::Initialize()
{
// acquire the provider to use
if (!CryptAcquireContext(&m_hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0)) {
// user may not have key set like the local system account so try machine set
if (GetLastError()==NTE_BAD_KEYSET) {
if (!CryptAcquireContext(&m_hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_MACHINE_KEYSET)) {
if (GetLastError()==NTE_BAD_KEYSET) {
if (!CryptAcquireContext(&m_hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_NEWKEYSET)) {
CDebugPrint::DebugPrint(_T("Error %u during CryptAcquireContext for new keyset!\n"), GetLastError());
return false;
}
else CDebugPrint::DebugPrint(_T("Created new keyset for CryptAcquireContext!\n"));
}
else {
CDebugPrint::DebugPrint(_T("Error %u during CryptAcquireContext for machine set!\n"), GetLastError());
return false;
}
}
else CDebugPrint::DebugPrint(_T("Using the Machine Key Set\n"));
}
else {
CDebugPrint::DebugPrint(_T("Error %u during CryptAcquireContext for user!\n"), GetLastError());
return false;
}
}
return true;
}
//-------------------------------------------------------------------------
// Purpose: Clean up object to release resources
//
// Input: na
//
// Output: true/false result
//
// Notes:
//
bool CMSCryptoAPI::Uninitialize()
{
if (DestroySessionKey()) {
// now release crypto context
if (m_hCryptProv) {
if (!CryptReleaseContext(m_hCryptProv, 0)) {
CDebugPrint::DebugPrint(_T("Error %u release context!\n"), GetLastError());
}
else m_hCryptProv=NULL;
}
}
return m_hCryptProv==NULL;
}
//-------------------------------------------------------------------------
// Purpose: Destroy the session key if it exists
//
// Input: na
//
// Output: true / false result of it being destroyed
//
// Notes: true is returned if it never existed
//
bool CMSCryptoAPI::DestroySessionKey()
{
if (m_hSessionKey) {
if (!CryptDestroyKey(m_hSessionKey)) {
CDebugPrint::DebugPrint(_T("Error %u destroying session key!\n"), GetLastError());
}
else m_hSessionKey=NULL;
}
return m_hSessionKey==NULL;
}
//-------------------------------------------------------------------------
// Purpose: Create a session key using random data
//
// Input: na
//
// Output: true/false result
//
// Notes:
//
bool CMSCryptoAPI::CreateRandomSessionKey()
{
bool result=false;
if (DestroySessionKey()) {
// Create a random session key.
if (CryptGenKey(m_hCryptProv, CALG_AES_128, CRYPT_EXPORTABLE, &m_hSessionKey)) {
// now populate IV
result=SetIV();
}
else CDebugPrint::DebugPrint(_T("Error %u creating session key\n"), GetLastError());
}
return result;
}
//-------------------------------------------------------------------------
// Purpose: Import the session key from remote
//
// Input: pkeyblob - [i] the session key blob from remote
// keyblobsize - [i] size of the session key blob
//
// Output: true/false result
//
// Notes:
//
bool CMSCryptoAPI::ImportSessionKey(const BYTE* pkeyblob, DWORD keyblobsize)
{
bool result=false;
if (DestroySessionKey()) {
// Get the handle to the exchange key.
HCRYPTKEY rsakeypair=CreateExchangeKey();
if (rsakeypair) {
// import the blob
if (CryptImportKey(m_hCryptProv, pkeyblob, keyblobsize, rsakeypair, 0, &m_hSessionKey)) {
result=true;
}
else CDebugPrint::DebugPrint(_T("Error %u importing session key\n"), GetLastError());
// clean up
if (!CryptDestroyKey(rsakeypair)) {
CDebugPrint::DebugPrint(_T("Error %u destroying exchange key for import\n"), GetLastError());
}
}
}
return result;
}
//-------------------------------------------------------------------------
// Purpose: Export the session key for a remote to use
//
// Input: publickeyblob - [i] remote systems public key
// publickeyblobsize - [i] size of the public key
// pblob - [o] the created session key blob
// blobsize - [o] size of the created session key blob
//
// Output: true/false result
//
// Notes:
//
bool CMSCryptoAPI::ExportSessionKey(const BYTE* publickeyblob, DWORD publickeyblobsize, BYTE** pblob, DWORD *blobsize)
{
// init output variables
*pblob=NULL;
*blobsize=0;
bool result=false;
// import the public key
HCRYPTKEY hremotepubickey;
if (!CryptImportKey(m_hCryptProv, publickeyblob, publickeyblobsize, NULL, AT_KEYEXCHANGE, &hremotepubickey)) {
CDebugPrint::DebugPrint(_T("Error %u importing public key blob for keyexchange\n"), GetLastError());
}
else {
// use that key to export the session key
result=ExportKey(m_hSessionKey, hremotepubickey, SIMPLEBLOB, pblob, blobsize);
// clean up
if (!CryptDestroyKey(hremotepubickey)) {
CDebugPrint::DebugPrint(_T("Error %u destroying remote public key\n"), GetLastError());
}
}
return result;
}
//-------------------------------------------------------------------------
// Purpose: Create the RSA exchange key pair
//
// Input: na
//
// Output: the key handle or NULL if problem
//
// Notes: Use CryptDestroyKey() when done with key
//
HCRYPTKEY CMSCryptoAPI::CreateExchangeKey()
{
HCRYPTKEY rsakeypair=NULL;
if (!CryptGetUserKey(m_hCryptProv, AT_KEYEXCHANGE, &rsakeypair)) {
if (GetLastError()==NTE_NO_KEY) {
// create a RSA private/public key pair
CDebugPrint::DebugPrint(_T("Create RSA Key\n"));
if (!CryptGenKey(m_hCryptProv, AT_KEYEXCHANGE, (KEYLENGTH<<KEYLENGTHSHIFT)|CRYPT_EXPORTABLE, &rsakeypair)) {
CDebugPrint::DebugPrint(_T("Error %u creating RSA Key\n"), GetLastError());
}
}
else CDebugPrint::DebugPrint(_T("Error %u getting user RSA key\n"), GetLastError());
}
return rsakeypair;
}
//-------------------------------------------------------------------------
// Purpose: Export a public key to a blob
//
// Input: pbolb - [o] the exported key blob
// blobsize - [o] size of the blob
//
// Output: true/false result
//
// Notes: The requester of a session key sends this over in order to
// receive a protected session key
//
bool CMSCryptoAPI::ExportPublicKey(BYTE** pblob, DWORD* blobsize)
{
bool result=false;
// Get the handle to the exchange key.
HCRYPTKEY rsakeypair=CreateExchangeKey();
if (rsakeypair) {
// now export the public key
result=ExportKey(rsakeypair, NULL, PUBLICKEYBLOB, pblob, blobsize);
// clean up
if (!CryptDestroyKey(rsakeypair)) {
CDebugPrint::DebugPrint(_T("Error %u destroying rsa key\n"), GetLastError());
}
}
return result;
}
//-------------------------------------------------------------------------
// Purpose: generic routine to export a key to a blob
//
// Input: hkey - [i] key to export
// hexpkey - [i] export key (remote public key)
// blobtype - [i] type of blob to create
// pblob - [o] blob created
// blobsize - [i] size of blob created
//
// Output: true/false result
//
// Notes:
//
bool CMSCryptoAPI::ExportKey(HCRYPTKEY hkey, HCRYPTKEY hexpkey, DWORD blobtype, BYTE** pblob, DWORD* blobsize)
{
bool result=false;
// get size of blob needed to export key
if (!CryptExportKey(hkey, hexpkey, blobtype, 0, NULL, blobsize)) {
CDebugPrint::DebugPrint(_T("Error %u getting size to export key with blob type %u\n"), GetLastError(), blobtype);
// ensure stays zero
*blobsize=0;
}
else {
CDebugPrint::DebugPrint(_T("Export Key blob size %u\n"), *blobsize);
// we have the size - create buffer for the exported session key
if ((*pblob=new BYTE[*blobsize])!=NULL) {
// success - now actually get the exported session key
if (!CryptExportKey(hkey, hexpkey, blobtype, 0, *pblob, blobsize)) {
CDebugPrint::DebugPrint(_T("Error %u exporting key with blob type %u\n"), GetLastError(), blobtype);
// clean up
delete[](*pblob);
// return nothing
*pblob=NULL;
*blobsize=0;
}
else result=true;
}
else {
CDebugPrint::DebugPrint(_T("Error allocating %u byte buffer to export key with blob type %u\n"), *blobsize, blobtype);
*blobsize=0;
}
}
return result;
}
//-------------------------------------------------------------------------
// Purpose: Encrypt a block of data
//
// Input: pplaintext - [i] plain text to encrypt
// plaintextsize - [i] size of plain text
// pciphertext - [o][opt] buffer with cipher data
// ciphertextsize- [io] size of cipher data
//
// Output: true/false result
//
// Notes: If pciphertext is NULL encryption is done in-place and the
// ciphertextsize gives the total size of the input buffer
//
bool CMSCryptoAPI::EncryptData(BYTE* pplaintext, DWORD plaintextsize, BYTE** pciphertext, DWORD* ciphertextsize)
{
bool result=false;
if (pciphertext) {
*ciphertextsize=0;
*pciphertext=NULL;
// Get the size of the output buffer needed
if (CryptEncrypt(m_hSessionKey, NULL, TRUE, 0, NULL, ciphertextsize, 0)) {
assert(*ciphertextsize>=plaintextsize);
// create the output buffer
if ((*pciphertext=new BYTE[*ciphertextsize])!=NULL) {
// created buffer - copy over data
memcpy(*pciphertext, pplaintext, plaintextsize);
}
}
}
else pciphertext=&pplaintext;
if (*pciphertext!=NULL) {
// do the encryption
if (CryptEncrypt(m_hSessionKey, NULL, TRUE, 0, *pciphertext, &plaintextsize, *ciphertextsize)) {
// ensure result of encryption matches size returned
assert(plaintextsize==*ciphertextsize);
result=true;
}
else CDebugPrint::DebugPrint(_T("Error %u encrypting data\n"), GetLastError());
}
else CDebugPrint::DebugPrint(_T("Unable to create buffer of %u bytes to encrypt data\n"), *ciphertextsize);
return result;
}
//-------------------------------------------------------------------------
// Purpose: Decrypt a block of data
//
// Input: pciphertext - [i] buffer with cipher data
// ciphertextsize- [i] size of cipher data
// pplaintext - [o][opt] plain text
// plaintextsize - [o] size of plain text
//
// Output: true/false result
//
// Notes: if plaintext is NULL the decryption occurs in-place
//
bool CMSCryptoAPI::DecryptData(BYTE* pciphertext, DWORD ciphertextsize, BYTE** pplaintext, DWORD* plaintextsize)
{
bool result=false;
*plaintextsize=0;
if (pplaintext) {
if ((*pplaintext=new BYTE[ciphertextsize])!=NULL) {
// copy over data
memcpy(*pplaintext, pciphertext, ciphertextsize);
}
}
else pplaintext=&pciphertext;
if (*pplaintext!=NULL) {
// do the decryption
*plaintextsize=ciphertextsize;
if (CryptDecrypt(m_hSessionKey, NULL, TRUE, 0, *pplaintext, plaintextsize)) {
result=true;
}
else CDebugPrint::DebugPrint(_T("Error %u decrypting data\n"), GetLastError());
}
else CDebugPrint::DebugPrint(_T("Unable to create buffer of %u bytes to decrypt data\n"), ciphertextsize);
return result;
}
//-------------------------------------------------------------------------
// Purpose: Setup the IV of a key
//
// Input: pbiv - [i][opt] IV data to use
//
// Output: true/false result
//
// Notes: Random data is used if pbiv is not given
//
bool CMSCryptoAPI::SetIV(BYTE* pbiv)
{
bool result=false;
// check if we put in random data or use callers value
if (pbiv==NULL) {
// get block length
DWORD dwblocklen=GetSessionKeyBlockSize();
if (dwblocklen!=0) {
// create buffer for random data
BYTE* pbtemp;
if ((pbtemp=new BYTE[dwblocklen])!=NULL) {
// create random data
if (CryptGenRandom(m_hCryptProv, dwblocklen, pbtemp)) {
// set the IV data
if (CryptSetKeyParam(m_hSessionKey, KP_IV, pbtemp, 0)) {
result=true;
}
else CDebugPrint::DebugPrint(_T("Error %u on CryptSetKeyParam KP_IV\n"), GetLastError());
}
else CDebugPrint::DebugPrint(_T("Error %u on CryptGenRandom\n"), GetLastError());
// clean up
delete[] pbtemp;
}
else CDebugPrint::DebugPrint(_T("Unable to create buffer of %u bytes for rng\n"), dwblocklen);
}
}
else {
if (CryptSetKeyParam(m_hSessionKey, KP_IV, pbiv, 0)) {
result=true;
}
else CDebugPrint::DebugPrint(_T("Error %u on CryptSetKeyParam KP_VI caller buffer\n"), GetLastError());
}
return result;
}
//-------------------------------------------------------------------------
// Purpose: Calculate size of buffer that can used for in place encryption
//
// Input: plaintextsize - [i] size of the plain text
//
// Output: size of aligned buffer that can be used for in place encryption
//
// Notes:
//
DWORD CMSCryptoAPI::CalcSizeForInPlaceEncryption(DWORD plaintextsize)
{
// get the block size
DWORD dwblocksize=GetSessionKeyBlockSize();
// align up to it
return (((plaintextsize+dwblocksize-1)/dwblocksize)*dwblocksize);
// if always a factor of 2 then
// return ((plaintextsize+dwblocksize-1) & ~(dwblocksize-1));
}
//-------------------------------------------------------------------------
// Purpose: Get the block size for the session key
//
// Input: na
//
// Output: size of aligned buffer that can be used for in place encryption
//
// Notes:
//
DWORD CMSCryptoAPI::GetSessionKeyBlockSize()
{
// get block length
DWORD dwblocklen=0;
DWORD dwdatalen=sizeof(dwblocklen);
if (CryptGetKeyParam(m_hSessionKey, KP_BLOCKLEN, (BYTE*) &dwblocklen, &dwdatalen, 0)) {
// convert bits to bytes
dwblocklen/=8;
}
else CDebugPrint::DebugPrint(_T("Error %u on CryptGetKeyParam KP_BLOCKLEN\n"), GetLastError());
return dwblocklen;
}