1424 lines
41 KiB
C++
1424 lines
41 KiB
C++
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
|
|
}
|
|
|
|
|