//+-------------------------------------------------------------------------
//
//  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;

}