1485 lines
49 KiB
C++
1485 lines
49 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
//
|
|
// File: nscp.cpp
|
|
//
|
|
// Contents: PFX: Personal Information Exchange.
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 02-Jun-97 mattt created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
#include "global.hxx"
|
|
|
|
#include <wincrypt.h>
|
|
#include "shacomm.h"
|
|
#include "des.h"
|
|
#include "tripldes.h"
|
|
#include "modes.h"
|
|
|
|
#define _PFX_SOURCE_
|
|
#include "dbgdef.h"
|
|
|
|
extern "C" {
|
|
#include "pfxnscp.h" // ASN1 generated
|
|
}
|
|
|
|
#include "pfxhelp.h"
|
|
#include "pfxcrypt.h"
|
|
#include "pfxcmn.h"
|
|
|
|
#define DES_BLOCKLEN 8
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
// OLD PKCS #12 Object Identifiers - these are for supporting the old netscape file format
|
|
#define OLD_szOID_PKCS_12_OIDs szOID_PKCS_12 ".5" // 1.2.840.113549.1.12.5
|
|
#define OLD_szOID_PKCS_12_PbeIds OLD_szOID_PKCS_12_OIDs ".1"
|
|
#define OLD_szOID_PKCS_12_pbeWithSHA1And128BitRC4 OLD_szOID_PKCS_12_PbeIds ".1"
|
|
#define OLD_szOID_PKCS_12_pbeWithSHA1And40BitRC4 OLD_szOID_PKCS_12_PbeIds ".2"
|
|
#define OLD_szOID_PKCS_12_pbeWithSHA1AndTripleDES OLD_szOID_PKCS_12_PbeIds ".3"
|
|
#define OLD_szOID_PKCS_12_pbeWithSHA1And128BitRC2 OLD_szOID_PKCS_12_PbeIds ".4"
|
|
#define OLD_szOID_PKCS_12_pbeWithSHA1And40BitRC2 OLD_szOID_PKCS_12_PbeIds ".5"
|
|
|
|
#define OLD_szOID_PKCS_12_EnvelopingIds OLD_szOID_PKCS_12_OIDs ".2"
|
|
#define OLD_szOID_PKCS_12_rsaEncryptionWith128BitRC4 OLD_szOID_PKCS_12_EnvelopingIds ".1"
|
|
#define OLD_szOID_PKCS_12_rsaEncryptionWith40BitRC4 OLD_szOID_PKCS_12_EnvelopingIds ".2"
|
|
#define OLD_szOID_PKCS_12_rsaEncryptionWithTripleDES OLD_szOID_PKCS_12_EnvelopingIds ".3"
|
|
|
|
#define OLD_szOID_PKCS_12_SignatureIds OLD_szOID_PKCS_12_OIDs ".3"
|
|
#define OLD_szOID_PKCS_12_rsaSignatureWithSHA1Digest OLD_szOID_PKCS_12_SignatureIds ".1"
|
|
|
|
#define OLD_szOID_PKCS_12_ModeIDs OLD_szOID_PKCS_12 ".1" // 1.2.840.113549.1.12.1
|
|
#define OLD_szOID_PKCS_12_PubKeyMode OLD_szOID_PKCS_12_ModeIDs ".1" // 1.2.840.113549.1.12.1.1
|
|
#define OLD_szOID_PKCS_12_PasswdMode OLD_szOID_PKCS_12_ModeIDs ".2" // 1.2.840.113549.1.12.1.2
|
|
#define OLD_szOID_PKCS_12_offlineTransportMode OLD_szOID_PKCS_12_ModeIds ".1" // obsolete
|
|
#define OLD_szOID_PKCS_12_onlineTransportMode OLD_szOID_PKCS_12_ModeIds ".2" // obsolete
|
|
|
|
#define OLD_szOID_PKCS_12_EspvkIDs OLD_szOID_PKCS_12 ".2" // 1.2.840.113549.1.12.2
|
|
#define OLD_szOID_PKCS_12_KeyShrouding OLD_szOID_PKCS_12_EspvkIDs ".1" // 1.2.840.113549.1.12.2.1
|
|
|
|
#define OLD_szOID_PKCS_12_BagIDs OLD_szOID_PKCS_12 ".3" // obsolete
|
|
#define OLD_szOID_PKCS_12_KeyBagIDs OLD_szOID_PKCS_12_BagIDs ".1" // obsolete
|
|
#define OLD_szOID_PKCS_12_CertCrlBagIDs OLD_szOID_PKCS_12_BagIDs ".2" // obsolete
|
|
#define OLD_szOID_PKCS_12_SecretBagIDs OLD_szOID_PKCS_12_BagIDs ".3" // obsolete
|
|
#define OLD_szOID_PKCS_12_SafeCntIDs OLD_szOID_PKCS_12_BagIDs ".4" // obsolete
|
|
#define OLD_szOID_PKCS_12_ShrKeyBagIDs OLD_szOID_PKCS_12_BagIDs ".5" // obsolete
|
|
|
|
#define OLD_szOID_PKCS_12_CertBagIDs OLD_szOID_PKCS_12 ".4" // obsolete
|
|
#define OLD_szOID_PKCS_12_x509CertCrlBagIDs OLD_szOID_PKCS_12_CertBagIDs ".1" // obsolete
|
|
#define OLD_szOID_PKCS_12_sdsiCertBagIDs OLD_szOID_PKCS_12_CertBagIDs ".2" // obsolete
|
|
|
|
|
|
static HCRYPTASN1MODULE hNSCPAsn1Module;
|
|
|
|
// fwd
|
|
//BOOL FNSCPDumpSafeCntsToHPFX(SafeContents* pSafeCnts, HPFX hpfx);
|
|
|
|
|
|
BOOL InitNSCP()
|
|
{
|
|
#ifdef OSS_CRYPT_ASN1
|
|
if (0 == (hNSCPAsn1Module = I_CryptInstallAsn1Module(pfxnscp, 0, NULL)) )
|
|
return FALSE;
|
|
#else
|
|
PFXNSCP_Module_Startup();
|
|
if (0 == (hNSCPAsn1Module = I_CryptInstallAsn1Module(
|
|
PFXNSCP_Module, 0, NULL))) {
|
|
PFXNSCP_Module_Cleanup();
|
|
return FALSE;
|
|
}
|
|
#endif // OSS_CRYPT_ASN1
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL TerminateNSCP()
|
|
{
|
|
I_CryptUninstallAsn1Module(hNSCPAsn1Module);
|
|
#ifndef OSS_CRYPT_ASN1
|
|
PFXNSCP_Module_Cleanup();
|
|
#endif // OSS_CRYPT_ASN1
|
|
return TRUE;
|
|
}
|
|
|
|
static inline ASN1decoding_t GetDecoder(void)
|
|
{
|
|
return I_CryptGetAsn1Decoder(hNSCPAsn1Module);
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Function: INSCP_Asn1ToObjectID
|
|
//
|
|
// Synopsis: Convert a dotted string oid to an ASN1 ObjectID
|
|
//
|
|
// Returns: FALSE iff failed
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
INSCP_Asn1ToObjectID(
|
|
IN OID oid,
|
|
OUT ObjectID *pooid
|
|
)
|
|
{
|
|
BOOL fRet;
|
|
|
|
pooid->count = 16;
|
|
if (!PkiAsn1ToObjectIdentifier(
|
|
oid,
|
|
&pooid->count,
|
|
pooid->value))
|
|
goto PkiAsn1ToObjectIdentifierError;
|
|
|
|
fRet = TRUE;
|
|
CommonReturn:
|
|
return fRet;
|
|
|
|
ErrorReturn:
|
|
SetLastError(CRYPT_E_OID_FORMAT);
|
|
fRet = FALSE;
|
|
goto CommonReturn;
|
|
TRACE_ERROR(PkiAsn1ToObjectIdentifierError)
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Function: INSCP_Asn1FromObjectID
|
|
//
|
|
// Synopsis: Convert an ASN1 ObjectID to a dotted string oid
|
|
//
|
|
// Returns: FALSE iff failed
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
INSCP_Asn1FromObjectID(
|
|
IN ObjectID *pooid,
|
|
OUT OID *poid
|
|
)
|
|
{
|
|
BOOL fRet;
|
|
OID oid = NULL;
|
|
DWORD cb;
|
|
|
|
if (!PkiAsn1FromObjectIdentifier(
|
|
pooid->count,
|
|
pooid->value,
|
|
NULL,
|
|
&cb))
|
|
goto PkiAsn1FromObjectIdentifierSizeError;
|
|
if (NULL == (oid = (OID)SSAlloc( cb)))
|
|
goto OidAllocError;
|
|
if (!PkiAsn1FromObjectIdentifier(
|
|
pooid->count,
|
|
pooid->value,
|
|
oid,
|
|
&cb))
|
|
goto PkiAsn1FromObjectIdentifierError;
|
|
|
|
fRet = TRUE;
|
|
CommonReturn:
|
|
*poid = oid;
|
|
return fRet;
|
|
|
|
ErrorReturn:
|
|
SSFree(oid);
|
|
fRet = FALSE;
|
|
goto CommonReturn;
|
|
TRACE_ERROR(OidAllocError)
|
|
SET_ERROR(PkiAsn1FromObjectIdentifierSizeError , CRYPT_E_OID_FORMAT)
|
|
SET_ERROR(PkiAsn1FromObjectIdentifierError , CRYPT_E_OID_FORMAT)
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Function: INSCP_EqualObjectIDs
|
|
//
|
|
// Compare 2 OSS object id's.
|
|
//
|
|
// Returns: FALSE iff !equal
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
INSCP_EqualObjectIDs(
|
|
IN ObjectID *poid1,
|
|
IN ObjectID *poid2)
|
|
{
|
|
BOOL fRet;
|
|
DWORD i;
|
|
PDWORD pdw1;
|
|
PDWORD pdw2;
|
|
|
|
if (poid1->count != poid2->count)
|
|
goto Unequal;
|
|
for (i=poid1->count, pdw1=poid1->value, pdw2=poid2->value;
|
|
(i>0) && (*pdw1==*pdw2);
|
|
i--, pdw1++, pdw2++)
|
|
;
|
|
if (i>0)
|
|
goto Unequal;
|
|
|
|
fRet = TRUE; // equal
|
|
CommonReturn:
|
|
return fRet;
|
|
|
|
Unequal:
|
|
fRet = FALSE; // !equal
|
|
goto CommonReturn;
|
|
}
|
|
|
|
|
|
|
|
//+ --------------------------------------------------------------
|
|
// in NSCP's initial implementation of PFX020, this
|
|
// is the algorithm they used to derive a key from a password.
|
|
// ACTUALLY, they have two slightly different methods of generating
|
|
// a key, this is the one needed to decrypt the baggage.
|
|
// We include it so we can interoperate.
|
|
BOOL NCSPDeriveBaggageDecryptionKey(
|
|
LPCWSTR szPassword,
|
|
int iPKCS5Iterations,
|
|
PBYTE pbPKCS5Salt,
|
|
DWORD cbPKCS5Salt,
|
|
PBYTE pbDerivedMaterial,
|
|
DWORD cbDerivedMaterial)
|
|
{
|
|
|
|
BOOL fRet = TRUE;
|
|
LPSTR szASCIIPassword = NULL;
|
|
DWORD cbASCIIPassword = 0;
|
|
DWORD i;
|
|
BYTE paddedPKCS5Salt[20];
|
|
BYTE *pbTempPKCS5Salt = NULL;
|
|
DWORD cbTempPKCS5Salt = 0;
|
|
|
|
BYTE rgbPKCS5Key[A_SHA_DIGEST_LEN];
|
|
|
|
// for some reason the password is used as ASCII in this key derivation
|
|
// so change it from unicode to ASCII
|
|
if (0 == (cbASCIIPassword = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
szPassword,
|
|
-1,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL))) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (NULL == (szASCIIPassword = (LPSTR) SSAlloc(cbASCIIPassword)))
|
|
goto ErrorReturn;
|
|
|
|
if (0 == (cbASCIIPassword = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
szPassword,
|
|
-1,
|
|
szASCIIPassword,
|
|
cbASCIIPassword,
|
|
NULL,
|
|
NULL))) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// get rid of the NULL character, Netscape doesn't include it
|
|
cbASCIIPassword--;
|
|
|
|
// because of a Netscape bug the minimum length of password + salt is 20,
|
|
// if the password + salt is less than 20 they pad with 0's.
|
|
// so, check to see if the password + salt is less than 20, if so then pad the
|
|
// salt since it will be appended to the password.
|
|
if (cbASCIIPassword+cbPKCS5Salt < 20) {
|
|
// reset the pbPKCS5Salt pointer to a local buffer
|
|
// which is padded with 0's, and adjust cbPKCS5Salt
|
|
memset(paddedPKCS5Salt, 0, 20);
|
|
memcpy(paddedPKCS5Salt, pbPKCS5Salt, cbPKCS5Salt);
|
|
pbTempPKCS5Salt = paddedPKCS5Salt;
|
|
cbTempPKCS5Salt = 20 - cbASCIIPassword;
|
|
}
|
|
else {
|
|
pbTempPKCS5Salt = pbPKCS5Salt;
|
|
cbTempPKCS5Salt = cbPKCS5Salt;
|
|
}
|
|
|
|
|
|
// use PKCS#5 to generate initial bit stream (seed)
|
|
if (!PKCS5_GenKey(
|
|
iPKCS5Iterations,
|
|
(BYTE *)szASCIIPassword,
|
|
cbASCIIPassword,
|
|
pbTempPKCS5Salt,
|
|
cbTempPKCS5Salt,
|
|
rgbPKCS5Key))
|
|
goto Ret;
|
|
|
|
// if there isn't engough key material, then use PHash to generate more
|
|
if (cbDerivedMaterial > sizeof(rgbPKCS5Key))
|
|
{
|
|
// P_hash (secret, seed) = HMAC_hash (secret, A(0) + seed),
|
|
// HMAC_hash (secret, A(1) + seed),
|
|
// HMAC_hash (secret, A(2) + seed),
|
|
// HMAC_hash (secret, A(3) + seed) ...
|
|
// where
|
|
// A(0) = seed
|
|
// A(i) = HMAC_hash(secret, A(i-1))
|
|
// seed = PKCS5 salt for PKCS5 PBE param
|
|
// secret = normal PKCS5 hashed key
|
|
|
|
if (!P_Hash (
|
|
rgbPKCS5Key,
|
|
sizeof(rgbPKCS5Key),
|
|
|
|
pbPKCS5Salt,
|
|
cbPKCS5Salt,
|
|
|
|
pbDerivedMaterial, // output
|
|
cbDerivedMaterial, // # of output bytes requested
|
|
TRUE) ) // NSCP compat mode?
|
|
goto Ret;
|
|
}
|
|
else
|
|
{
|
|
// we already have enough bits to satisfy the request
|
|
CopyMemory(pbDerivedMaterial, rgbPKCS5Key, cbDerivedMaterial);
|
|
}
|
|
|
|
goto Ret;
|
|
|
|
ErrorReturn:
|
|
fRet = FALSE;
|
|
Ret:
|
|
if (szASCIIPassword)
|
|
SSFree(szASCIIPassword);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
// this function will create a SAFE_BAG structure contained in a single buffer
|
|
// for the given encoded private key, friendly name, and local key ID
|
|
static
|
|
BOOL
|
|
SetupKeyBag (
|
|
SAFE_BAG **ppKeyBag,
|
|
DWORD dwLocalKeyID,
|
|
BYTE *pbFriendlyName,
|
|
DWORD cbFriendlyName,
|
|
BYTE *pbEncodedPrivateKey,
|
|
DWORD cbEncodedPrivateKey
|
|
)
|
|
{
|
|
|
|
BOOL fRet = TRUE;
|
|
SAFE_BAG *pSafeBag;
|
|
DWORD cbBytesNeeded = sizeof(SAFE_BAG);
|
|
DWORD dwKeyID = 0;
|
|
CRYPT_ATTR_BLOB keyID;
|
|
CERT_NAME_VALUE wideFriendlyName;
|
|
BYTE *pbEncodedLocalKeyID = NULL;
|
|
DWORD cbEncodedLocalKeyID = 0;
|
|
BYTE *pbEncodedFriendlyName = NULL;
|
|
DWORD cbEncodedFriendlyName = 0;
|
|
BYTE *pbCurrentBufferLocation = NULL;
|
|
|
|
keyID.pbData = (BYTE *) &dwKeyID;
|
|
keyID.cbData = sizeof(DWORD);
|
|
dwKeyID = dwLocalKeyID;
|
|
|
|
// calculate the size needed for a buffer to fit all the SAFE_BAG information
|
|
cbBytesNeeded += strlen(szOID_PKCS_12_KEY_BAG) + 1;
|
|
cbBytesNeeded += cbEncodedPrivateKey;
|
|
cbBytesNeeded += sizeof(CRYPT_ATTRIBUTE) * 2;
|
|
cbBytesNeeded += strlen(szOID_PKCS_12_LOCAL_KEY_ID) + 1;
|
|
cbBytesNeeded += sizeof(CRYPT_ATTR_BLOB);
|
|
|
|
// encode the keyID attribute
|
|
if (!CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
&keyID,
|
|
NULL,
|
|
&cbEncodedLocalKeyID)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (NULL == (pbEncodedLocalKeyID = (BYTE *) SSAlloc(cbEncodedLocalKeyID)))
|
|
goto ErrorReturn;
|
|
|
|
cbBytesNeeded += cbEncodedLocalKeyID;
|
|
|
|
if (!CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
&keyID,
|
|
pbEncodedLocalKeyID,
|
|
&cbEncodedLocalKeyID)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
cbBytesNeeded += strlen(szOID_PKCS_12_FRIENDLY_NAME_ATTR) + 1;
|
|
cbBytesNeeded += sizeof(CRYPT_ATTR_BLOB);
|
|
|
|
// encode the friendly name attribute
|
|
wideFriendlyName.dwValueType = CERT_RDN_BMP_STRING;
|
|
wideFriendlyName.Value.pbData = pbFriendlyName;
|
|
wideFriendlyName.Value.cbData = 0;
|
|
|
|
if (!CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
(void *)&wideFriendlyName,
|
|
NULL,
|
|
&cbEncodedFriendlyName)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (NULL == (pbEncodedFriendlyName = (BYTE *) SSAlloc(cbEncodedFriendlyName)))
|
|
goto ErrorReturn;
|
|
|
|
cbBytesNeeded += cbEncodedFriendlyName;
|
|
|
|
if (!CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
(void *)&wideFriendlyName,
|
|
pbEncodedFriendlyName,
|
|
&cbEncodedFriendlyName)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// now allocate space for the all the SAFE_BAG data and copy the data into the buffer
|
|
if (NULL == (pSafeBag = (SAFE_BAG *) SSAlloc(cbBytesNeeded)))
|
|
goto ErrorReturn;
|
|
|
|
memset(pSafeBag, 0, cbBytesNeeded);
|
|
|
|
// set current buffer location to be at the end of the SAFE_BAG
|
|
// structure which is at the head of the buffer
|
|
pbCurrentBufferLocation = ((BYTE *) pSafeBag) + sizeof(SAFE_BAG);
|
|
|
|
// copy key bag type OID
|
|
pSafeBag->pszBagTypeOID = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR) pbCurrentBufferLocation, szOID_PKCS_12_KEY_BAG);
|
|
pbCurrentBufferLocation += strlen(szOID_PKCS_12_KEY_BAG) + 1;
|
|
|
|
// copy the private key
|
|
pSafeBag->BagContents.pbData = pbCurrentBufferLocation;
|
|
pSafeBag->BagContents.cbData = cbEncodedPrivateKey;
|
|
memcpy(pbCurrentBufferLocation, pbEncodedPrivateKey, cbEncodedPrivateKey);
|
|
pbCurrentBufferLocation += cbEncodedPrivateKey;
|
|
|
|
// create space for the attributes array
|
|
pSafeBag->Attributes.cAttr = 2;
|
|
pSafeBag->Attributes.rgAttr = (CRYPT_ATTRIBUTE *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTRIBUTE) * 2;
|
|
|
|
// copy the local key ID attribute and value
|
|
pSafeBag->Attributes.rgAttr[0].pszObjId = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR) pbCurrentBufferLocation, szOID_PKCS_12_LOCAL_KEY_ID);
|
|
pbCurrentBufferLocation += strlen(szOID_PKCS_12_LOCAL_KEY_ID) + 1;
|
|
pSafeBag->Attributes.rgAttr[0].cValue = 1;
|
|
pSafeBag->Attributes.rgAttr[0].rgValue = (CRYPT_ATTR_BLOB *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTR_BLOB);
|
|
pSafeBag->Attributes.rgAttr[0].rgValue->cbData = cbEncodedLocalKeyID;
|
|
pSafeBag->Attributes.rgAttr[0].rgValue->pbData = pbCurrentBufferLocation;
|
|
memcpy(pbCurrentBufferLocation, pbEncodedLocalKeyID, cbEncodedLocalKeyID);
|
|
pbCurrentBufferLocation += cbEncodedLocalKeyID;
|
|
|
|
// copy the friendly name attribute and value
|
|
pSafeBag->Attributes.rgAttr[1].pszObjId = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR) pbCurrentBufferLocation, szOID_PKCS_12_FRIENDLY_NAME_ATTR);
|
|
pbCurrentBufferLocation += strlen(szOID_PKCS_12_FRIENDLY_NAME_ATTR) + 1;
|
|
pSafeBag->Attributes.rgAttr[1].cValue = 1;
|
|
pSafeBag->Attributes.rgAttr[1].rgValue = (CRYPT_ATTR_BLOB *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTR_BLOB);
|
|
pSafeBag->Attributes.rgAttr[1].rgValue->cbData = cbEncodedFriendlyName;
|
|
pSafeBag->Attributes.rgAttr[1].rgValue->pbData = pbCurrentBufferLocation;
|
|
memcpy(pbCurrentBufferLocation, pbEncodedFriendlyName, cbEncodedFriendlyName);
|
|
|
|
*ppKeyBag = pSafeBag;
|
|
|
|
goto Ret;
|
|
|
|
ErrorReturn:
|
|
fRet = FALSE;
|
|
Ret:
|
|
if (pbEncodedLocalKeyID)
|
|
SSFree(pbEncodedLocalKeyID);
|
|
if (pbEncodedFriendlyName)
|
|
SSFree(pbEncodedFriendlyName);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
// this function will extract a private key from the baggage structure handed in
|
|
// and put the private key in a SAFE_BAG structure, where all the data of the
|
|
// SAFE_BAG is contained in a single in a single buffer
|
|
static
|
|
BOOL
|
|
ExtractKeyFromBaggage(
|
|
Baggage baggage,
|
|
SAFE_BAG **ppKeyBag,
|
|
LPCWSTR szPassword,
|
|
DWORD dwLocalKeyID,
|
|
BYTE **ppbCertThumbprint
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
DWORD dwErr;
|
|
|
|
DWORD cbEncryptedPrivateKeyInfoStruct = 0;
|
|
CRYPT_ENCRYPTED_PRIVATE_KEY_INFO *pEncryptedPrivateKeyInfoStruct = NULL;
|
|
BYTE rgbDerivedKeyMatl[40]; // 320 bits is enough for 128 bit key, 64 bit IV
|
|
DWORD cbEncodedPrivateKeyInfoStruct = 0;
|
|
BYTE *pbEncodedPrivateKeyInfoStruct = NULL;
|
|
PBEParameter *pPBEParameter = NULL;
|
|
ASN1decoding_t pDec = GetDecoder();
|
|
|
|
|
|
// there should only be one baggage item
|
|
if (baggage.count != 1)
|
|
goto SetPFXDecodeError;
|
|
|
|
// there should only be one private key
|
|
if (baggage.value->espvks.count != 1)
|
|
goto SetPFXDecodeError;
|
|
|
|
// decode the PKCS8, which is actually stored in the espvkCipherText field
|
|
// of the ESPVK structure. it's a Netscape thing man!!!!
|
|
if (!CryptDecodeObject(X509_ASN_ENCODING,
|
|
PKCS_ENCRYPTED_PRIVATE_KEY_INFO,
|
|
(BYTE *) baggage.value->espvks.value->espvkCipherText.value,
|
|
baggage.value->espvks.value->espvkCipherText.length,
|
|
CRYPT_DECODE_NOCOPY_FLAG,
|
|
NULL,
|
|
&cbEncryptedPrivateKeyInfoStruct))
|
|
goto SetPFXDecodeError;
|
|
|
|
if (NULL == (pEncryptedPrivateKeyInfoStruct = (CRYPT_ENCRYPTED_PRIVATE_KEY_INFO *)
|
|
SSAlloc(cbEncryptedPrivateKeyInfoStruct)))
|
|
goto SetPFXDecodeError;
|
|
|
|
if (!CryptDecodeObject(X509_ASN_ENCODING,
|
|
PKCS_ENCRYPTED_PRIVATE_KEY_INFO,
|
|
(BYTE *) baggage.value->espvks.value->espvkCipherText.value,
|
|
baggage.value->espvks.value->espvkCipherText.length,
|
|
CRYPT_DECODE_NOCOPY_FLAG,
|
|
pEncryptedPrivateKeyInfoStruct,
|
|
&cbEncryptedPrivateKeyInfoStruct))
|
|
goto SetPFXDecodeError;
|
|
|
|
// verify that the algorithm is the one we expect
|
|
if (strcmp("1.2.840.113549.1.12.5.1.3", pEncryptedPrivateKeyInfoStruct->EncryptionAlgorithm.pszObjId) != 0)
|
|
goto SetPFXDecodeError;
|
|
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&pPBEParameter,
|
|
PBEParameter_PDU,
|
|
pEncryptedPrivateKeyInfoStruct->EncryptionAlgorithm.Parameters.pbData,
|
|
pEncryptedPrivateKeyInfoStruct->EncryptionAlgorithm.Parameters.cbData))
|
|
goto SetPFXDecodeError;
|
|
|
|
// derive the key to be used for decrypting,
|
|
if (!NCSPDeriveBaggageDecryptionKey(
|
|
szPassword,
|
|
pPBEParameter->iterationCount,
|
|
pPBEParameter->salt.value, // pkcs5 salt
|
|
pPBEParameter->salt.length,
|
|
rgbDerivedKeyMatl,
|
|
40)) { // 192 bits for triple des - 3key, and 64 bit IV ---- for some reason netscape asks for
|
|
// 40 bytes of key material, then uses the first 192 bits for key and last 64 bits for IV,
|
|
// skipping 64 bits in between. who knows why they do these things!!
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// decrypt the private key
|
|
{
|
|
DWORD dwDataPos;
|
|
DWORD cbToBeDec = pEncryptedPrivateKeyInfoStruct->EncryptedPrivateKey.cbData;
|
|
DES3TABLE des3Table;
|
|
BYTE des3Fdbk [DES_BLOCKLEN];
|
|
|
|
|
|
// key setup
|
|
tripledes3key(&des3Table, rgbDerivedKeyMatl);
|
|
CopyMemory(des3Fdbk, &rgbDerivedKeyMatl[40 - sizeof(des3Fdbk)], sizeof(des3Fdbk)); // fdbk is last chunk
|
|
|
|
cbEncodedPrivateKeyInfoStruct =
|
|
((pEncryptedPrivateKeyInfoStruct->EncryptedPrivateKey.cbData + 7) / 8) * 8;
|
|
|
|
if (NULL == (pbEncodedPrivateKeyInfoStruct = (BYTE *) SSAlloc(cbEncodedPrivateKeyInfoStruct)))
|
|
goto ErrorReturn;
|
|
|
|
for (dwDataPos=0; cbToBeDec > 0; dwDataPos += DES_BLOCKLEN, cbToBeDec -= DES_BLOCKLEN)
|
|
{
|
|
BYTE rgbDec[DES_BLOCKLEN];
|
|
|
|
CBC(
|
|
tripledes,
|
|
DES_BLOCKLEN,
|
|
rgbDec,
|
|
&(pEncryptedPrivateKeyInfoStruct->EncryptedPrivateKey.pbData[dwDataPos]),
|
|
(void *) &des3Table,
|
|
DECRYPT,
|
|
des3Fdbk);
|
|
|
|
CopyMemory(&pbEncodedPrivateKeyInfoStruct[dwDataPos], rgbDec, DES_BLOCKLEN);
|
|
}
|
|
}
|
|
|
|
// set up the SAFE_BAG to be returned
|
|
if (!SetupKeyBag(
|
|
ppKeyBag,
|
|
dwLocalKeyID,
|
|
(BYTE *) baggage.value->espvks.value->espvkData.nickname.value,
|
|
baggage.value->espvks.value->espvkData.nickname.length,
|
|
pbEncodedPrivateKeyInfoStruct,
|
|
cbEncodedPrivateKeyInfoStruct)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// copy the cert thumbprint
|
|
assert(baggage.value->espvks.value->espvkData.assocCerts.count == 1);
|
|
if (NULL == (*ppbCertThumbprint = (BYTE *)
|
|
SSAlloc(baggage.value->espvks.value->espvkData.assocCerts.value->digest.length)))
|
|
goto ErrorReturn;
|
|
|
|
memcpy(
|
|
*ppbCertThumbprint,
|
|
baggage.value->espvks.value->espvkData.assocCerts.value->digest.value,
|
|
baggage.value->espvks.value->espvkData.assocCerts.value->digest.length);
|
|
|
|
goto Ret;
|
|
|
|
SetPFXDecodeError:
|
|
SetLastError(CRYPT_E_BAD_ENCODE);
|
|
ErrorReturn:
|
|
fRet = FALSE;
|
|
|
|
Ret:
|
|
// save last error from TLS madness
|
|
dwErr = GetLastError();
|
|
|
|
if (pEncryptedPrivateKeyInfoStruct)
|
|
SSFree(pEncryptedPrivateKeyInfoStruct);
|
|
if (pbEncodedPrivateKeyInfoStruct)
|
|
SSFree(pbEncodedPrivateKeyInfoStruct);
|
|
|
|
PkiAsn1FreeDecoded(pDec, pPBEParameter, PBEParameter_PDU);
|
|
|
|
// save last error from TLS madness
|
|
SetLastError(dwErr);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
// this function will take a SafeContents structure and format it as an array
|
|
// array of SAFE_BAGs with all the date for the SAGE_BAGs containted in a single
|
|
// buffer. it also adds the local key ID attribute to the cert which has
|
|
// the same thumbprint as the thumbprint passed in
|
|
static
|
|
BOOL
|
|
SetupCertBags(
|
|
SafeContents *pSafeCnts,
|
|
SAFE_BAG **ppCertBags,
|
|
DWORD *pcNumCertBags,
|
|
DWORD dwLocalKeyID,
|
|
BYTE *pbCertThumbprint
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
DWORD dwErr;
|
|
|
|
SAFE_BAG *pSafeBags = NULL;
|
|
DWORD cNumSafeBags = 0;
|
|
|
|
DWORD cbBytesNeeded = 0;
|
|
BYTE *pbCurrentBufferLocation = NULL;
|
|
|
|
X509Bag *pX509Bag = NULL;
|
|
CertCRLBag *pCertCRLBag = NULL;
|
|
|
|
HCERTSTORE hCertStore = NULL;
|
|
CRYPT_DATA_BLOB cryptDataBlob;
|
|
PCCERT_CONTEXT pCertContext = NULL;
|
|
DWORD dwSafeBagIndex = 0;
|
|
|
|
DWORD dwKeyID = 0;
|
|
CRYPT_ATTR_BLOB keyID;
|
|
DWORD cbEncodedLocalKeyID = 0;
|
|
BYTE *pbEncodedLocalKeyID = NULL;
|
|
ASN1decoding_t pDec = GetDecoder();
|
|
|
|
keyID.pbData = (BYTE *) &dwKeyID;
|
|
keyID.cbData = sizeof(DWORD);
|
|
dwKeyID = dwLocalKeyID;
|
|
|
|
// decode the safe bag content, should be a CertCrlBag
|
|
assert(pSafeCnts->count == 1);
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&pCertCRLBag,
|
|
CertCRLBag_PDU,
|
|
(BYTE *) pSafeCnts->value->safeBagContent.value,
|
|
pSafeCnts->value->safeBagContent.length))
|
|
goto SetPFXDecodeError;
|
|
|
|
// decode the X509bag
|
|
assert(pCertCRLBag->count == 1);
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&pX509Bag,
|
|
X509Bag_PDU,
|
|
(BYTE *) pCertCRLBag->value[0].value.value,
|
|
pCertCRLBag->value[0].value.length))
|
|
goto SetPFXDecodeError;
|
|
|
|
// encode the keyID so it is ready to be added to a SAFE_BAGs attributes
|
|
if (!CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
&keyID,
|
|
NULL,
|
|
&cbEncodedLocalKeyID)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (NULL == (pbEncodedLocalKeyID = (BYTE *) SSAlloc(cbEncodedLocalKeyID)))
|
|
goto ErrorReturn;
|
|
|
|
|
|
if (!CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
&keyID,
|
|
pbEncodedLocalKeyID,
|
|
&cbEncodedLocalKeyID)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// open a cert store with the SignedData buffer we got from the SafeContents passed in,
|
|
// this will allow access to all the certs as X509 encoded blobs, and it
|
|
// will give access to the thumbprints so a cert can be matched with the
|
|
// private key
|
|
cryptDataBlob.pbData = (BYTE *) pX509Bag->certOrCRL.content.value;
|
|
cryptDataBlob.cbData = pX509Bag->certOrCRL.content.length;
|
|
hCertStore = CertOpenStore(
|
|
CERT_STORE_PROV_PKCS7,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
NULL,
|
|
0,
|
|
&cryptDataBlob);
|
|
|
|
if (NULL == hCertStore) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// calculate how much space is needed to fit the array of SAFE_BAGs and
|
|
// all their data into one contiguous buffer
|
|
while (NULL != (pCertContext = CertEnumCertificatesInStore(
|
|
hCertStore,
|
|
pCertContext))) {
|
|
DWORD cbEncodedCertBag = 0;
|
|
|
|
cNumSafeBags++;
|
|
cbBytesNeeded += sizeof(SAFE_BAG);
|
|
|
|
// get the size for wrapping an encoded cert into an encoded cert bag
|
|
if (!MakeEncodedCertBag(
|
|
pCertContext->pbCertEncoded,
|
|
pCertContext->cbCertEncoded,
|
|
NULL,
|
|
&cbEncodedCertBag)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
cbBytesNeeded += cbEncodedCertBag;
|
|
cbBytesNeeded += strlen(szOID_PKCS_12_CERT_BAG) + 1;
|
|
}
|
|
|
|
// add bytes to cbBytesNeeded so there is enough space to add the
|
|
// LocalKeyID attribute to ONE of the certificates
|
|
cbBytesNeeded += sizeof(CRYPT_ATTRIBUTE);
|
|
cbBytesNeeded += sizeof(CRYPT_ATTR_BLOB);
|
|
cbBytesNeeded += strlen(szOID_PKCS_12_LOCAL_KEY_ID) + 1;
|
|
cbBytesNeeded += cbEncodedLocalKeyID;
|
|
|
|
// allocate the one big buffer
|
|
if (NULL == (pSafeBags = (SAFE_BAG *) SSAlloc(cbBytesNeeded)))
|
|
goto ErrorReturn;
|
|
|
|
memset(pSafeBags, 0, cbBytesNeeded);
|
|
|
|
// set the current buffer location to the end of the SAFE_BAG array which
|
|
// is at the head of the buffer
|
|
pbCurrentBufferLocation = ((BYTE *) pSafeBags) + (sizeof(SAFE_BAG) * cNumSafeBags);
|
|
|
|
// get the X509 blob for each cert and fill in the array of SAFE_BAGs
|
|
pCertContext = NULL;
|
|
dwSafeBagIndex = 0;
|
|
while (NULL != (pCertContext = CertEnumCertificatesInStore(
|
|
hCertStore,
|
|
pCertContext))) {
|
|
|
|
BYTE *pbLocalThumbprint = NULL;
|
|
DWORD cbLocalThumbprint = 0;
|
|
BYTE *pbEncodedCertBag = NULL;
|
|
DWORD cbEncodedCertBag = 0;
|
|
|
|
// copy the bag type OID
|
|
pSafeBags[dwSafeBagIndex].pszBagTypeOID = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR)pbCurrentBufferLocation, szOID_PKCS_12_CERT_BAG);
|
|
pbCurrentBufferLocation += strlen(szOID_PKCS_12_CERT_BAG) + 1;
|
|
|
|
// wrap the encoded cert into an encoded certbag
|
|
// get the size for wrapping an encoded cert into an encoded cert bag
|
|
if (!MakeEncodedCertBag(
|
|
pCertContext->pbCertEncoded,
|
|
pCertContext->cbCertEncoded,
|
|
NULL,
|
|
&cbEncodedCertBag)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (NULL == (pbEncodedCertBag = (BYTE *) SSAlloc(cbEncodedCertBag)))
|
|
goto ErrorReturn;
|
|
|
|
|
|
if (!MakeEncodedCertBag(
|
|
pCertContext->pbCertEncoded,
|
|
pCertContext->cbCertEncoded,
|
|
pbEncodedCertBag,
|
|
&cbEncodedCertBag)) {
|
|
SSFree(pbEncodedCertBag);
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// copy the encoded certbag
|
|
pSafeBags[dwSafeBagIndex].BagContents.cbData = cbEncodedCertBag;
|
|
pSafeBags[dwSafeBagIndex].BagContents.pbData = pbCurrentBufferLocation;
|
|
memcpy(pbCurrentBufferLocation, pbEncodedCertBag, cbEncodedCertBag);
|
|
pbCurrentBufferLocation += cbEncodedCertBag;
|
|
|
|
// we don't need the encoded cert bag anymore
|
|
SSFree(pbEncodedCertBag);
|
|
|
|
// check to see if this cert is the cert that matches the private key by
|
|
// comparing the thumbprints
|
|
|
|
// Get the thumbprint
|
|
if (!CertGetCertificateContextProperty(
|
|
pCertContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
NULL,
|
|
&cbLocalThumbprint)) {
|
|
CertFreeCertificateContext(pCertContext);
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (NULL == (pbLocalThumbprint = (BYTE *) SSAlloc(cbLocalThumbprint))) {
|
|
CertFreeCertificateContext(pCertContext);
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (!CertGetCertificateContextProperty(
|
|
pCertContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
pbLocalThumbprint,
|
|
&cbLocalThumbprint)) {
|
|
CertFreeCertificateContext(pCertContext);
|
|
SSFree(pbLocalThumbprint);
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// compare thumbprints
|
|
if (memcmp(pbCertThumbprint, pbLocalThumbprint, cbLocalThumbprint) == 0) {
|
|
|
|
// the thumbprints match so add a single attribute with a single value which
|
|
pSafeBags[dwSafeBagIndex].Attributes.cAttr = 1;
|
|
pSafeBags[dwSafeBagIndex].Attributes.rgAttr = (CRYPT_ATTRIBUTE *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTRIBUTE);
|
|
pSafeBags[dwSafeBagIndex].Attributes.rgAttr[0].pszObjId = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR) pbCurrentBufferLocation, szOID_PKCS_12_LOCAL_KEY_ID);
|
|
pbCurrentBufferLocation += strlen(szOID_PKCS_12_LOCAL_KEY_ID) + 1;
|
|
pSafeBags[dwSafeBagIndex].Attributes.rgAttr[0].cValue = 1;
|
|
pSafeBags[dwSafeBagIndex].Attributes.rgAttr[0].rgValue = (CRYPT_ATTR_BLOB *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTR_BLOB);
|
|
pSafeBags[dwSafeBagIndex].Attributes.rgAttr[0].rgValue[0].cbData = cbEncodedLocalKeyID;
|
|
pSafeBags[dwSafeBagIndex].Attributes.rgAttr[0].rgValue[0].pbData = pbCurrentBufferLocation;
|
|
memcpy(pbCurrentBufferLocation, pbEncodedLocalKeyID, cbEncodedLocalKeyID);
|
|
pbCurrentBufferLocation += cbEncodedLocalKeyID;
|
|
}
|
|
else {
|
|
|
|
// otherwise the certificate bag has no attributes in it
|
|
pSafeBags[dwSafeBagIndex].Attributes.cAttr = 0;
|
|
pSafeBags[dwSafeBagIndex].Attributes.rgAttr = NULL;
|
|
}
|
|
|
|
SSFree(pbLocalThumbprint);
|
|
dwSafeBagIndex++;
|
|
}
|
|
|
|
// return the safe bag array and the number of safe bags in the array
|
|
*ppCertBags = pSafeBags;
|
|
*pcNumCertBags = cNumSafeBags;
|
|
|
|
goto Ret;
|
|
|
|
SetPFXDecodeError:
|
|
SetLastError(CRYPT_E_BAD_ENCODE);
|
|
ErrorReturn:
|
|
fRet = FALSE;
|
|
|
|
if (pSafeBags)
|
|
SSFree(pSafeBags);
|
|
*ppCertBags = NULL;
|
|
*pcNumCertBags = 0;
|
|
|
|
Ret:
|
|
// save last error from TLS madness
|
|
dwErr = GetLastError();
|
|
|
|
PkiAsn1FreeDecoded(pDec, pCertCRLBag, CertCRLBag_PDU);
|
|
PkiAsn1FreeDecoded(pDec, pX509Bag, X509Bag_PDU);
|
|
|
|
if (pbEncodedLocalKeyID)
|
|
SSFree(pbEncodedLocalKeyID);
|
|
|
|
if (hCertStore)
|
|
CertCloseStore(hCertStore, 0);
|
|
|
|
// save last error from TLS madness
|
|
SetLastError(dwErr);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
// this function will calculate the number of bytes needed for an
|
|
// attribute
|
|
static
|
|
DWORD
|
|
CalculateSizeOfAttributes(
|
|
CRYPT_ATTRIBUTES *pAttributes
|
|
)
|
|
{
|
|
DWORD cbBytesNeeded = 0;
|
|
DWORD i,j;
|
|
|
|
for (i=0; i<pAttributes->cAttr; i++) {
|
|
cbBytesNeeded += sizeof(CRYPT_ATTRIBUTE);
|
|
cbBytesNeeded += strlen(pAttributes->rgAttr[i].pszObjId) + 1;
|
|
for (j=0; j<pAttributes->rgAttr[i].cValue; j++) {
|
|
cbBytesNeeded += sizeof(CRYPT_ATTR_BLOB);
|
|
cbBytesNeeded += pAttributes->rgAttr[i].rgValue[j].cbData;
|
|
}
|
|
}
|
|
|
|
return cbBytesNeeded;
|
|
}
|
|
|
|
|
|
// this function will take two SAFE_BAG arrays and concatenate them into
|
|
// a SAFE_CONTENT structure. also, the SAFE_CONTENT structure and all
|
|
// it's supporting data will be in a single contiguous buffer
|
|
static
|
|
BOOL
|
|
ConcatenateSafeBagsIntoSafeContents(
|
|
SAFE_BAG *pSafeBagArray1,
|
|
DWORD cSafeBagArray1,
|
|
SAFE_BAG *pSafeBagArray2,
|
|
DWORD cSafeBagArray2,
|
|
SAFE_CONTENTS **ppSafeContents
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
DWORD cbBytesNeeded = 0;
|
|
DWORD i,j;
|
|
SAFE_CONTENTS *pSafeContents = NULL;
|
|
DWORD dwSafeBagIndex = 0;
|
|
BYTE *pbCurrentBufferLocation = NULL;
|
|
|
|
cbBytesNeeded += sizeof(SAFE_CONTENTS);
|
|
cbBytesNeeded += sizeof(SAFE_BAG) * (cSafeBagArray1 + cSafeBagArray2);
|
|
|
|
for (i=0; i<cSafeBagArray1; i++) {
|
|
cbBytesNeeded += strlen(pSafeBagArray1[i].pszBagTypeOID) + 1;
|
|
cbBytesNeeded += pSafeBagArray1[i].BagContents.cbData;
|
|
cbBytesNeeded += CalculateSizeOfAttributes(&pSafeBagArray1[i].Attributes);
|
|
}
|
|
|
|
for (i=0; i<cSafeBagArray2; i++) {
|
|
cbBytesNeeded += strlen(pSafeBagArray2[i].pszBagTypeOID) + 1;
|
|
cbBytesNeeded += pSafeBagArray2[i].BagContents.cbData;
|
|
cbBytesNeeded += CalculateSizeOfAttributes(&pSafeBagArray2[i].Attributes);
|
|
}
|
|
|
|
if (NULL == (pSafeContents = (SAFE_CONTENTS *) SSAlloc(cbBytesNeeded)))
|
|
goto ErrorReturn;
|
|
|
|
memset(pSafeContents, 0, cbBytesNeeded);
|
|
|
|
pbCurrentBufferLocation = ((BYTE *) pSafeContents) + sizeof(SAFE_CONTENTS);
|
|
pSafeContents->cSafeBags = cSafeBagArray1 + cSafeBagArray2;
|
|
pSafeContents->pSafeBags = (SAFE_BAG *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(SAFE_BAG) * (cSafeBagArray1 + cSafeBagArray2);
|
|
|
|
for (i=0; i<cSafeBagArray1; i++) {
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].pszBagTypeOID = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR) pbCurrentBufferLocation, pSafeBagArray1[i].pszBagTypeOID);
|
|
pbCurrentBufferLocation += strlen(pSafeBagArray1[i].pszBagTypeOID) + 1;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].BagContents.cbData = pSafeBagArray1[i].BagContents.cbData;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].BagContents.pbData = pbCurrentBufferLocation;
|
|
memcpy(pbCurrentBufferLocation, pSafeBagArray1[i].BagContents.pbData, pSafeBagArray1[i].BagContents.cbData);
|
|
pbCurrentBufferLocation += pSafeBagArray1[i].BagContents.cbData;
|
|
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.cAttr = pSafeBagArray1[i].Attributes.cAttr;
|
|
if (pSafeBagArray1[i].Attributes.cAttr != 0) {
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr = (CRYPT_ATTRIBUTE *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTRIBUTE) * pSafeBagArray1[i].Attributes.cAttr;
|
|
}
|
|
else {
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr = NULL;
|
|
}
|
|
|
|
for (j=0; j<pSafeBagArray1[i].Attributes.cAttr; j++) {
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].pszObjId = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR) pbCurrentBufferLocation, pSafeBagArray1[i].Attributes.rgAttr[j].pszObjId);
|
|
pbCurrentBufferLocation += strlen(pSafeBagArray1[i].Attributes.rgAttr[j].pszObjId) + 1;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].cValue = 1;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].rgValue = (CRYPT_ATTR_BLOB *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTR_BLOB);
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].rgValue[0].cbData =
|
|
pSafeBagArray1[i].Attributes.rgAttr[j].rgValue[0].cbData;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].rgValue[0].pbData = pbCurrentBufferLocation;
|
|
memcpy(
|
|
pbCurrentBufferLocation,
|
|
pSafeBagArray1[i].Attributes.rgAttr[j].rgValue[0].pbData,
|
|
pSafeBagArray1[i].Attributes.rgAttr[j].rgValue[0].cbData);
|
|
pbCurrentBufferLocation += pSafeBagArray1[i].Attributes.rgAttr[j].rgValue[0].cbData;
|
|
}
|
|
|
|
dwSafeBagIndex++;
|
|
}
|
|
|
|
for (i=0; i<cSafeBagArray2; i++) {
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].pszBagTypeOID = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR) pbCurrentBufferLocation, pSafeBagArray2[i].pszBagTypeOID);
|
|
pbCurrentBufferLocation += strlen(pSafeBagArray2[i].pszBagTypeOID) + 1;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].BagContents.cbData = pSafeBagArray2[i].BagContents.cbData;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].BagContents.pbData = pbCurrentBufferLocation;
|
|
memcpy(pbCurrentBufferLocation, pSafeBagArray2[i].BagContents.pbData, pSafeBagArray2[i].BagContents.cbData);
|
|
pbCurrentBufferLocation += pSafeBagArray2[i].BagContents.cbData;
|
|
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.cAttr = pSafeBagArray2[i].Attributes.cAttr;
|
|
if (pSafeBagArray2[i].Attributes.cAttr != 0) {
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr = (CRYPT_ATTRIBUTE *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTRIBUTE) * pSafeBagArray2[i].Attributes.cAttr;
|
|
}
|
|
else {
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr = NULL;
|
|
}
|
|
|
|
for (j=0; j<pSafeBagArray2[i].Attributes.cAttr; j++) {
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].pszObjId = (LPSTR) pbCurrentBufferLocation;
|
|
strcpy((LPSTR) pbCurrentBufferLocation, pSafeBagArray2[i].Attributes.rgAttr[j].pszObjId);
|
|
pbCurrentBufferLocation += strlen(pSafeBagArray2[i].Attributes.rgAttr[j].pszObjId) + 1;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].cValue = 1;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].rgValue = (CRYPT_ATTR_BLOB *) pbCurrentBufferLocation;
|
|
pbCurrentBufferLocation += sizeof(CRYPT_ATTR_BLOB);
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].rgValue[0].cbData =
|
|
pSafeBagArray2[i].Attributes.rgAttr[j].rgValue[0].cbData;
|
|
pSafeContents->pSafeBags[dwSafeBagIndex].Attributes.rgAttr[j].rgValue[0].pbData = pbCurrentBufferLocation;
|
|
memcpy(
|
|
pbCurrentBufferLocation,
|
|
pSafeBagArray2[i].Attributes.rgAttr[j].rgValue[0].pbData,
|
|
pSafeBagArray2[i].Attributes.rgAttr[j].rgValue[0].cbData);
|
|
pbCurrentBufferLocation += pSafeBagArray2[i].Attributes.rgAttr[j].rgValue[0].cbData;
|
|
}
|
|
|
|
dwSafeBagIndex++;
|
|
}
|
|
|
|
*ppSafeContents = pSafeContents;
|
|
goto Ret;
|
|
|
|
ErrorReturn:
|
|
fRet = FALSE;
|
|
Ret:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PFXAPI
|
|
NSCPImportBlob
|
|
(
|
|
LPCWSTR szPassword,
|
|
PBYTE pbIn,
|
|
DWORD cbIn,
|
|
SAFE_CONTENTS **ppSafeContents
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
DWORD dwErr;
|
|
|
|
int iEncrType;
|
|
OID oid = NULL;
|
|
|
|
PFX *psPfx = NULL;
|
|
EncryptedData *pEncrData = NULL;
|
|
RSAData *pRSAData = NULL;
|
|
PBEParameter *pPBEParameter = NULL;
|
|
SafeContents *pSafeCnts = NULL;
|
|
AuthenticatedSafe *pAuthSafe = NULL;
|
|
SAFE_BAG *pKeyBag = NULL;
|
|
SAFE_BAG *pCertBag = NULL;
|
|
BYTE *pCertThumbprint = NULL;
|
|
DWORD cNumCertBags = 0;
|
|
ASN1decoding_t pDec = GetDecoder();
|
|
|
|
// Crack the PFX blob
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&psPfx,
|
|
PFX_PDU,
|
|
pbIn,
|
|
cbIn))
|
|
goto SetPFXDecodeError;
|
|
|
|
// info blurted into psPfx(PFX) - ensure content present
|
|
if (0 == (psPfx->authSafe.bit_mask & content_present))
|
|
goto SetPFXDecodeError;
|
|
|
|
|
|
// UNDONE: tear apart MACData
|
|
|
|
|
|
// Check authsafe(ContentInfo)
|
|
|
|
// could be data/signeddata
|
|
// UNDONE: only support szOID_RSA_data
|
|
if (!INSCP_Asn1FromObjectID( &psPfx->authSafe.contentType, &oid))
|
|
goto ErrorReturn;
|
|
if (0 != strcmp( oid, szOID_RSA_data))
|
|
goto SetPFXDecodeError;
|
|
SSFree(oid);
|
|
oid = NULL;
|
|
|
|
// content is data: decode
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&pRSAData,
|
|
RSAData_PDU,
|
|
(BYTE *) psPfx->authSafe.content.value,
|
|
psPfx->authSafe.content.length))
|
|
goto SetPFXDecodeError;
|
|
|
|
// now we have octet string: this is an encoded authSafe
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&pAuthSafe,
|
|
AuthenticatedSafe_PDU,
|
|
pRSAData->value,
|
|
pRSAData->length))
|
|
goto SetPFXDecodeError;
|
|
|
|
// check version of the safe
|
|
if (pAuthSafe->bit_mask & version_present)
|
|
#ifdef OSS_CRYPT_ASN1
|
|
if (pAuthSafe->version != v1)
|
|
#else
|
|
if (pAuthSafe->version != Version_v1)
|
|
#endif // OSS_CRYPT_ASN1
|
|
goto SetPFXDecodeError;
|
|
|
|
// require (officially optional) pieces
|
|
|
|
// NSCP: transport mode is used but count is encoded incorrectly
|
|
// if (0 == (pAuthSafe->bit_mask & transportMode_present))
|
|
// goto PFXDecodeError;
|
|
|
|
if (0 == (pAuthSafe->bit_mask & privacySalt_present))
|
|
goto SetPFXDecodeError;
|
|
if (0 == (pAuthSafe->bit_mask & baggage_present))
|
|
goto SetPFXDecodeError;
|
|
|
|
// could be encryptedData/envelopedData
|
|
// UNDONE: only support szOID_RSA_encryptedData
|
|
if (!INSCP_Asn1FromObjectID( &pAuthSafe->safe.contentType, &oid))
|
|
goto ErrorReturn;
|
|
if (0 != strcmp( oid, szOID_RSA_encryptedData))
|
|
goto SetPFXDecodeError;
|
|
SSFree(oid);
|
|
oid = NULL;
|
|
|
|
|
|
//
|
|
// we have pAuthSafe->safe data as RSA_encryptedData
|
|
// we have pAuthSafe->privacySalt to help us decrypt it
|
|
|
|
// we have pAuthSafe->baggage
|
|
|
|
// decode content to encryptedData
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&pEncrData,
|
|
EncryptedData_PDU,
|
|
(BYTE *) pAuthSafe->safe.content.value,
|
|
pAuthSafe->safe.content.length))
|
|
goto SetPFXDecodeError;
|
|
|
|
|
|
// chk version
|
|
if (pEncrData->version != 0)
|
|
goto SetPFXDecodeError;
|
|
|
|
// chk content present, type
|
|
if (0 == (pEncrData->encryptedContentInfo.bit_mask & encryptedContent_present))
|
|
goto SetPFXDecodeError;
|
|
if (!INSCP_Asn1FromObjectID(&pEncrData->encryptedContentInfo.contentType, &oid))
|
|
goto ErrorReturn;
|
|
if (0 != strcmp( oid, szOID_RSA_data))
|
|
goto SetPFXDecodeError;
|
|
SSFree(oid);
|
|
oid = NULL;
|
|
|
|
|
|
// chk encr alg present, type
|
|
if (0 == (pEncrData->encryptedContentInfo.contentEncryptionAlg.bit_mask & parameters_present))
|
|
goto SetPFXDecodeError;
|
|
if (!INSCP_Asn1FromObjectID(&pEncrData->encryptedContentInfo.contentEncryptionAlg.algorithm, &oid))
|
|
goto ErrorReturn;
|
|
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&pPBEParameter,
|
|
PBEParameter_PDU,
|
|
(BYTE *) pEncrData->encryptedContentInfo.contentEncryptionAlg.parameters.value,
|
|
pEncrData->encryptedContentInfo.contentEncryptionAlg.parameters.length))
|
|
goto SetPFXDecodeError;
|
|
|
|
|
|
|
|
if (0 == strcmp( oid, OLD_szOID_PKCS_12_pbeWithSHA1And40BitRC2))
|
|
{
|
|
iEncrType = RC2_40;
|
|
}
|
|
else if (0 == strcmp( oid, OLD_szOID_PKCS_12_pbeWithSHA1And40BitRC4))
|
|
{
|
|
iEncrType = RC4_40;
|
|
}
|
|
else if (0 == strcmp( oid, OLD_szOID_PKCS_12_pbeWithSHA1And128BitRC2))
|
|
{
|
|
iEncrType = RC2_128;
|
|
}
|
|
else if (0 == strcmp( oid, OLD_szOID_PKCS_12_pbeWithSHA1And128BitRC4))
|
|
{
|
|
iEncrType = RC4_128;
|
|
}
|
|
else if (0 == strcmp( oid, OLD_szOID_PKCS_12_pbeWithSHA1AndTripleDES))
|
|
{
|
|
iEncrType = TripleDES;
|
|
}
|
|
else
|
|
goto SetPFXAlgIDError;
|
|
SSFree(oid);
|
|
oid = NULL;
|
|
|
|
|
|
// DECRYPT encryptedData
|
|
if (!NSCPPasswordDecryptData(
|
|
iEncrType,
|
|
|
|
szPassword,
|
|
pAuthSafe->privacySalt.value, // privacy salt
|
|
pAuthSafe->privacySalt.length/8,
|
|
pPBEParameter->iterationCount,
|
|
pPBEParameter->salt.value, // pkcs5 salt
|
|
pPBEParameter->salt.length,
|
|
|
|
&pEncrData->encryptedContentInfo.encryptedContent.value,
|
|
(PDWORD)&pEncrData->encryptedContentInfo.encryptedContent.length))
|
|
goto SetPFXDecryptError;
|
|
|
|
// decode plaintext encryptedData
|
|
if (0 != PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&pSafeCnts,
|
|
SafeContents_PDU,
|
|
pEncrData->encryptedContentInfo.encryptedContent.value,
|
|
pEncrData->encryptedContentInfo.encryptedContent.length))
|
|
goto SetPFXDecodeError;
|
|
|
|
// get private keys out of baggage
|
|
if (!ExtractKeyFromBaggage(
|
|
pAuthSafe->baggage,
|
|
&pKeyBag,
|
|
szPassword,
|
|
1, // this parameter is the Local Key ID to add to the key bags attributes
|
|
&pCertThumbprint)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// set up the cert bag
|
|
if (!SetupCertBags(
|
|
pSafeCnts,
|
|
&pCertBag,
|
|
&cNumCertBags,
|
|
1, // this parameter is the Local Key ID to add to the cert bags attributes
|
|
pCertThumbprint)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
ConcatenateSafeBagsIntoSafeContents(
|
|
pKeyBag,
|
|
1,
|
|
pCertBag,
|
|
cNumCertBags,
|
|
ppSafeContents);
|
|
|
|
goto Ret;
|
|
|
|
|
|
SetPFXAlgIDError:
|
|
SetLastError(NTE_BAD_ALGID);
|
|
goto ErrorReturn;
|
|
|
|
SetPFXDecodeError:
|
|
SetLastError(CRYPT_E_BAD_ENCODE);
|
|
goto ErrorReturn;
|
|
|
|
SetPFXDecryptError:
|
|
SetLastError(NTE_FAIL);
|
|
goto ErrorReturn;
|
|
|
|
ErrorReturn:
|
|
fRet = FALSE;
|
|
Ret:
|
|
|
|
// save last error from TLS madness
|
|
dwErr = GetLastError();
|
|
|
|
PkiAsn1FreeDecoded(pDec, psPfx, PFX_PDU);
|
|
PkiAsn1FreeDecoded(pDec, pRSAData, RSAData_PDU);
|
|
PkiAsn1FreeDecoded(pDec, pAuthSafe, AuthenticatedSafe_PDU);
|
|
PkiAsn1FreeDecoded(pDec, pEncrData, EncryptedData_PDU);
|
|
PkiAsn1FreeDecoded(pDec, pPBEParameter, PBEParameter_PDU);
|
|
PkiAsn1FreeDecoded(pDec, pSafeCnts, SafeContents_PDU);
|
|
|
|
if (oid != NULL)
|
|
SSFree(oid);
|
|
|
|
if (pKeyBag)
|
|
SSFree(pKeyBag);
|
|
|
|
if (pCertBag)
|
|
SSFree(pCertBag);
|
|
|
|
if (pCertThumbprint)
|
|
SSFree(pCertThumbprint);
|
|
|
|
// save last error from TLS madness
|
|
SetLastError(dwErr);
|
|
|
|
return fRet; // return bogus handle
|
|
}
|
|
|
|
|
|
BOOL
|
|
PFXAPI
|
|
IsNetscapePFXBlob(CRYPT_DATA_BLOB* pPFX)
|
|
{
|
|
PFX *psPfx = NULL;
|
|
ASN1decoding_t pDec = GetDecoder();
|
|
|
|
// Crack the PFX blob
|
|
if (0 == PkiAsn1Decode(
|
|
pDec,
|
|
(void **)&psPfx,
|
|
PFX_PDU,
|
|
pPFX->pbData,
|
|
pPFX->cbData))
|
|
{
|
|
PkiAsn1FreeDecoded(pDec, psPfx, PFX_PDU);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
BOOL FNSCPDumpSafeCntsToHPFX(SafeContents* pSafeCnts, HPFX hpfx)
|
|
{
|
|
PPFX_INFO ppfx = (PPFX_INFO)hpfx;
|
|
|
|
// sort and dump bags into correct areas
|
|
ObjectID oKeyBag, oCertBag;
|
|
DWORD dw;
|
|
|
|
ZeroMemory(&oKeyBag, sizeof(ObjectID));
|
|
ZeroMemory(&oCertBag, sizeof(ObjectID));
|
|
|
|
if (!INSCP_Asn1ToObjectID( &szOID_PKCS_12_KeyBagIDs, &oKeyBag))
|
|
return FALSE;
|
|
|
|
if (!INSCP_Asn1ToObjectID( &szOID_PKCS_12_CertCrlBagIDs, &oCertBag))
|
|
return FALSE;
|
|
|
|
for (dw=pSafeCnts->count; dw>0; --dw)
|
|
{
|
|
if (INSCP_EqualObjectIDs(&pSafeCnts->value->safeBagType,
|
|
&oKeyBag) )
|
|
{
|
|
// inc size
|
|
ppfx->cKeys++;
|
|
if (ppfx->rgKeys)
|
|
ppfx->rgKeys = (void**)SSReAlloc(ppfx->rgKeys, ppfx->cKeys * sizeof(SafeBag*));
|
|
else
|
|
ppfx->rgKeys = (void**)SSAlloc(ppfx->cKeys * sizeof(SafeBag*));
|
|
|
|
// assign to keys
|
|
ppfx->rgKeys[ppfx->cKeys-1] = &pSafeCnts->value[dw];
|
|
}
|
|
else if (INSCP_EqualObjectIDs(&pSafeCnts->value->safeBagType,
|
|
&oCertBag) )
|
|
{
|
|
// inc size
|
|
ppfx->cCertcrls++;
|
|
if (ppfx->rgCertcrls)
|
|
ppfx->rgCertcrls = (void**)SSReAlloc(ppfx->rgCertcrls, ppfx->cCertcrls * sizeof(SafeBag*));
|
|
else
|
|
ppfx->rgCertcrls = (void**)SSAlloc(ppfx->cCertcrls * sizeof(SafeBag*));
|
|
|
|
// assign to certs/crls
|
|
ppfx->rgCertcrls[ppfx->cCertcrls-1] = &pSafeCnts->value[dw];
|
|
}
|
|
else
|
|
{
|
|
// inc size
|
|
ppfx->cSecrets++;
|
|
if (ppfx->rgSecrets)
|
|
ppfx->rgSecrets = (void**)SSReAlloc(ppfx->rgSecrets, ppfx->cSecrets * sizeof(SafeBag*));
|
|
else
|
|
ppfx->rgSecrets = (void**)SSAlloc(ppfx->cSecrets * sizeof(SafeBag*));
|
|
|
|
// assign to safebag
|
|
ppfx->rgSecrets[ppfx->cSecrets-1] = &pSafeCnts->value[dw];
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
*/
|