Microsoft KB Archive/228786

= How to export and import plain text session keys by using CryptoAPI =

Article ID: 228786

Article Last Modified on 11/21/2006

-

APPLIES TO

 Microsoft Win32 Application Programming Interface, when used with:  Microsoft Windows 95

 Microsoft Windows 98 Standard Edition

 Microsoft Windows Millennium Edition

 Microsoft Windows NT 4.0</li></ul>

 Microsoft Windows 2000 Standard Edition</li></ul>

 Microsoft Windows XP Professional</li></ul> </li></ul>

-

<div class="notice_section">

This article was previously published under Q228786

<div class="summary_section">

SUMMARY
Sometimes it is convenient to export and import plain text session keys. However, the Microsoft Cryptographic Providers (Base and Enhanced) do not support this feature. Both CryptExportKey and CryptImportKey require a valid key handle to encrypt and decrypt the session key, respectively. But by using an "exponent-of-one" private key the same effect can be achieved to encrypt and decrypt the session key.

<div class="moreinformation_section">

MORE INFORMATION
Because the key exponent is one, both the encryption and decryption do nothing to the plain text, and thus essentially leave the session key in plain text.

The following sample code illustrates how to implement this feature:
 * 1) include <windows.h>
 * 2) include <wincrypt.h>
 * 3) include <stdio.h>

BOOL CreatePrivateExponentOneKey(LPTSTR szProvider,                                 DWORD dwProvType,                                  LPTSTR szContainer,                                 DWORD dwKeySpec,                                 HCRYPTPROV *hProv,                                  HCRYPTKEY *hPrivateKey);

BOOL GenerateSessionKeyWithAlgorithm(HCRYPTPROV hProv,                                     ALG_ID Alg,                                     HCRYPTKEY *hSessionKey);

BOOL DeriveSessionKeyWithAlgorithm(HCRYPTPROV hProv,                                   ALG_ID Alg,                                   LPBYTE lpHashingData,                                   DWORD dwHashingData,                                   HCRYPTKEY *hSessionKey);

BOOL ExportPlainSessionBlob(HCRYPTKEY hPublicKey,                           HCRYPTKEY hSessionKey,                            LPBYTE *pbKeyMaterial,                            DWORD *dwKeyMaterial);

BOOL ImportPlainSessionBlob(HCRYPTPROV hProv,                           HCRYPTKEY hPrivateKey,                            ALG_ID dwAlgId,                            LPBYTE pbKeyMaterial,                            DWORD dwKeyMaterial,                            HCRYPTKEY *hSessionKey);

