0

我试图想出如何使用 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;
}
4

1 回答 1

0

好吧,我想知道它是否可能是IV。我在创建会话密钥时更改为不设置它,所以它看起来像:

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(); REMOVED
      result=true;
    }
    else CDebugPrint::DebugPrint(_T("Error %u creating session key\n"), GetLastError());
  }

  return result;
}

现在它在双方都有效,但这让我想知道,我应该设置一些固定的 IV 还是 Windows 处理它,还是它总是 0 或类似的东西?

顺便说一句,为像我这样需要一些东西的人享受课程,其他人可能会帮助改进它。我倾向于使用 nothrownew.obj,但其他人可能使用异常处理(我并没有完全跟上)。

于 2021-07-17T05:58:15.703 回答