725 lines
24 KiB
C++
725 lines
24 KiB
C++
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
certupgr.cxx
|
|
|
|
Abstract:
|
|
|
|
Functions used in upgrading server certs from K2 [server cert in metabase] to
|
|
Avalanche [server cert in CAPI store].
|
|
|
|
Author:
|
|
|
|
Alex Mallet (amallet) 07-Dec-1997
|
|
Boyd Multerer (boydm) 20-Jan-1998 Converted to be useful in setup
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
#include <objbase.h>
|
|
|
|
#ifndef _CHICAGO_
|
|
|
|
|
|
#include "oidenc.h"
|
|
|
|
|
|
// keyring include
|
|
//#include "intrlkey.h"
|
|
// This stuff below is moved from above include
|
|
#define REQUEST_HEADER_K2B2VERSION 0x0101
|
|
|
|
#define REQUEST_HEADER_IDENTIFIER 'RHDR'
|
|
#define REQUEST_HEADER_CURVERSION 0x0101
|
|
|
|
|
|
|
|
typedef struct _KeyRequestHeader
|
|
{
|
|
DWORD Identifier; // must be 'RHDR'
|
|
DWORD Version; // version of header record
|
|
DWORD cbSizeOfHeader; // byte count of header. Afterwards is the request.
|
|
DWORD cbRequestSize; // size of the request that follows
|
|
BOOL fReqSentToOnlineCA;
|
|
LONG longRequestID;
|
|
BOOL fWaitingForApproval;
|
|
char chCA[MAX_PATH];
|
|
} KeyRequestHeader, *LPREQUEST_HEADER;
|
|
///--- end of #include "intrlkey.h"
|
|
|
|
//
|
|
//Local includes
|
|
//
|
|
#include "certupgr.h"
|
|
//#include "certtools.h"
|
|
|
|
|
|
// The below define is in some interal schannel header file. John Banes
|
|
// told me to just redefine it below as such........ - Boyd
|
|
LPCSTR SGC_KEY_SALT = "SGCKEYSALT";
|
|
|
|
|
|
// prototypes
|
|
BOOL DecodeAndImportPrivateKey( PBYTE pbEncodedPrivateKey IN,
|
|
DWORD cbEncodedPrivateKey IN,
|
|
PCHAR pszPassword IN,
|
|
PWCHAR pszKeyContainerIN,
|
|
CRYPT_KEY_PROV_INFO *pCryptKeyProvInfo );
|
|
BOOL UpdateCSPInfo( PCCERT_CONTEXT pcCertContext );
|
|
|
|
|
|
BOOL FImportAndStoreRequest( PCCERT_CONTEXT pCert, PVOID pbPKCS10req, DWORD cbPKCS10req );
|
|
|
|
//-------------------------------------------------------------------------
|
|
PCCERT_CONTEXT CopyKRCertToCAPIStore_A( PVOID pbPrivateKey, DWORD cbPrivateKey,
|
|
PVOID pbPublicKey, DWORD cbPublicKey,
|
|
PVOID pbPKCS10req, DWORD cbPKCS10req,
|
|
PCHAR pszPassword,
|
|
PCHAR pszCAPIStore,
|
|
BOOL bOverWrite)
|
|
{
|
|
PCCERT_CONTEXT pCert = NULL;
|
|
|
|
// prep the wide strings
|
|
PWCHAR pszwCAPIStore = NULL;
|
|
DWORD lenStore = (strlen(pszCAPIStore)+1) * sizeof(WCHAR);
|
|
pszwCAPIStore = (PWCHAR)GlobalAlloc( GPTR, lenStore );
|
|
if ( !pszwCAPIStore )
|
|
goto cleanup;
|
|
|
|
// convert the strings
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pszCAPIStore, -1, pszwCAPIStore, lenStore );
|
|
|
|
// do the real call
|
|
pCert = CopyKRCertToCAPIStore_W(
|
|
pbPrivateKey, cbPrivateKey,
|
|
pbPublicKey, cbPublicKey,
|
|
pbPKCS10req, cbPKCS10req,
|
|
pszPassword,
|
|
pszwCAPIStore,
|
|
bOverWrite);
|
|
|
|
cleanup:
|
|
// preserve the last error state
|
|
DWORD err = GetLastError();
|
|
|
|
// clean up the strings
|
|
if ( pszwCAPIStore )
|
|
GlobalFree( pszwCAPIStore );
|
|
|
|
// reset the last error state
|
|
SetLastError( err );
|
|
|
|
// return the cert
|
|
return pCert;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Copies an old Key-Ring style cert to the CAPI store. This cert comes in as two binaries and a password.
|
|
PCCERT_CONTEXT CopyKRCertToCAPIStore_W( PVOID pbPrivateKey, DWORD cbPrivateKey,
|
|
PVOID pbPublicKey, DWORD cbPublicKey,
|
|
PVOID pbPKCS10req, DWORD cbPKCS10req,
|
|
PCHAR pszPassword,
|
|
PWCHAR pszCAPIStore,
|
|
BOOL bOverWrite)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Upgrades K2 server certs to Avalanche server certs - reads server cert out of K2
|
|
metabase, creates cert context and stores it in CAPI2 "MY" store and writes
|
|
relevant information back to metabase.
|
|
|
|
Arguments:
|
|
|
|
pMDObject - pointer to Metabase object
|
|
pszOldMBPath - path to where server cert is stored in old MB, relative to SSL_W3_KEYS_MD_PATH
|
|
pszNewMBPath - fully qualified path to where server cert info should be stored in new MB
|
|
|
|
Returns:
|
|
|
|
BOOL indicating success/failure
|
|
|
|
--*/
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
|
|
HCERTSTORE hStore = NULL;
|
|
PCCERT_CONTEXT pcCertContext = NULL;
|
|
LPOLESTR polestr = NULL;
|
|
|
|
|
|
// start by opening the CAPI store that we will be saving the certificate into
|
|
hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM,
|
|
0,
|
|
NULL,
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
|
pszCAPIStore );
|
|
if ( !hStore )
|
|
{
|
|
// iisDebugOut((_T("Error 0x%x calling CertOpenStore \n"), GetLastError());
|
|
goto EndUpgradeServerCert;
|
|
}
|
|
|
|
|
|
// at this point we check to see if a certificate was passed in. If none was, then we need
|
|
// to create a dummy-temporary certificate that markes the private key as incomplete. That
|
|
// way, then the real certificate comes back from verisign the regular tools can be used
|
|
// to complete the key.
|
|
//CertCreateSelfSignCertificate()
|
|
|
|
|
|
//
|
|
//Create cert context to be stored in CAPI store
|
|
//
|
|
pbPublicKey = (PVOID)((PBYTE)pbPublicKey + CERT_DER_PREFIX);
|
|
cbPublicKey -= CERT_DER_PREFIX;
|
|
pcCertContext = CertCreateCertificateContext( X509_ASN_ENCODING, (PUCHAR)pbPublicKey, cbPublicKey);
|
|
if ( pcCertContext )
|
|
{
|
|
|
|
// the private key gets stored in a seperate location from the certificate and gets referred to
|
|
// by the certificate. We should try to pick a unique name so that some other cert won't step
|
|
// on it by accident. There is no formal format for this name whatsoever. Some groups use a
|
|
// human-readable string, some use a hash of the cert, and some use a GUID string. All are valid
|
|
// although for generated certs the hash or the GUID are probably better.
|
|
|
|
// get the 128 big md5 hash of the cert for the name
|
|
DWORD dwHashSize;
|
|
BOOL fHash;
|
|
|
|
BYTE MD5Hash[16]; // give it some extra size
|
|
dwHashSize = sizeof(MD5Hash);
|
|
fHash = CertGetCertificateContextProperty( pcCertContext,
|
|
CERT_MD5_HASH_PROP_ID,
|
|
(VOID *) MD5Hash,
|
|
&dwHashSize );
|
|
|
|
// Since the MD5 hash is the same size as a guid, we can use the guid utilities to make a
|
|
// nice string out of it.
|
|
HRESULT hresult;
|
|
hresult = StringFromCLSID( (REFCLSID)MD5Hash, &polestr );
|
|
|
|
//
|
|
// Now decode private key blob and import it into CAPI1 private key
|
|
//
|
|
CRYPT_KEY_PROV_INFO CryptKeyProvInfo;
|
|
|
|
if ( DecodeAndImportPrivateKey( (PUCHAR)pbPrivateKey, cbPrivateKey, pszPassword,
|
|
polestr, &CryptKeyProvInfo ) )
|
|
{
|
|
//
|
|
// Add the private key to the cert context
|
|
//
|
|
BOOL f;
|
|
f = CertSetCertificateContextProperty( pcCertContext, CERT_KEY_PROV_INFO_PROP_ID,
|
|
0, &CryptKeyProvInfo );
|
|
f = UpdateCSPInfo( pcCertContext );
|
|
if ( f )
|
|
{
|
|
//
|
|
// Store it in the provided store
|
|
//
|
|
if (bOverWrite)
|
|
{
|
|
if ( CertAddCertificateContextToStore( hStore, pcCertContext,
|
|
CERT_STORE_ADD_REPLACE_EXISTING, NULL ) )
|
|
{
|
|
fSuccess = TRUE;
|
|
|
|
// Write out the original request as a property on the cert
|
|
FImportAndStoreRequest( pcCertContext, pbPKCS10req, cbPKCS10req );
|
|
}
|
|
else
|
|
{
|
|
// iisDebugOut((_T("Error 0x%x calling CertAddCertificateContextToStore"), GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( CertAddCertificateContextToStore( hStore, pcCertContext,
|
|
CERT_STORE_ADD_NEW, NULL ) )
|
|
{
|
|
fSuccess = TRUE;
|
|
|
|
// Write out the original request as a property on the cert
|
|
FImportAndStoreRequest( pcCertContext, pbPKCS10req, cbPKCS10req );
|
|
}
|
|
else
|
|
{
|
|
// iisDebugOut((_T("Error 0x%x calling CertAddCertificateContextToStore"), GetLastError());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// iisDebugOut((_T("Error 0x%x calling CertSetCertificateContextProperty"), GetLastError());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// iisDebugOut((_T("Error 0x%x calling CertCreateCertificateContext"), GetLastError());
|
|
}
|
|
|
|
//
|
|
//Cleanup that's done only on failure
|
|
//
|
|
if ( !fSuccess )
|
|
{
|
|
if ( pcCertContext )
|
|
{
|
|
CertFreeCertificateContext( pcCertContext );
|
|
}
|
|
pcCertContext = NULL;
|
|
}
|
|
|
|
EndUpgradeServerCert:
|
|
// cleanup
|
|
if ( hStore )
|
|
CertCloseStore ( hStore, 0 );
|
|
|
|
if ( polestr )
|
|
CoTaskMemFree( polestr );
|
|
|
|
|
|
// return the answer
|
|
return pcCertContext;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
BOOL UpdateCSPInfo( PCCERT_CONTEXT pcCertContext )
|
|
{
|
|
BYTE cbData[1000];
|
|
CRYPT_KEY_PROV_INFO* pProvInfo = (CRYPT_KEY_PROV_INFO *) cbData;
|
|
DWORD dwFoo = 1000;
|
|
BOOL fSuccess = TRUE;
|
|
|
|
if ( ! CertGetCertificateContextProperty( pcCertContext,
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
pProvInfo,
|
|
&dwFoo ) )
|
|
{
|
|
fSuccess = FALSE;
|
|
// iisDebugOut((_T("Fudge. failed to get property : 0x%x"), GetLastError());
|
|
}
|
|
else
|
|
{
|
|
pProvInfo->dwProvType = PROV_RSA_SCHANNEL;
|
|
pProvInfo->pwszProvName = NULL;
|
|
if ( !CertSetCertificateContextProperty( pcCertContext,
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
0,
|
|
pProvInfo ) )
|
|
{
|
|
fSuccess = FALSE;
|
|
// iisDebugOut((_T("Fudge. failed to set property : 0x%x"), GetLastError());
|
|
}
|
|
}
|
|
|
|
// return success
|
|
return fSuccess;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
BOOL DecodeAndImportPrivateKey( PBYTE pbEncodedPrivateKey IN,
|
|
DWORD cbEncodedPrivateKey IN,
|
|
PCHAR pszPassword IN,
|
|
PWCHAR pszKeyContainer IN,
|
|
CRYPT_KEY_PROV_INFO *pCryptKeyProvInfo )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts the private key stored in the metabase, in Schannel-internal format,
|
|
into a key that can be imported via CryptImportKey() to create a CAP1 key blob.
|
|
|
|
Arguments:
|
|
|
|
pbEncodedPrivateKey - pointer to [encoded] private key
|
|
cbEncodedPrivateKey - size of encoded private key blob
|
|
pszPassword - password used to encode private key
|
|
pszKeyContainer - container name for private key
|
|
pCryptKeyProvInfo - pointer to CRYPT_KEY_PROV_INFO structure filled in on success
|
|
|
|
Returns:
|
|
|
|
BOOL indicating success/failure
|
|
|
|
--*/
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
DWORD cbPassword = strlen(pszPassword);
|
|
PPRIVATE_KEY_FILE_ENCODE pPrivateFile = NULL;
|
|
DWORD cbPrivateFile = 0;
|
|
MD5_CTX md5Ctx;
|
|
struct RC4_KEYSTRUCT rc4Key;
|
|
DWORD i;
|
|
HCRYPTPROV hProv = NULL;
|
|
HCRYPTKEY hPrivateKey = NULL;
|
|
DWORD cbDecodedPrivateKey = 0;
|
|
PBYTE pbDecodedPrivateKey = NULL;
|
|
|
|
DWORD err;
|
|
//
|
|
//HACK HACK HACK - need to make sure Schannel is initialized, so it registers
|
|
//its custom decoders, which we make use of in the following code. So, make a
|
|
//bogus call to an Schannel function
|
|
|
|
// Note: on NT5, the AcquireCredentialsHandle operates in the lsass process and
|
|
// thus will not properly initialize the stuff we need in our process. Thus we
|
|
// call SslGenerateRandomBits instead.
|
|
//
|
|
DWORD dw;
|
|
SslGenerateRandomBits( (PUCHAR)&dw, sizeof(dw) );
|
|
|
|
// We have to do a little fixup here. Old versions of
|
|
// schannel wrote the wrong header data into the ASN
|
|
// for private key files, so we must fix the size data.
|
|
pbEncodedPrivateKey[2] = (BYTE) (((cbEncodedPrivateKey - 4) & 0xFF00) >> 8); //Get MSB
|
|
pbEncodedPrivateKey[3] = (BYTE) ((cbEncodedPrivateKey - 4) & 0xFF); //Get LSB
|
|
|
|
//
|
|
// ASN.1 decode the private key.
|
|
//
|
|
|
|
//
|
|
// Figure out the size of the buffer needed
|
|
//
|
|
if( !CryptDecodeObject(X509_ASN_ENCODING,
|
|
szPrivateKeyFileEncode,
|
|
pbEncodedPrivateKey,
|
|
cbEncodedPrivateKey,
|
|
0,
|
|
NULL,
|
|
&cbPrivateFile) )
|
|
{
|
|
err = GetLastError();
|
|
// iisDebugOut((_T("Error 0x%x decoding the private key"), err);
|
|
goto EndDecodeKey;
|
|
}
|
|
|
|
pPrivateFile = (PPRIVATE_KEY_FILE_ENCODE) LocalAlloc( LPTR, cbPrivateFile );
|
|
|
|
if(pPrivateFile == NULL)
|
|
{
|
|
SetLastError( ERROR_OUTOFMEMORY );
|
|
goto EndDecodeKey;
|
|
}
|
|
|
|
//
|
|
// Actually fill in the buffer
|
|
//
|
|
if( !CryptDecodeObject( X509_ASN_ENCODING,
|
|
szPrivateKeyFileEncode,
|
|
pbEncodedPrivateKey,
|
|
cbEncodedPrivateKey,
|
|
0,
|
|
pPrivateFile,
|
|
&cbPrivateFile ) )
|
|
{
|
|
err = GetLastError();
|
|
// iisDebugOut((_T("Error 0x%x decoding the private key"), err);
|
|
goto EndDecodeKey;
|
|
}
|
|
|
|
//
|
|
// Decrypt the decoded private key using the password.
|
|
//
|
|
MD5Init(&md5Ctx);
|
|
MD5Update(&md5Ctx, (PBYTE) pszPassword, cbPassword);
|
|
MD5Final(&md5Ctx);
|
|
|
|
rc4_key( &rc4Key, 16, md5Ctx.digest );
|
|
// memset( &md5Ctx, 0, sizeof(md5Ctx) );
|
|
|
|
rc4( &rc4Key,
|
|
pPrivateFile->EncryptedBlob.cbData,
|
|
pPrivateFile->EncryptedBlob.pbData );
|
|
|
|
|
|
|
|
//
|
|
// Build a PRIVATEKEYBLOB from the decrypted private key.
|
|
//
|
|
|
|
//
|
|
// Figure out size of buffer needed
|
|
//
|
|
if( !CryptDecodeObject( X509_ASN_ENCODING,
|
|
szPrivateKeyInfoEncode,
|
|
pPrivateFile->EncryptedBlob.pbData,
|
|
pPrivateFile->EncryptedBlob.cbData,
|
|
0,
|
|
NULL,
|
|
&cbDecodedPrivateKey ) )
|
|
{
|
|
// NOTE: This stuff is complicated!!! The following code came
|
|
// from John Banes. Heck this whole routine pretty much came
|
|
// from John Banes. -- Boyd
|
|
|
|
// Maybe this was a SGC style key.
|
|
// Re-encrypt it, and build the SGC decrypting
|
|
// key, and re-decrypt it.
|
|
BYTE md5Digest[MD5DIGESTLEN];
|
|
|
|
rc4_key(&rc4Key, 16, md5Ctx.digest);
|
|
rc4(&rc4Key,
|
|
pPrivateFile->EncryptedBlob.cbData,
|
|
pPrivateFile->EncryptedBlob.pbData);
|
|
CopyMemory(md5Digest, md5Ctx.digest, MD5DIGESTLEN);
|
|
|
|
MD5Init(&md5Ctx);
|
|
MD5Update(&md5Ctx, md5Digest, MD5DIGESTLEN);
|
|
MD5Update(&md5Ctx, (PUCHAR)SGC_KEY_SALT, strlen(SGC_KEY_SALT));
|
|
MD5Final(&md5Ctx);
|
|
rc4_key(&rc4Key, 16, md5Ctx.digest);
|
|
rc4(&rc4Key,
|
|
pPrivateFile->EncryptedBlob.cbData,
|
|
pPrivateFile->EncryptedBlob.pbData);
|
|
|
|
// Try again...
|
|
if(!CryptDecodeObject(X509_ASN_ENCODING,
|
|
szPrivateKeyInfoEncode,
|
|
pPrivateFile->EncryptedBlob.pbData,
|
|
pPrivateFile->EncryptedBlob.cbData,
|
|
0,
|
|
NULL,
|
|
&cbDecodedPrivateKey))
|
|
{
|
|
ZeroMemory(&md5Ctx, sizeof(md5Ctx));
|
|
err = GetLastError();
|
|
goto EndDecodeKey;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
pbDecodedPrivateKey = (PBYTE) LocalAlloc( LPTR, cbDecodedPrivateKey );
|
|
|
|
if( pbDecodedPrivateKey == NULL )
|
|
{
|
|
SetLastError( ERROR_OUTOFMEMORY );
|
|
goto EndDecodeKey;
|
|
}
|
|
|
|
//
|
|
// Actually fill in the buffer
|
|
//
|
|
if( !CryptDecodeObject( X509_ASN_ENCODING,
|
|
szPrivateKeyInfoEncode,
|
|
pPrivateFile->EncryptedBlob.pbData,
|
|
pPrivateFile->EncryptedBlob.cbData,
|
|
0,
|
|
pbDecodedPrivateKey,
|
|
&cbDecodedPrivateKey ) )
|
|
{
|
|
err = GetLastError();
|
|
// iisDebugOut((_T("Error 0x%x decoding the private key"), err);
|
|
goto EndDecodeKey;
|
|
}
|
|
|
|
|
|
// On NT 4 the ff holds true : <- from Alex Mallet
|
|
// Although key is going to be used for key exchange, mark it as being
|
|
// used for signing, because only 512-bit key exchange keys are supported
|
|
// in the non-domestic rsabase.dll, whereas signing keys can be up to
|
|
// 2048 bits.
|
|
//
|
|
// On NT 5, PROV_RSA_FULL should be changed to PROV_RSA_SCHANNEL, and
|
|
// aiKeyAlg to CALG_RSA_KEYX, because PROV_RSA_SCHANNEL, which is only
|
|
// installed on NT 5, supports 1024-bit private keys for key exchange
|
|
//
|
|
// On NT4, Schannel doesn't care whether a key is marked for signing or exchange,
|
|
// but on NT5 it does, so aiKeyAlg must be set appropriately
|
|
//
|
|
((BLOBHEADER *) pbDecodedPrivateKey)->aiKeyAlg = CALG_RSA_KEYX;
|
|
|
|
//
|
|
// Clean out the key container, pszKeyContainer
|
|
//
|
|
|
|
CryptAcquireContext(&hProv,
|
|
pszKeyContainer,
|
|
NULL,
|
|
PROV_RSA_SCHANNEL,
|
|
CRYPT_DELETEKEYSET | CRYPT_MACHINE_KEYSET);
|
|
//
|
|
// Create a CryptoAPI key container in which to store the key.
|
|
//
|
|
if( !CryptAcquireContext( &hProv,
|
|
pszKeyContainer,
|
|
NULL,
|
|
PROV_RSA_SCHANNEL,
|
|
CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
|
|
{
|
|
err = GetLastError();
|
|
// iisDebugOut((_T("Error 0x%x calling CryptAcquireContext"), err);
|
|
goto EndDecodeKey;
|
|
}
|
|
|
|
//
|
|
// Import the private key blob into the key container.
|
|
//
|
|
if( !CryptImportKey( hProv,
|
|
pbDecodedPrivateKey,
|
|
cbDecodedPrivateKey,
|
|
0,
|
|
CRYPT_EXPORTABLE, //so we can export it later
|
|
&hPrivateKey ) )
|
|
{
|
|
err = GetLastError();
|
|
// iisDebugOut((_T("Error 0x%x importing PRIVATEKEYBLOB"), err);
|
|
goto EndDecodeKey;
|
|
}
|
|
|
|
|
|
//
|
|
// Fill in the CRYPT_KEY_PROV_INFO structure, with the same parameters we
|
|
// used in the call to CryptAcquireContext() above
|
|
//
|
|
|
|
//
|
|
// container name in the structure is a unicode string, so we need to convert
|
|
//
|
|
|
|
if ( pszKeyContainer != NULL )
|
|
{
|
|
// point the key container name to the passed in string
|
|
// WARNING: this does not actually copy the string, just the pointer
|
|
// to it. So the strings needs to remain valid until the ProvInfo is commited.
|
|
pCryptKeyProvInfo->pwszContainerName = pszKeyContainer;
|
|
}
|
|
else
|
|
{
|
|
pCryptKeyProvInfo->pwszContainerName = NULL;
|
|
}
|
|
|
|
pCryptKeyProvInfo->pwszProvName = NULL;
|
|
pCryptKeyProvInfo->dwProvType = PROV_RSA_FULL;
|
|
pCryptKeyProvInfo->dwFlags = 0x20; // allow the cert to be exchanged
|
|
pCryptKeyProvInfo->cProvParam = 0;
|
|
pCryptKeyProvInfo->rgProvParam = NULL;
|
|
pCryptKeyProvInfo->dwKeySpec = AT_KEYEXCHANGE; // allow the cert to be exchanged
|
|
|
|
fSuccess = TRUE;
|
|
|
|
EndDecodeKey:
|
|
|
|
//
|
|
// Clean-up that happens regardless of success/failure
|
|
//
|
|
if ( pPrivateFile )
|
|
{
|
|
LocalFree( pPrivateFile );
|
|
}
|
|
|
|
if ( pbDecodedPrivateKey )
|
|
{
|
|
LocalFree( pbDecodedPrivateKey );
|
|
}
|
|
|
|
if ( hPrivateKey )
|
|
{
|
|
CryptDestroyKey( hPrivateKey );
|
|
}
|
|
|
|
if ( hProv )
|
|
{
|
|
CryptReleaseContext( hProv, 0 );
|
|
}
|
|
|
|
return fSuccess;
|
|
|
|
|
|
} //DecodeAndImportPrivateKey
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes an incoming PKCS10 request and saves it as a property attached to the key. It also
|
|
checks if the request is in the old internal Keyring format or not......
|
|
|
|
Arguments:
|
|
|
|
pCert - CAPI certificate context pointer for the cert to save the request on
|
|
pbPKCS10req - pointer to the request
|
|
cbPKCS10req - size of the request
|
|
|
|
Returns:
|
|
|
|
BOOL indicating success/failure
|
|
|
|
--*/
|
|
BOOL FImportAndStoreRequest( PCCERT_CONTEXT pCert, PVOID pbPKCS10req, DWORD cbPKCS10req )
|
|
{
|
|
BOOL f;
|
|
DWORD err;
|
|
|
|
// if any NULLS are passed in, fail gracefully
|
|
if ( !pCert || !pbPKCS10req || !cbPKCS10req )
|
|
return FALSE;
|
|
|
|
// first, check if the incoming request is actually pointing to an old KeyRing internal
|
|
// request format. That just means that the real request is actuall slightly into
|
|
// the block. The way you tell is by testing the first DWORD to see it
|
|
// is REQUEST_HEADER_IDENTIFIER
|
|
// start by seeing if this is a new style key request
|
|
LPREQUEST_HEADER pHeader = (LPREQUEST_HEADER)pbPKCS10req;
|
|
if ( pHeader->Identifier == REQUEST_HEADER_IDENTIFIER )
|
|
{
|
|
// update the request pointer and data count
|
|
pbPKCS10req = (PBYTE)pbPKCS10req + pHeader->cbSizeOfHeader;
|
|
cbPKCS10req = pHeader->cbRequestSize;
|
|
}
|
|
|
|
// now save the request onto the key
|
|
CRYPT_DATA_BLOB dataBlob;
|
|
ZeroMemory( &dataBlob, sizeof(dataBlob) );
|
|
dataBlob.pbData = (PBYTE)pbPKCS10req; // pointer to blob data
|
|
dataBlob.cbData = cbPKCS10req; // blob length info
|
|
f = CertSetCertificateContextProperty(
|
|
pCert,
|
|
CERTWIZ_REQUEST_PROP_ID,
|
|
0,
|
|
&dataBlob
|
|
);
|
|
err = GetLastError();
|
|
|
|
/*
|
|
HRESULT hRes = CertTool_SetBinaryBlobProp(
|
|
pCert, // cert context to set the prop on
|
|
pbPKCS10req, // pointer to blob data
|
|
cbPKCS10req, // blob length info
|
|
CERTWIZ_REQUEST_PROP_ID,// property ID for context
|
|
TRUE // the request is already encoded
|
|
);
|
|
*/
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
|
|
#endif //_CHICAGO_
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|