windows-nt/Source/XPSP1/NT/ds/security/cryptoapi/pki/pfxlib/impexppk.cpp

1424 lines
41 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1995 - 1999
//
// File: certhlpr.cpp
//
// Contents: import and export of private keys
//
// Functions: ImportExoprtDllMain
// CryptImportPKCS8
// CryptExportPKCS8
//
// History:
//--------------------------------------------------------------------------
#include "global.hxx"
//#include "prvtkey.h"
#include "impexppk.h"
#include "pfxcrypt.h"
// All the *pvInfo extra stuff needs to be aligned
#define INFO_LEN_ALIGN(Len) ((Len + 7) & ~7)
static BOOL WINAPI ExportRSAPrivateKeyInfo(
HCRYPTPROV hCryptProv, // in
DWORD dwKeySpec, // in
LPSTR pszPrivateKeyObjId, // in
DWORD dwFlags, // in
void *pvAuxInfo, // in
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo, // out
DWORD *pcbPrivateKeyInfo // in, out
);
static BOOL WINAPI ImportRSAPrivateKeyInfo(
HCRYPTPROV hCryptProv, // in
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo, // in
DWORD dwFlags, // in, optional
void *pvAuxInfo // in, optional
);
static BOOL WINAPI ExportDSSPrivateKeyInfo(
HCRYPTPROV hCryptProv, // in
DWORD dwKeySpec, // in
LPSTR pszPrivateKeyObjId, // in
DWORD dwFlags, // in
void *pvAuxInfo, // in
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo, // out
DWORD *pcbPrivateKeyInfo // in, out
);
static BOOL WINAPI ImportDSSPrivateKeyInfo(
HCRYPTPROV hCryptProv, // in
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo, // in
DWORD dwFlags, // in, optional
void *pvAuxInfo // in, optional
);
static HCRYPTOIDFUNCSET hExportPrivKeyFuncSet;
static HCRYPTOIDFUNCSET hImportPrivKeyFuncSet;
// Internal default OIDs
#define DEFAULT_CSP_PRIVKEY1 ((LPCSTR) 1)
#define DEFAULT_CSP_PRIVKEY2 ((LPCSTR) 2)
static const CRYPT_OID_FUNC_ENTRY ExportPrivKeyFuncTable[] = {
DEFAULT_CSP_PRIVKEY1, ExportRSAPrivateKeyInfo,
szOID_RSA_RSA, ExportRSAPrivateKeyInfo,
szOID_OIWSEC_dsa, ExportDSSPrivateKeyInfo,
szOID_X957_DSA, ExportDSSPrivateKeyInfo
};
#define EXPORT_PRIV_KEY_FUNC_COUNT (sizeof(ExportPrivKeyFuncTable) / \
sizeof(ExportPrivKeyFuncTable[0]))
static const CRYPT_OID_FUNC_ENTRY ImportPrivKeyFuncTable[] = {
szOID_RSA_RSA, ImportRSAPrivateKeyInfo,
szOID_OIWSEC_dsa, ImportDSSPrivateKeyInfo,
szOID_X957_DSA, ImportDSSPrivateKeyInfo
};
#define IMPORT_PRIV_KEY_FUNC_COUNT (sizeof(ImportPrivKeyFuncTable) / \
sizeof(ImportPrivKeyFuncTable[0]))
BOOL
WINAPI
ImportExportDllMain(
HMODULE hInst,
ULONG ul_reason_for_call,
LPVOID lpReserved)
{
switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
// Private key function setup
if (NULL == (hExportPrivKeyFuncSet = CryptInitOIDFunctionSet(
CRYPT_OID_EXPORT_PRIVATE_KEY_INFO_FUNC,
0)))
goto ErrorReturn;
if (NULL == (hImportPrivKeyFuncSet = CryptInitOIDFunctionSet(
CRYPT_OID_IMPORT_PRIVATE_KEY_INFO_FUNC,
0)))
goto ErrorReturn;
if (!CryptInstallOIDFunctionAddress(
NULL, // hModule
X509_ASN_ENCODING,
CRYPT_OID_EXPORT_PRIVATE_KEY_INFO_FUNC,
EXPORT_PRIV_KEY_FUNC_COUNT,
ExportPrivKeyFuncTable,
0)) // dwFlags
goto ErrorReturn;
if (!CryptInstallOIDFunctionAddress(
NULL, // hModule
X509_ASN_ENCODING,
CRYPT_OID_IMPORT_PRIVATE_KEY_INFO_FUNC,
IMPORT_PRIV_KEY_FUNC_COUNT,
ImportPrivKeyFuncTable,
0)) // dwFlags
goto ErrorReturn;
break;
case DLL_PROCESS_DETACH:
break;
default:
break;
}
return TRUE;
ErrorReturn:
return FALSE;
}
//+-------------------------------------------------------------------------
// phCryptProv - a pointer to a HCRYPTPROV to put the handle of the provider
// that received the imported keyset. if this is NON_NULL then
// the caller is responsible for calling CryptReleaseContext().
// pdwKeySpec - a pointer to a DWORD to receive the KeySpec of imported keyset
// privateKeyAndParams - private key blob and corresponding parameters
// dwFlags - The available flags are:
// CRYPT_EXPORTABLE
// this flag is used when importing private keys, for a full
// explanation please see the documentation for CryptImportKey.
// phCryptProv - filled in with the handle of the provider the key was
// imported to, the caller is responsible for freeing it
// pvAuxInfo - This parameter is reserved for future use and should be set
// to NULL in the interim.
//+-------------------------------------------------------------------------
BOOL
WINAPI
CryptImportPKCS8(
CRYPT_PKCS8_IMPORT_PARAMS sPrivateKeyAndParams, // in
DWORD dwFlags, // in, optional
HCRYPTPROV *phCryptProv, // out
void *pvAuxInfo // in, optional
)
{
BOOL fResult = TRUE;
void *pvFuncAddr;
HCRYPTOIDFUNCADDR hFuncAddr;
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfoStruct = NULL;
DWORD cbPrivateKeyInfoStruct = 0;
CRYPT_ENCRYPTED_PRIVATE_KEY_INFO *pEncryptedPrivateKeyInfoStruct = NULL;
DWORD cbEncryptedPrivateKeyInfoStruct = 0;
BYTE *pbEncodedPrivateKey = sPrivateKeyAndParams.PrivateKey.pbData;
DWORD cbEncodedPrivateKey = sPrivateKeyAndParams.PrivateKey.cbData;
BOOL bEncodedPrivateKeyAlloced = FALSE;
HCRYPTPROV hCryptProv = NULL;
// try to decode private key blob as a CRYPT_PRIVATE_KEY_INFO structure
if (!CryptDecodeObject(X509_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
sPrivateKeyAndParams.PrivateKey.pbData,
sPrivateKeyAndParams.PrivateKey.cbData,
CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&cbPrivateKeyInfoStruct)) {
// that decode failed, so try to decode as CRYPT_ENCRYPTED_PRIVATE_KEY_INFO structure
if (!CryptDecodeObject(X509_ASN_ENCODING,
PKCS_ENCRYPTED_PRIVATE_KEY_INFO,
sPrivateKeyAndParams.PrivateKey.pbData,
sPrivateKeyAndParams.PrivateKey.cbData,
CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&cbEncryptedPrivateKeyInfoStruct))
goto ErrorReturn;
if (NULL == (pEncryptedPrivateKeyInfoStruct = (CRYPT_ENCRYPTED_PRIVATE_KEY_INFO *)
SSAlloc(cbEncryptedPrivateKeyInfoStruct)))
goto ErrorReturn;
if (!CryptDecodeObject(X509_ASN_ENCODING,
PKCS_ENCRYPTED_PRIVATE_KEY_INFO,
sPrivateKeyAndParams.PrivateKey.pbData,
sPrivateKeyAndParams.PrivateKey.cbData,
CRYPT_DECODE_NOCOPY_FLAG,
pEncryptedPrivateKeyInfoStruct,
&cbEncryptedPrivateKeyInfoStruct))
goto ErrorReturn;
// call back the callee to decrypt the private key info
pbEncodedPrivateKey = NULL;
cbEncodedPrivateKey = 0;
if (!sPrivateKeyAndParams.pDecryptPrivateKeyFunc(
pEncryptedPrivateKeyInfoStruct->EncryptionAlgorithm,
pEncryptedPrivateKeyInfoStruct->EncryptedPrivateKey,
NULL,
&cbEncodedPrivateKey,
sPrivateKeyAndParams.pVoidDecryptFunc))
goto ErrorReturn;
if (NULL == (pbEncodedPrivateKey = (BYTE *)
SSAlloc(cbEncodedPrivateKey)))
goto ErrorReturn;
bEncodedPrivateKeyAlloced = TRUE;
if (!sPrivateKeyAndParams.pDecryptPrivateKeyFunc(
pEncryptedPrivateKeyInfoStruct->EncryptionAlgorithm,
pEncryptedPrivateKeyInfoStruct->EncryptedPrivateKey,
pbEncodedPrivateKey,
&cbEncodedPrivateKey,
sPrivateKeyAndParams.pVoidDecryptFunc))
goto ErrorReturn;
// we are now back to square one with an encoded CRYPT_PRIVATE_KEY_INFO struct,
// so get the size of that when it's decoded
if (!CryptDecodeObject(X509_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
pbEncodedPrivateKey,
cbEncodedPrivateKey,
CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&cbPrivateKeyInfoStruct))
goto ErrorReturn;
}
if (NULL == (pPrivateKeyInfoStruct = (CRYPT_PRIVATE_KEY_INFO *)
SSAlloc(cbPrivateKeyInfoStruct)))
goto ErrorReturn;
if (!CryptDecodeObject(X509_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
pbEncodedPrivateKey,
cbEncodedPrivateKey,
CRYPT_DECODE_NOCOPY_FLAG,
pPrivateKeyInfoStruct,
&cbPrivateKeyInfoStruct))
goto ErrorReturn;
// call the caller back to get the provider to import to, if the
// call back is null then just use the default provider.
if (sPrivateKeyAndParams.pResolvehCryptProvFunc != NULL) {
if (!sPrivateKeyAndParams.pResolvehCryptProvFunc(
pPrivateKeyInfoStruct,
&hCryptProv,
sPrivateKeyAndParams.pVoidResolveFunc)) {
goto ErrorReturn;
}
}
else {
if (!CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET)) {
goto ErrorReturn;
}
}
// resolve what supporting import function to call based on the algorithm
// OID of the private key
if (CryptGetOIDFunctionAddress(
hImportPrivKeyFuncSet,
X509_ASN_ENCODING,
pPrivateKeyInfoStruct->Algorithm.pszObjId,
0, // dwFlags
&pvFuncAddr,
&hFuncAddr)) {
fResult = ((PFN_IMPORT_PRIV_KEY_FUNC) pvFuncAddr)(
hCryptProv,
pPrivateKeyInfoStruct,
dwFlags,
pvAuxInfo
);
CryptFreeOIDFunctionAddress(hFuncAddr, 0);
}
else {
SetLastError(ERROR_UNSUPPORTED_TYPE);
goto ErrorReturn;
}
// check to see if the caller wants the hCryptProv
if (phCryptProv) {
*phCryptProv = hCryptProv;
}
else {
HRESULT hr = GetLastError();
CryptReleaseContext(hCryptProv, 0);
SetLastError(hr);
}
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
if (hCryptProv)
{
HRESULT hr = GetLastError();
CryptReleaseContext(hCryptProv, 0);
SetLastError(hr);
}
CommonReturn:
if (pPrivateKeyInfoStruct)
SSFree(pPrivateKeyInfoStruct);
if (pEncryptedPrivateKeyInfoStruct)
SSFree(pEncryptedPrivateKeyInfoStruct);
if (bEncodedPrivateKeyAlloced)
SSFree(pbEncodedPrivateKey);
return fResult;
}
////////
// old crusty API kept around for compat reasons
BOOL
WINAPI
CryptExportPKCS8(
HCRYPTPROV hCryptProv, // in
DWORD dwKeySpec, // in
LPSTR pszPrivateKeyObjId, // in
DWORD dwFlags, // in
void *pvAuxInfo, // in
BYTE *pbPrivateKeyBlob, // out
DWORD *pcbPrivateKeyBlob // in, out
)
{
CRYPT_PKCS8_EXPORT_PARAMS sExportParams;
ZeroMemory(&sExportParams, sizeof(sExportParams));
// copy args to pkcs8_export struct
sExportParams.hCryptProv = hCryptProv;
sExportParams.dwKeySpec = dwKeySpec;
sExportParams.pszPrivateKeyObjId = pszPrivateKeyObjId;
// these are not available to non-Ex function
sExportParams.pEncryptPrivateKeyFunc = NULL;
sExportParams.pVoidEncryptFunc = NULL;
return CryptExportPKCS8Ex(
&sExportParams,
dwFlags,
pvAuxInfo,
pbPrivateKeyBlob,
pcbPrivateKeyBlob);
}
//+-------------------------------------------------------------------------
// hCryptProv - specifies the provider to export from
// dwKeySpec - Identifies the public key to use from the provider's container.
// For example, AT_KEYEXCHANGE or AT_SIGNATURE.
// pszPrivateKeyObjId - Specifies the private key algorithm. If an installable
// function was not found for the pszPrivateKeyObjId, an
// attempt is made to export the key as a RSA Public Key
// (szOID_RSA_RSA).
// dwFlags - The flag values. Current supported values are:
// DELETE_KEYSET - (NOT CURRENTLY SUPPORTED!!!!)
// will delete key after export
// pvAuxInfo - This parameter is reserved for future use and should be set to
// NULL in the interim.
// pbPrivateKeyBlob - A pointer to the private key blob. It will be encoded
// as a PKCS8 PrivateKeyInfo.
// pcbPrivateKeyBlob - A pointer to a DWORD that contains the size, in bytes,
// of the private key blob being exported.
//+-------------------------------------------------------------------------
BOOL
WINAPI
CryptExportPKCS8Ex(
CRYPT_PKCS8_EXPORT_PARAMS* psExportParams, // in
DWORD dwFlags, // in
void *pvAuxInfo, // in
BYTE *pbPrivateKeyBlob, // out
DWORD *pcbPrivateKeyBlob // in, out
)
{
BOOL fResult = TRUE;
void *pvFuncAddr;
HCRYPTOIDFUNCADDR hFuncAddr;
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo = NULL;
DWORD cbPrivateKeyInfo = 0;
DWORD cbEncoded = 0;
// optional; used during encrypted export
PBYTE pbTmpKeyBlob = NULL;
CRYPT_ENCRYPTED_PRIVATE_KEY_INFO sEncryptedKeyInfo; ZeroMemory(&sEncryptedKeyInfo, sizeof(sEncryptedKeyInfo));
if (CryptGetOIDFunctionAddress(
hExportPrivKeyFuncSet,
X509_ASN_ENCODING,
psExportParams->pszPrivateKeyObjId,
0, // dwFlags
&pvFuncAddr,
&hFuncAddr)) {
if (!((PFN_EXPORT_PRIV_KEY_FUNC) pvFuncAddr)(
psExportParams->hCryptProv,
psExportParams->dwKeySpec,
psExportParams->pszPrivateKeyObjId,
dwFlags & ~GIVE_ME_DATA, // sizeit
pvAuxInfo,
NULL,
&cbPrivateKeyInfo
))
goto ErrorReturn;
if (NULL == (pPrivateKeyInfo = (CRYPT_PRIVATE_KEY_INFO *)
SSAlloc(cbPrivateKeyInfo)))
goto ErrorReturn;
if (!((PFN_EXPORT_PRIV_KEY_FUNC) pvFuncAddr)(
psExportParams->hCryptProv,
psExportParams->dwKeySpec,
psExportParams->pszPrivateKeyObjId,
dwFlags, // maybe real data...
pvAuxInfo,
pPrivateKeyInfo,
&cbPrivateKeyInfo
))
goto ErrorReturn;
CryptFreeOIDFunctionAddress(hFuncAddr, 0);
}
else { // if (CryptGetOIDFunctionAddress())
SetLastError(ERROR_UNSUPPORTED_TYPE);
return FALSE;
}
// encode the private key info struct
if (!CryptEncodeObject(
X509_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
pPrivateKeyInfo,
NULL,
&cbEncoded))
goto ErrorReturn;
if (NULL == psExportParams->pEncryptPrivateKeyFunc)
{
// no encryption; this is output buffer
// check to see if the caller specified a buffer and has enough space
if ((pbPrivateKeyBlob != NULL) && (*pcbPrivateKeyBlob >= cbEncoded)) {
if (!CryptEncodeObject(
X509_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
pPrivateKeyInfo,
pbPrivateKeyBlob,
pcbPrivateKeyBlob))
goto ErrorReturn;
}
else {
*pcbPrivateKeyBlob = cbEncoded;
if (pbPrivateKeyBlob != NULL) {
SetLastError((DWORD) ERROR_MORE_DATA);
goto ErrorReturn;
}
}
}
else
{
// we do want to encrypt!!
// always encode: use tmp alloc
pbTmpKeyBlob = (PBYTE)SSAlloc(cbEncoded);
if (pbTmpKeyBlob == NULL)
goto ErrorReturn;
DWORD cbTmpKeyBlob = cbEncoded;
// NOW add optional encryption and encode as ENCR_PRIV_KEY_INFO
CRYPT_DATA_BLOB sClearTextKey = { cbTmpKeyBlob, pbTmpKeyBlob};
// do inner encode
if (!CryptEncodeObject(
X509_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
pPrivateKeyInfo,
pbTmpKeyBlob,
&cbTmpKeyBlob))
goto ErrorReturn;
// exported the key; encoded as PRIVATE_KEY_INFO.
if (!psExportParams->pEncryptPrivateKeyFunc(
&sEncryptedKeyInfo.EncryptionAlgorithm, // out
&sClearTextKey, // in
NULL, // opt
&sEncryptedKeyInfo.EncryptedPrivateKey.cbData, // out
psExportParams->pVoidEncryptFunc))
goto ErrorReturn;
if (NULL == (sEncryptedKeyInfo.EncryptedPrivateKey.pbData = (BYTE*) SSAlloc(sEncryptedKeyInfo.EncryptedPrivateKey.cbData)))
goto ErrorReturn;
if (dwFlags & GIVE_ME_DATA)
{
if (!psExportParams->pEncryptPrivateKeyFunc(
&sEncryptedKeyInfo.EncryptionAlgorithm, // out
&sClearTextKey, // in
sEncryptedKeyInfo.EncryptedPrivateKey.pbData, // opt
&sEncryptedKeyInfo.EncryptedPrivateKey.cbData, // out
psExportParams->pVoidEncryptFunc))
goto ErrorReturn;
}
else
{
// fill in phony encr key
FillMemory(sEncryptedKeyInfo.EncryptedPrivateKey.pbData, sEncryptedKeyInfo.EncryptedPrivateKey.cbData, 0x69);
}
// item is now encrypted; now encode
// encode the private key info struct
if (!CryptEncodeObject(
X509_ASN_ENCODING,
PKCS_ENCRYPTED_PRIVATE_KEY_INFO,
&sEncryptedKeyInfo,
NULL,
&cbEncoded))
goto ErrorReturn;
// check to see if the caller specified a buffer and has enough space
if ((pbPrivateKeyBlob != NULL) && (*pcbPrivateKeyBlob >= cbEncoded)) {
if (!CryptEncodeObject(
X509_ASN_ENCODING,
PKCS_ENCRYPTED_PRIVATE_KEY_INFO,
&sEncryptedKeyInfo,
pbPrivateKeyBlob,
pcbPrivateKeyBlob))
goto ErrorReturn;
}
else {
*pcbPrivateKeyBlob = cbEncoded;
if (pbPrivateKeyBlob != NULL) {
SetLastError((DWORD) ERROR_MORE_DATA);
goto ErrorReturn;
}
}
}
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
CommonReturn:
if (pPrivateKeyInfo)
SSFree(pPrivateKeyInfo);
if (pbTmpKeyBlob)
SSFree(pbTmpKeyBlob);
if (sEncryptedKeyInfo.EncryptedPrivateKey.pbData)
SSFree(sEncryptedKeyInfo.EncryptedPrivateKey.pbData);
if (sEncryptedKeyInfo.EncryptionAlgorithm.Parameters.pbData)
SSFree(sEncryptedKeyInfo.EncryptionAlgorithm.Parameters.pbData);
return fResult;
}
static LONG counter = 0;
// hack function to create a mock RSA private key blob based only on size
BYTE * AllocFakeRSAPrivateKey(DWORD cb)
{
BLOBHEADER *pBlobHeader;
RSAPUBKEY *pKey;
BYTE *pByte;
DWORD dwJumpSize;
pBlobHeader = (BLOBHEADER *) SSAlloc(cb);
if (pBlobHeader == NULL)
return NULL;
memset(pBlobHeader, 0, cb);
pBlobHeader->bType = PRIVATEKEYBLOB;
pBlobHeader->bVersion = CUR_BLOB_VERSION;
pBlobHeader->reserved = 0;
pBlobHeader->aiKeyAlg = CALG_RSA_SIGN;
pKey = (RSAPUBKEY *) (((BYTE*) pBlobHeader) + sizeof(BLOBHEADER));
pKey->magic = 0x32415352;
pKey->bitlen = ((cb - sizeof(BLOBHEADER) - sizeof(RSAPUBKEY)) / 9) * 2 * 8;
pKey->pubexp = 65537;
dwJumpSize = (cb - sizeof(BLOBHEADER) - sizeof(RSAPUBKEY)) / 9;
pByte = ((BYTE *) pBlobHeader) + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY);
// put some bogus data at the start of the key so
// that we know will be unique for each key so that
// they look different durring a comparison
InterlockedIncrement(&counter);
*((LONG *) pByte) = counter;
// most significant byte of modulus
pByte += (dwJumpSize * 2) - 1;
*pByte = 0x80;
// most significant byte of prime1
pByte += dwJumpSize;
*pByte = 0x80;
// most significant byte of prime2
pByte += dwJumpSize;
*pByte = 0x80;
// most significant byte of exponent1
pByte += dwJumpSize;
*pByte = 0x80;
// most significant byte of exponent2
pByte += dwJumpSize;
*pByte = 0x80;
// most significant byte of coefficient
pByte += dwJumpSize;
*pByte = 0x80;
// most significant byte of privateExponent
pByte += dwJumpSize * 2;
*pByte = 0x80;
return ((BYTE *)pBlobHeader);
}
static BOOL WINAPI ExportRSAPrivateKeyInfo(
HCRYPTPROV hCryptProv, // in
DWORD dwKeySpec, // in
LPSTR pszPrivateKeyObjId, // in
DWORD dwFlags, // in
void *pvAuxInfo, // in
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo, // out
DWORD *pcbPrivateKeyInfo // in, out
)
{
BOOL fResult = TRUE;
HCRYPTKEY hCryptKey = NULL;
BYTE *pKeyBlob = NULL;
DWORD cbKeyBlob = 0;
BYTE *pEncodedKeyBlob = NULL;
DWORD cbEncodedKeyBlob = 0;
BYTE *pKeyUsage = NULL;
DWORD cbKeyUsage = 0;
DWORD dwSize = 0;
CRYPT_BIT_BLOB CryptBitBlob;
BYTE KeyUsageByte = 0;
BYTE *pbCurrentLocation = NULL;
// get a handle to the keyset to export
if (!CryptGetUserKey(
hCryptProv,
dwKeySpec,
&hCryptKey))
goto ErrorReturn;
// export the key set to a CAPI blob
if (!CryptExportKey(
hCryptKey,
0,
PRIVATEKEYBLOB,
0,
NULL,
&cbKeyBlob))
goto ErrorReturn;
// make sure the caller REALLY wants the key at this point
if ((dwFlags & PFX_MODE) && !(dwFlags & GIVE_ME_DATA))
{
if (NULL == (pKeyBlob = AllocFakeRSAPrivateKey(cbKeyBlob)))
goto ErrorReturn;
}
// if not in PFX export mode or we really want the key then just do normal processing
else
{
if (NULL == (pKeyBlob = (BYTE *) SSAlloc(cbKeyBlob)))
goto ErrorReturn;
if (!CryptExportKey(
hCryptKey,
0,
PRIVATEKEYBLOB,
0,
pKeyBlob,
&cbKeyBlob))
goto ErrorReturn;
}
// encode the key blob to a RSA private key
if (!CryptEncodeObject(
X509_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY,
pKeyBlob,
NULL,
&cbEncodedKeyBlob))
goto ErrorReturn;
if (NULL == (pEncodedKeyBlob = (BYTE *) SSAlloc(cbEncodedKeyBlob)))
goto ErrorReturn;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY,
pKeyBlob,
pEncodedKeyBlob,
&cbEncodedKeyBlob))
goto ErrorReturn;
// encode the KEY_USAGE attribute
CryptBitBlob.cbData = 1;
CryptBitBlob.pbData = &KeyUsageByte;
CryptBitBlob.cUnusedBits = 0;
if (((BLOBHEADER *) pKeyBlob)->aiKeyAlg == CALG_RSA_SIGN)
KeyUsageByte = CERT_DIGITAL_SIGNATURE_KEY_USAGE;
else if (((BLOBHEADER *) pKeyBlob)->aiKeyAlg == CALG_RSA_KEYX)
KeyUsageByte = CERT_DATA_ENCIPHERMENT_KEY_USAGE;
else {
goto ErrorReturn;
}
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_BITS,
(void *) &CryptBitBlob,
NULL,
&cbKeyUsage))
goto ErrorReturn;
if (NULL == (pKeyUsage = (BYTE *) SSAlloc(cbKeyUsage)))
goto ErrorReturn;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_BITS,
(void *) &CryptBitBlob,
pKeyUsage,
&cbKeyUsage))
goto ErrorReturn;
// we can now calculate the size needed
dwSize = sizeof(CRYPT_PRIVATE_KEY_INFO) + // main private key info struct
INFO_LEN_ALIGN(sizeof(szOID_RSA_RSA)) + // size of the RSA algorithm identifier string
INFO_LEN_ALIGN(cbEncodedKeyBlob) + // buffer that holds encoded RSA private key
sizeof(CRYPT_ATTRIBUTES) + // struct for private key attributes
sizeof(CRYPT_ATTRIBUTE) + // struct for the one attribute being set, KEY_USAGE
INFO_LEN_ALIGN(sizeof(szOID_KEY_USAGE)) + // size of attribute OID for key usage
sizeof(CRYPT_ATTR_BLOB) + // struct for values in attribute
cbKeyUsage; // size of buffer for encoded attribute
// check to see if the caller passed in a buffer, and enough space
if (pPrivateKeyInfo == NULL)
goto CommonReturn;
else if (*pcbPrivateKeyInfo < dwSize) {
SetLastError((DWORD) ERROR_MORE_DATA);
goto ErrorReturn;
}
// everything is OK so copy all the information to the caller's buffer
pbCurrentLocation = ((BYTE *) pPrivateKeyInfo) + sizeof(CRYPT_PRIVATE_KEY_INFO);
pPrivateKeyInfo->Version = 0;
pPrivateKeyInfo->Algorithm.pszObjId = (LPSTR) pbCurrentLocation;
memcpy(pbCurrentLocation, szOID_RSA_RSA, sizeof(szOID_RSA_RSA));
pbCurrentLocation += INFO_LEN_ALIGN(sizeof(szOID_RSA_RSA));
pPrivateKeyInfo->Algorithm.Parameters.cbData = 0; // no parameters for RSA
pPrivateKeyInfo->Algorithm.Parameters.pbData = NULL;// no parameters for RSA
pPrivateKeyInfo->PrivateKey.cbData = cbEncodedKeyBlob;
pPrivateKeyInfo->PrivateKey.pbData = pbCurrentLocation;
memcpy(pbCurrentLocation, pEncodedKeyBlob, cbEncodedKeyBlob);
pbCurrentLocation += INFO_LEN_ALIGN(cbEncodedKeyBlob);
pPrivateKeyInfo->pAttributes = (PCRYPT_ATTRIBUTES) pbCurrentLocation;
pbCurrentLocation += sizeof(CRYPT_ATTRIBUTES);
pPrivateKeyInfo->pAttributes->cAttr = 1; // the only attribute right now is KEY_USAGE
pPrivateKeyInfo->pAttributes->rgAttr = (PCRYPT_ATTRIBUTE) pbCurrentLocation;
pbCurrentLocation += sizeof(CRYPT_ATTRIBUTE);
pPrivateKeyInfo->pAttributes->rgAttr[0].pszObjId = (LPSTR) pbCurrentLocation;
memcpy(pbCurrentLocation, szOID_KEY_USAGE, sizeof(szOID_KEY_USAGE));
pbCurrentLocation += INFO_LEN_ALIGN(sizeof(szOID_KEY_USAGE));
pPrivateKeyInfo->pAttributes->rgAttr[0].cValue = 1;
pPrivateKeyInfo->pAttributes->rgAttr[0].rgValue = (PCRYPT_ATTR_BLOB) pbCurrentLocation;
pbCurrentLocation += sizeof(CRYPT_ATTR_BLOB);
pPrivateKeyInfo->pAttributes->rgAttr[0].rgValue[0].cbData = cbKeyUsage;
pPrivateKeyInfo->pAttributes->rgAttr[0].rgValue[0].pbData = pbCurrentLocation;
memcpy(pbCurrentLocation, pKeyUsage, cbKeyUsage);
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
CommonReturn:
*pcbPrivateKeyInfo = dwSize;
if (hCryptKey)
{
DWORD dwErr = GetLastError();
CryptDestroyKey(hCryptKey);
SetLastError(dwErr);
}
if (pKeyBlob)
SSFree(pKeyBlob);
if (pEncodedKeyBlob)
SSFree(pEncodedKeyBlob);
if (pKeyUsage)
SSFree(pKeyUsage);
return fResult;
}
static DWORD ResolveKeySpec(
PCRYPT_ATTRIBUTES pCryptAttributes)
{
DWORD i = 0;
DWORD dwKeySpec = 0;
DWORD cbAttribute = 0;
CRYPT_BIT_BLOB *pAttribute = NULL;
if (pCryptAttributes != NULL)
while (i < pCryptAttributes->cAttr) {
if (lstrcmp(pCryptAttributes->rgAttr[i].pszObjId, szOID_KEY_USAGE) == 0) {
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_BITS,
pCryptAttributes->rgAttr[i].rgValue->pbData,
pCryptAttributes->rgAttr[i].rgValue->cbData,
0,
NULL,
&cbAttribute
)) {
i++;
continue;
}
if (NULL == (pAttribute = (CRYPT_BIT_BLOB *) SSAlloc(cbAttribute)))
{
i++;
continue;
}
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_BITS,
pCryptAttributes->rgAttr[i].rgValue->pbData,
pCryptAttributes->rgAttr[i].rgValue->cbData,
0,
pAttribute,
&cbAttribute
)) {
i++;
SSFree(pAttribute);
continue;
}
if ((pAttribute->pbData[0] & CERT_KEY_ENCIPHERMENT_KEY_USAGE) ||
(pAttribute->pbData[0] & CERT_DATA_ENCIPHERMENT_KEY_USAGE)) {
dwKeySpec = AT_KEYEXCHANGE;
goto CommonReturn;
}
else if ((pAttribute->pbData[0] & CERT_DIGITAL_SIGNATURE_KEY_USAGE) ||
(pAttribute->pbData[0] & CERT_KEY_CERT_SIGN_KEY_USAGE) ||
(pAttribute->pbData[0] & CERT_CRL_SIGN_KEY_USAGE)) {
dwKeySpec = AT_SIGNATURE;
goto CommonReturn;
}
} // if (lstrcmp(pCryptAttributes->rgAttr[i].pszObjId, szOID_KEY_USAGE) == 0)
i++;
} // while (i < pCryptAttributes->cAttr)
//ErrorReturn:
CommonReturn:
if (pAttribute)
SSFree(pAttribute);
return dwKeySpec;
}
static BOOL WINAPI ImportRSAPrivateKeyInfo(
HCRYPTPROV hCryptProv, // in
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo, // in
DWORD dwFlags, // in, optional
void *pvAuxInfo // in, optional
)
{
BOOL fResult = TRUE;
DWORD cbRSAPrivateKey = 0;
BYTE *pbRSAPrivateKey = NULL;
HCRYPTKEY hCryptKey = NULL;
DWORD dwKeySpec = 0;
// decode the rsa der-encoded keyblob into a CAPI type keyblob
if (!CryptDecodeObject(X509_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY,
pPrivateKeyInfo->PrivateKey.pbData,
pPrivateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&cbRSAPrivateKey))
goto ErrorReturn;
if (NULL == (pbRSAPrivateKey = (BYTE *) SSAlloc(cbRSAPrivateKey)))
goto ErrorReturn;
if (!CryptDecodeObject(X509_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY,
pPrivateKeyInfo->PrivateKey.pbData,
pPrivateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_NOCOPY_FLAG,
pbRSAPrivateKey,
&cbRSAPrivateKey))
goto ErrorReturn;
// figure out what keyspec to use and manually set the algid in the keyblob accordingly
dwKeySpec = ResolveKeySpec(pPrivateKeyInfo->pAttributes);
if ((dwKeySpec == AT_KEYEXCHANGE) || (dwKeySpec == 0))
((BLOBHEADER *) pbRSAPrivateKey)->aiKeyAlg = CALG_RSA_KEYX;
else
((BLOBHEADER *) pbRSAPrivateKey)->aiKeyAlg = CALG_RSA_SIGN;
// import this thing
if (!CryptImportKey(hCryptProv,
pbRSAPrivateKey,
cbRSAPrivateKey,
0,
dwFlags & (CRYPT_EXPORTABLE | CRYPT_USER_PROTECTED), // mask the flags that are used
&hCryptKey)) // during the CryptImportKey
goto ErrorReturn;
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
CommonReturn:
if (pbRSAPrivateKey)
SSFree(pbRSAPrivateKey);
if (hCryptKey)
CryptDestroyKey(hCryptKey);
return fResult;
}
#ifndef DSS2
#define DSS2 ((DWORD)'D'+((DWORD)'S'<<8)+((DWORD)'S'<<16)+((DWORD)'2'<<24))
#endif
#ifndef DSS_Q_LEN
#define DSS_Q_LEN 20
#endif
// hack function to create a mock RSA private key blob based only on size
BYTE * AllocFakeDSSPrivateKey(DWORD cb)
{
BLOBHEADER *pBlobHeader;
DSSPUBKEY *pCspPubKey = NULL;
BYTE *pbKeyBlob;
BYTE *pbKey;
DWORD cbKey;
DSSSEED *pCspSeed = NULL;
pBlobHeader = (BLOBHEADER *) SSAlloc(cb);
if (pBlobHeader == NULL)
return NULL;
memset(pBlobHeader, 0, cb);
pbKeyBlob = (BYTE *) pBlobHeader;
pCspPubKey = (DSSPUBKEY *) (pbKeyBlob + sizeof(BLOBHEADER));
pbKey = pbKeyBlob + sizeof(BLOBHEADER) + sizeof(DSSPUBKEY);
// BLOBHEADER
pBlobHeader->bType = PRIVATEKEYBLOB;
pBlobHeader->bVersion = CUR_BLOB_VERSION;
pBlobHeader->reserved = 0;
pBlobHeader->aiKeyAlg = CALG_DSS_SIGN;
// DSSPUBKEY
pCspPubKey->magic = DSS2;
cbKey = (cb - sizeof(BLOBHEADER) - sizeof(DSSPUBKEY) - (2 * DSS_Q_LEN) - sizeof(DSSSEED)) / 2;
pCspPubKey->bitlen = cbKey * 8;
// put some bogus data at the start of the key so
// that we know will be unique for each key so that
// they look different durring a comparison
InterlockedIncrement(&counter);
// rgbP[cbKey]
memset(pbKey, counter, cbKey);
pbKey += cbKey;
*(pbKey-1) = 0x80;
// rgbQ[20]
memset(pbKey, counter, DSS_Q_LEN);
pbKey += DSS_Q_LEN;
*(pbKey-1) = 0x80;
// rgbG[cbKey]
memset(pbKey, counter, cbKey);
pbKey += cbKey;
*(pbKey-1) = 0x80;
// rgbX[20]
memset(pbKey, counter, DSS_Q_LEN);
pbKey += DSS_Q_LEN;
*(pbKey-1) = 0x80;
// DSSSEED: set counter to 0xFFFFFFFF to indicate not available
pCspSeed = (DSSSEED *) pbKey;
memset(&pCspSeed->counter, 0xFF, sizeof(pCspSeed->counter));
return ((BYTE *)pBlobHeader);
}
static BOOL WINAPI ExportDSSPrivateKeyInfo(
HCRYPTPROV hCryptProv, // in
DWORD dwKeySpec, // in
LPSTR pszPrivateKeyObjId, // in
DWORD dwFlags, // in
void *pvAuxInfo, // in
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo, // out
DWORD *pcbPrivateKeyInfo // in, out
)
{
BOOL fResult = TRUE;
HCRYPTKEY hCryptKey = NULL;
BYTE *pbKeyBlob = NULL;
DWORD cbKeyBlob = 0;
BYTE *pbEncodedPrivateKeyBlob = NULL;
DWORD cbEncodedPrivateKeyBlob = 0;
BYTE *pbEncodedParameters = NULL;
DWORD cbEncodedParameters = 0;
CRYPT_INTEGER_BLOB PrivateKeyBlob;
CERT_DSS_PARAMETERS DssParameters;
DWORD cbKey;
DSSPUBKEY *pCspPubKey = NULL;
BYTE *pbBytes;
DWORD dwSize = 0;
BYTE *pbCurrentLocation;
// get a handle to the keyset to export
if (!CryptGetUserKey(
hCryptProv,
dwKeySpec,
&hCryptKey))
goto ErrorReturn;
// export the key set to a CAPI blob
if (!CryptExportKey(
hCryptKey,
0,
PRIVATEKEYBLOB,
0,
NULL,
&cbKeyBlob))
goto ErrorReturn;
// make sure the caller REALLY wants the key at this point
if ((dwFlags & PFX_MODE) && !(dwFlags & GIVE_ME_DATA))
{
if (NULL == (pbKeyBlob = AllocFakeDSSPrivateKey(cbKeyBlob)))
goto ErrorReturn;
}
// if not in PFX export mode or we really want the key then just do normal processing
else
{
if (NULL == (pbKeyBlob = (BYTE *) SSAlloc(cbKeyBlob)))
goto ErrorReturn;
if (!CryptExportKey(
hCryptKey,
0,
PRIVATEKEYBLOB,
0,
pbKeyBlob,
&cbKeyBlob))
goto ErrorReturn;
}
pCspPubKey = (DSSPUBKEY *) (pbKeyBlob + sizeof(BLOBHEADER));
pbBytes = pbKeyBlob + sizeof(PUBLICKEYSTRUC) + sizeof(DSSPUBKEY);
cbKey = pCspPubKey->bitlen / 8;
// encode the DSS paramaters
memset(&DssParameters, 0, sizeof(CERT_DSS_PARAMETERS));
DssParameters.p.cbData = cbKey;
DssParameters.p.pbData = pbBytes;
pbBytes += cbKey;
DssParameters.q.cbData = DSS_Q_LEN;
DssParameters.q.pbData = pbBytes;
pbBytes += DSS_Q_LEN;
DssParameters.g.cbData = cbKey;
DssParameters.g.pbData = pbBytes;
pbBytes += cbKey;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_DSS_PARAMETERS,
&DssParameters,
NULL,
&cbEncodedParameters))
goto ErrorReturn;
if (NULL == (pbEncodedParameters = (BYTE *) SSAlloc(cbEncodedParameters)))
goto ErrorReturn;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_DSS_PARAMETERS,
&DssParameters,
pbEncodedParameters,
&cbEncodedParameters))
goto ErrorReturn;
// encode the key DSS private key
PrivateKeyBlob.cbData = DSS_Q_LEN;
PrivateKeyBlob.pbData = pbBytes;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_MULTI_BYTE_INTEGER,
&PrivateKeyBlob,
NULL,
&cbEncodedPrivateKeyBlob))
goto ErrorReturn;
if (NULL == (pbEncodedPrivateKeyBlob = (BYTE *) SSAlloc(cbEncodedPrivateKeyBlob)))
goto ErrorReturn;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_MULTI_BYTE_INTEGER,
&PrivateKeyBlob,
pbEncodedPrivateKeyBlob,
&cbEncodedPrivateKeyBlob))
goto ErrorReturn;
// we can now calculate the size needed
dwSize = sizeof(CRYPT_PRIVATE_KEY_INFO) + // main private key info struct
sizeof(szOID_X957_DSA) + // size of the DSA algorithm identifier string
cbEncodedParameters + // size of the DSA parameters
cbEncodedPrivateKeyBlob; // buffer that holds encoded DSS private key
// check to see if the caller passed in a buffer, and enough space
if (pPrivateKeyInfo == NULL)
goto CommonReturn;
else if (*pcbPrivateKeyInfo < dwSize) {
SetLastError((DWORD) ERROR_MORE_DATA);
goto ErrorReturn;
}
// everything is OK so copy all the information to the caller's buffer
pbCurrentLocation = ((BYTE *) pPrivateKeyInfo) + sizeof(CRYPT_PRIVATE_KEY_INFO);
pPrivateKeyInfo->Version = 0;
pPrivateKeyInfo->Algorithm.pszObjId = (LPSTR) pbCurrentLocation;
memcpy(pbCurrentLocation, szOID_X957_DSA, sizeof(szOID_X957_DSA));
pbCurrentLocation += sizeof(szOID_X957_DSA);
pPrivateKeyInfo->Algorithm.Parameters.cbData = cbEncodedParameters;
pPrivateKeyInfo->Algorithm.Parameters.pbData = pbCurrentLocation;
memcpy(pbCurrentLocation, pbEncodedParameters, cbEncodedParameters);
pbCurrentLocation += cbEncodedParameters;
pPrivateKeyInfo->PrivateKey.cbData = cbEncodedPrivateKeyBlob;
pPrivateKeyInfo->PrivateKey.pbData = pbCurrentLocation;
memcpy(pbCurrentLocation, pbEncodedPrivateKeyBlob, cbEncodedPrivateKeyBlob);
pbCurrentLocation += cbEncodedPrivateKeyBlob;
pPrivateKeyInfo->pAttributes = NULL;
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
CommonReturn:
*pcbPrivateKeyInfo = dwSize;
if (hCryptKey)
{
DWORD dwErr = GetLastError();
CryptDestroyKey(hCryptKey);
SetLastError(dwErr);
}
if (pbKeyBlob)
SSFree(pbKeyBlob);
if (pbEncodedParameters)
SSFree(pbEncodedParameters);
if (pbEncodedPrivateKeyBlob)
SSFree(pbEncodedPrivateKeyBlob);
return fResult;
}
static BOOL WINAPI ImportDSSPrivateKeyInfo(
HCRYPTPROV hCryptProv, // in
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo, // in
DWORD dwFlags, // in, optional
void *pvAuxInfo // in, optional
)
{
BOOL fResult = TRUE;
DWORD cbDSSPrivateKey = 0;
CRYPT_DATA_BLOB *pbDSSPrivateKey = NULL;
HCRYPTKEY hCryptKey = NULL;
DWORD dwKeySpec = 0;
DWORD cbParameters = 0;
PCERT_DSS_PARAMETERS pDssParameters = NULL;
BLOBHEADER *pPrivateKeyBlob = NULL;
DWORD cbPrivateKeyStruc = 0;
DSSPUBKEY *pCspPubKey = NULL;
DSSSEED *pCspSeed = NULL;
BYTE *pbKey = NULL;
BYTE *pbKeyBlob = NULL;
DWORD cb;
DWORD cbKey;
// decode the DSS private key
if (!CryptDecodeObject(X509_ASN_ENCODING,
X509_MULTI_BYTE_UINT,
pPrivateKeyInfo->PrivateKey.pbData,
pPrivateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&cbDSSPrivateKey))
goto ErrorReturn;
if (NULL == (pbDSSPrivateKey = (CRYPT_DATA_BLOB *) SSAlloc(cbDSSPrivateKey)))
{
SetLastError(E_OUTOFMEMORY);
goto ErrorReturn;
}
if (!CryptDecodeObject(X509_ASN_ENCODING,
X509_MULTI_BYTE_UINT,
pPrivateKeyInfo->PrivateKey.pbData,
pPrivateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_NOCOPY_FLAG,
pbDSSPrivateKey,
&cbDSSPrivateKey))
goto ErrorReturn;
// decode the DSS parameters
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_DSS_PARAMETERS,
pPrivateKeyInfo->Algorithm.Parameters.pbData,
pPrivateKeyInfo->Algorithm.Parameters.cbData,
0,
NULL,
&cbParameters
))
goto ErrorReturn;
if (NULL == (pDssParameters = (PCERT_DSS_PARAMETERS) SSAlloc(cbParameters)))
{
SetLastError(E_OUTOFMEMORY);
goto ErrorReturn;
}
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_DSS_PARAMETERS,
pPrivateKeyInfo->Algorithm.Parameters.pbData,
pPrivateKeyInfo->Algorithm.Parameters.cbData,
0,
pDssParameters,
&cbParameters
))
goto ErrorReturn;
// The CAPI private key representation consists of the following sequence:
// - BLOBHEADER
// - DSSPUBKEY
// - rgbP[cbKey]
// - rgbQ[20]
// - rgbG[cbKey]
// - rgbX[20]
// - DSSSEED
cbKey = pDssParameters->p.cbData;
if (0 == cbKey)
goto ErrorInvalidKey;
cbPrivateKeyStruc = sizeof(BLOBHEADER) + sizeof(DSSPUBKEY) +
cbKey + DSS_Q_LEN + cbKey + DSS_Q_LEN + sizeof(DSSSEED);
if (NULL == (pPrivateKeyBlob = (BLOBHEADER *) SSAlloc(cbPrivateKeyStruc)))
{
SetLastError(E_OUTOFMEMORY);
goto ErrorReturn;
}
pbKeyBlob = (BYTE *) pPrivateKeyBlob;
pCspPubKey = (DSSPUBKEY *) (pbKeyBlob + sizeof(BLOBHEADER));
pbKey = pbKeyBlob + sizeof(BLOBHEADER) + sizeof(DSSPUBKEY);
// NOTE, the length of G can be less than the length of P.
// The CSP requires G to be padded out with 0x00 bytes if it
// is less and in little endian form
// BLOBHEADER
pPrivateKeyBlob->bType = PRIVATEKEYBLOB;
pPrivateKeyBlob->bVersion = CUR_BLOB_VERSION;
pPrivateKeyBlob->reserved = 0;
pPrivateKeyBlob->aiKeyAlg = CALG_DSS_SIGN;
// DSSPUBKEY
pCspPubKey->magic = DSS2;
pCspPubKey->bitlen = cbKey * 8;
// rgbP[cbKey]
memcpy(pbKey, pDssParameters->p.pbData, cbKey);
pbKey += cbKey;
// rgbQ[20]
cb = pDssParameters->q.cbData;
if (0 == cb || cb > DSS_Q_LEN)
goto ErrorInvalidKey;
memcpy(pbKey, pDssParameters->q.pbData, cb);
if (DSS_Q_LEN > cb)
memset(pbKey + cb, 0, DSS_Q_LEN - cb);
pbKey += DSS_Q_LEN;
// rgbG[cbKey]
cb = pDssParameters->g.cbData;
if (0 == cb || cb > cbKey)
goto ErrorInvalidKey;
memcpy(pbKey, pDssParameters->g.pbData, cb);
if (cbKey > cb)
memset(pbKey + cb, 0, cbKey - cb);
pbKey += cbKey;
// rgbX[20]
cb = pbDSSPrivateKey->cbData;
if (0 == cb || cb > DSS_Q_LEN)
goto ErrorInvalidKey;
memcpy(pbKey, pbDSSPrivateKey->pbData, cb);
if (DSS_Q_LEN > cb)
memset(pbKey + cb, 0, DSS_Q_LEN - cb);
pbKey += DSS_Q_LEN;
// DSSSEED: set counter to 0xFFFFFFFF to indicate not available
pCspSeed = (DSSSEED *) pbKey;
memset(&pCspSeed->counter, 0xFF, sizeof(pCspSeed->counter));
// import this thing
if (!CryptImportKey(hCryptProv,
(BYTE *)pPrivateKeyBlob,
cbPrivateKeyStruc,
0,
dwFlags & (CRYPT_EXPORTABLE | CRYPT_USER_PROTECTED), // mask the flags that are used
&hCryptKey)) // during the CryptImportKey
{
DWORD dw = GetLastError();
goto ErrorReturn;
}
goto CommonReturn;
ErrorInvalidKey:
SetLastError(E_INVALIDARG);
ErrorReturn:
fResult = FALSE;
CommonReturn:
if (pbDSSPrivateKey)
SSFree(pbDSSPrivateKey);
if (pDssParameters)
SSFree(pDssParameters);
if (pPrivateKeyBlob)
SSFree(pPrivateKeyBlob);
if (hCryptKey)
CryptDestroyKey(hCryptKey);
return fResult;
}