void main {  HCRYPTPROV hProv = 0; HCRYPTKEY hPubPrivKey = 0; HCRYPTKEY hSessionKey = 0; BOOL fResult; LPBYTE pbKeyMaterial = NULL; DWORD dwKeyMaterial ; int n;

__try {     printf("Creating Exponent of One Private Key.\n\n");

// Create Exponent of One private key fResult = CreatePrivateExponentOneKey(MS_ENHANCED_PROV, PROV_RSA_FULL,                                           "TestContainer", AT_KEYEXCHANGE,                                             &hProv, &hPubPrivKey); if (!fResult) {        printf("CreatePrivateExponentOneKey failed with %x\n", GetLastError); __leave; }

// Allocate memory for 3DES key and // Fill key with data 1,2,3,... in this case pbKeyMaterial = (LPBYTE)LocalAlloc(LPTR, 192/8); for (n = 0; n < 192/8; n++) pbKeyMaterial[n] = n+1; dwKeyMaterial = 192/8;

printf("\nImporting 3DES key with key material 1,2,3,...\n");

// Import this key and get an HCRYPTKEY handle if (!ImportPlainSessionBlob(hProv, hPubPrivKey, CALG_3DES, pbKeyMaterial, dwKeyMaterial, &hSessionKey)) {        printf("ImportPlainSessionBlob failed with %x\n", GetLastError); __leave; }

LocalFree(pbKeyMaterial); pbKeyMaterial = NULL;

printf("Exporting 3DES key. We should get back 1,2,3,...\n"); // Export it and get the key material back // It should be 1,2,3, ... fResult = ExportPlainSessionBlob(hPubPrivKey, hSessionKey,                                       &pbKeyMaterial, &dwKeyMaterial ); if (!fResult) {        printf("ExportPlainSessionBlob failed with %x\n", GetLastError); __leave; }     // Print Key material printf("3DES session key:\n"); for (n = 0; n < dwKeyMaterial; n++) {        if (n%8 == 0) printf(" "); printf("%02x", pbKeyMaterial[n]); }     printf("\n"); }  __finally {     if (pbKeyMaterial ) LocalFree(pbKeyMaterial ); if (hSessionKey) CryptDestroyKey(hSessionKey); if (hPubPrivKey) CryptDestroyKey(hPubPrivKey); if (hProv) {          CryptReleaseContext(hProv, 0); CryptAcquireContext(&hProv, "TestContainer", MS_ENHANCED_PROV,                                    PROV_RSA_FULL, CRYPT_DELETEKEYSET); }  } }

BOOL CreatePrivateExponentOneKey(LPTSTR szProvider,                                 DWORD dwProvType,                                 LPTSTR szContainer,                                 DWORD dwKeySpec,                                 HCRYPTPROV *hProv,                                  HCRYPTKEY *hPrivateKey) {  BOOL fReturn = FALSE; BOOL fResult; int n;  LPBYTE keyblob = NULL; DWORD dwkeyblob; DWORD dwBitLen; BYTE *ptr;

__try {     *hProv = 0; *hPrivateKey = 0;

if ((dwKeySpec != AT_KEYEXCHANGE) && (dwKeySpec != AT_SIGNATURE)) __leave;

// Try to create new container fResult = CryptAcquireContext(hProv, szContainer, szProvider,                                    dwProvType, CRYPT_NEWKEYSET); if (!fResult) {        // If the container exists, open it         if (GetLastError == NTE_EXISTS) {           fResult = CryptAcquireContext(hProv, szContainer, szProvider, dwProvType, 0); if (!fResult) {              // No good, leave __leave; }        }         else {           // No good, leave __leave; }     }

// Generate the private key fResult = CryptGenKey(*hProv, dwKeySpec, CRYPT_EXPORTABLE, hPrivateKey); if (!fResult) __leave;

// Export the private key, we'll convert it to a private // exponent of one key fResult = CryptExportKey(*hPrivateKey, 0, PRIVATEKEYBLOB, 0, NULL, &dwkeyblob); if (!fResult) __leave;

keyblob = (LPBYTE)LocalAlloc(LPTR, dwkeyblob); if (!keyblob) __leave;

fResult = CryptExportKey(*hPrivateKey, 0, PRIVATEKEYBLOB, 0, keyblob, &dwkeyblob); if (!fResult) __leave;

CryptDestroyKey(*hPrivateKey); *hPrivateKey = 0;

// Get the bit length of the key memcpy(&dwBitLen, &keyblob[12], 4);

// Modify the Exponent in Key BLOB format // Key BLOB format is documented in SDK

// Convert pubexp in rsapubkey to 1 ptr = &keyblob[16]; for (n = 0; n < 4; n++) {        if (n == 0) ptr[n] = 1; else ptr[n] = 0; }

// Skip pubexp ptr += 4; // Skip modulus, prime1, prime2 ptr += (dwBitLen/8); ptr += (dwBitLen/16); ptr += (dwBitLen/16);

// Convert exponent1 to 1 for (n = 0; n < (dwBitLen/16); n++) {        if (n == 0) ptr[n] = 1; else ptr[n] = 0; }

// Skip exponent1 ptr += (dwBitLen/16);

// Convert exponent2 to 1 for (n = 0; n < (dwBitLen/16); n++) {        if (n == 0) ptr[n] = 1; else ptr[n] = 0; }

// Skip exponent2, coefficient ptr += (dwBitLen/16); ptr += (dwBitLen/16);

// Convert privateExponent to 1 for (n = 0; n < (dwBitLen/8); n++) {        if (n == 0) ptr[n] = 1; else ptr[n] = 0; }     // Import the exponent-of-one private key. if (!CryptImportKey(*hProv, keyblob, dwkeyblob, 0, 0, hPrivateKey)) {                         __leave; }

fReturn = TRUE; }  __finally {     if (keyblob) LocalFree(keyblob);

if (!fReturn) {        if (*hPrivateKey) CryptDestroyKey(*hPrivateKey); if (*hProv) CryptReleaseContext(*hProv, 0); }  }

return fReturn; }

BOOL GenerateSessionKeyWithAlgorithm(HCRYPTPROV hProv,                                     ALG_ID Alg,                                     HCRYPTKEY *hSessionKey) {     BOOL fResult;

*hSessionKey = 0;

fResult = CryptGenKey(hProv, Alg, CRYPT_EXPORTABLE, hSessionKey); if (!fResult) {     return FALSE; }  return TRUE; }

BOOL DeriveSessionKeyWithAlgorithm(HCRYPTPROV hProv,                                   ALG_ID Alg,                                   LPBYTE lpHashingData,                                   DWORD dwHashingData,                                   HCRYPTKEY *hSessionKey) {  BOOL fResult; BOOL fReturn = FALSE; HCRYPTHASH hHash = 0;

__try {     *hSessionKey = 0;

fResult = CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash); if (!fResult) __leave;

fResult = CryptHashData(hHash, lpHashingData, dwHashingData, 0); if (!fResult) __leave;

fResult = CryptDeriveKey(hProv, Alg, hHash, CRYPT_EXPORTABLE, hSessionKey); if (!fResult) __leave;

fReturn = TRUE; }  __finally {           if (hHash) CryptDestroyHash(hHash); }

return fReturn; }

BOOL ExportPlainSessionBlob(HCRYPTKEY hPublicKey,                           HCRYPTKEY hSessionKey,                            LPBYTE *pbKeyMaterial,                            DWORD *dwKeyMaterial ) {  BOOL fReturn = FALSE; BOOL fResult; DWORD dwSize, n;  LPBYTE pbSessionBlob = NULL; DWORD dwSessionBlob; LPBYTE pbPtr;

__try {     *pbKeyMaterial  = NULL; *dwKeyMaterial = 0;

fResult = CryptExportKey(hSessionKey, hPublicKey, SIMPLEBLOB,                              0, NULL, &dwSessionBlob ); if (!fResult) __leave;

pbSessionBlob = (LPBYTE)LocalAlloc(LPTR, dwSessionBlob ); if (!pbSessionBlob) __leave;

fResult = CryptExportKey(hSessionKey, hPublicKey, SIMPLEBLOB,                              0, pbSessionBlob, &dwSessionBlob ); if (!fResult) __leave;

// Get session key size in bits dwSize = sizeof(DWORD); fResult = CryptGetKeyParam(hSessionKey, KP_KEYLEN, (LPBYTE)dwKeyMaterial, &dwSize, 0); if (!fResult) __leave;

// Get the number of bytes and allocate buffer *dwKeyMaterial /= 8; *pbKeyMaterial = (LPBYTE)LocalAlloc(LPTR, *dwKeyMaterial); if (!*pbKeyMaterial) __leave;

// Skip the header pbPtr = pbSessionBlob; pbPtr += sizeof(BLOBHEADER); pbPtr += sizeof(ALG_ID);

// We are at the beginning of the key // but we need to start at the end since // it's reversed pbPtr += (*dwKeyMaterial - 1); // Copy the raw key into our return buffer for (n = 0; n < *dwKeyMaterial; n++) {        (*pbKeyMaterial)[n] = *pbPtr; pbPtr--; }           fReturn = TRUE; }  __finally {     if (pbSessionBlob) LocalFree(pbSessionBlob);

if ((!fReturn) && (*pbKeyMaterial )) {        LocalFree(*pbKeyMaterial ); *pbKeyMaterial = NULL; *dwKeyMaterial = 0; }  }

return fReturn; }

BOOL ImportPlainSessionBlob(HCRYPTPROV hProv,                           HCRYPTKEY hPrivateKey,                            ALG_ID dwAlgId,                            LPBYTE pbKeyMaterial,                            DWORD dwKeyMaterial ,                            HCRYPTKEY *hSessionKey) {  BOOL fResult; BOOL fReturn = FALSE; BOOL fFound = FALSE; LPBYTE pbSessionBlob = NULL; DWORD dwSessionBlob, dwSize, n;  DWORD dwPublicKeySize; DWORD dwProvSessionKeySize; ALG_ID dwPrivKeyAlg; LPBYTE pbPtr; DWORD dwFlags = CRYPT_FIRST; PROV_ENUMALGS_EX ProvEnum; HCRYPTKEY hTempKey = 0;

__try {     // Double check to see if this provider supports this algorithm // and key size do     { dwSize = sizeof(ProvEnum); fResult = CryptGetProvParam(hProv, PP_ENUMALGS_EX, (LPBYTE)&ProvEnum,                                    &dwSize, dwFlags); if (!fResult) break;

dwFlags = 0;

if (ProvEnum.aiAlgid == dwAlgId) fFound = TRUE; } while (!fFound);

if (!fFound) __leave;

// We have to get the key size(including padding) // from an HCRYPTKEY handle. PP_ENUMALGS_EX contains // the key size without the padding so we can't use it. fResult = CryptGenKey(hProv, dwAlgId, 0, &hTempKey); if (!fResult) __leave; dwSize = sizeof(DWORD); fResult = CryptGetKeyParam(hTempKey, KP_KEYLEN, (LPBYTE)&dwProvSessionKeySize,                                &dwSize, 0); if (!fResult) __leave; CryptDestroyKey(hTempKey); hTempKey = 0;

// Our key is too big, leave if ((dwKeyMaterial * 8) > dwProvSessionKeySize) __leave;

// Get private key's algorithm dwSize = sizeof(ALG_ID); fResult = CryptGetKeyParam(hPrivateKey, KP_ALGID, (LPBYTE)&dwPrivKeyAlg, &dwSize, 0); if (!fResult) __leave;

// Get private key's length in bits dwSize = sizeof(DWORD); fResult = CryptGetKeyParam(hPrivateKey, KP_KEYLEN, (LPBYTE)&dwPublicKeySize, &dwSize, 0); if (!fResult) __leave;

// calculate Simple blob's length dwSessionBlob = (dwPublicKeySize/8) + sizeof(ALG_ID) + sizeof(BLOBHEADER);

// allocate simple blob buffer pbSessionBlob = (LPBYTE)LocalAlloc(LPTR, dwSessionBlob); if (!pbSessionBlob) __leave;

pbPtr = pbSessionBlob;

// SIMPLEBLOB Format is documented in SDK // Copy header to buffer ((BLOBHEADER *)pbPtr)->bType = SIMPLEBLOB; ((BLOBHEADER *)pbPtr)->bVersion = 2; ((BLOBHEADER *)pbPtr)->reserved = 0; ((BLOBHEADER *)pbPtr)->aiKeyAlg = dwAlgId; pbPtr += sizeof(BLOBHEADER);

// Copy private key algorithm to buffer *((DWORD *)pbPtr) = dwPrivKeyAlg; pbPtr += sizeof(ALG_ID);

// Place the key material in reverse order for (n = 0; n < dwKeyMaterial; n++) {        pbPtr[n] = pbKeyMaterial[dwKeyMaterial-n-1]; }     // 3 is for the first reserved byte after the key material + the 2 reserved bytes at the end. dwSize = dwSessionBlob - (sizeof(ALG_ID) + sizeof(BLOBHEADER) + dwKeyMaterial + 3); pbPtr += (dwKeyMaterial+1);

// Generate random data for the rest of the buffer // (except that last two bytes) fResult = CryptGenRandom(hProv, dwSize, pbPtr); if (!fResult) __leave;

for (n = 0; n < dwSize; n++) {        if (pbPtr[n] == 0) pbPtr[n] = 1; }

pbSessionBlob[dwSessionBlob - 2] = 2;

fResult = CryptImportKey(hProv, pbSessionBlob, dwSessionBlob,                               hPrivateKey, CRYPT_EXPORTABLE, hSessionKey); if (!fResult) __leave;

fReturn = TRUE; }  __finally {     if (hTempKey) CryptDestroyKey(hTempKey); if (pbSessionBlob) LocalFree(pbSessionBlob); }  return fReturn; }

<div class="references_section">