/*
 * Store the results of a successful decode in app-specified 
 * keychain per mImportFlags. Also assign public key hash attributes to any 
 * private keys found.
 */
void P12Coder::storeDecodeResults()
{
	assert(mKeychain != NULL);
	assert(mDlDbHand.DLHandle != 0);
	if(mImportFlags & kSecImportKeys) {
		setPrivateKeyHashes();
	}
	if(mImportFlags & kSecImportCertificates) {
		for(unsigned dex=0; dex<numCerts(); dex++) {
			P12CertBag *certBag = mCerts[dex];
			SecCertificateRef secCert = certBag->getSecCert();
			OSStatus ortn = SecCertificateAddToKeychain(secCert, mKeychain);
			CFRelease(secCert);
			switch(ortn) {
				case errSecSuccess:					// normal
					p12DecodeLog("cert added to keychain");
					break;
				case errSecDuplicateItem:	// dup cert, OK< skip
					p12DecodeLog("skipping dup cert");
					break;
				default:
					p12ErrorLog("SecCertificateAddToKeychain failure\n");
					MacOSError::throwMe(ortn);
			}
		}
	}
	
	if(mImportFlags & kSecImportCRLs) {
		for(unsigned dex=0; dex<numCrls(); dex++) {
			P12CrlBag *crlBag = mCrls[dex];
			CSSM_RETURN crtn = cuAddCrlToDb(mDlDbHand,
				clHand(),
				&crlBag->crlData(),
				NULL);			// no URI known
			switch(crtn) {
				case CSSM_OK:								// normal
					p12DecodeLog("CRL added to keychain");
					break;
				case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:	// dup, ignore
					p12DecodeLog("skipping dup CRL");
					break;
				default:
					p12LogCssmError("Error adding CRL to keychain", crtn);
					CssmError::throwMe(crtn);
			}
		}
	}
	
	/* If all of that succeeded, post notification for imported keys */
	if(mImportFlags & kSecImportKeys) {
		notifyKeyImport();
	}
}
Example #2
0
/*
 * ShroudedKeyBag parser w/decrypt
 */
static int shroudedKeyBagParse(pkcs12_context * context, const NSS_P12_SafeBag *safeBag)
{
	p12DecodeLog("Found shrouded key bag");

	const NSS_P12_ShroudedKeyBag *keyBag = safeBag->bagValue.shroudedKeyBag;
    SecAsn1Item ptext = {0, NULL};
    require_noerr_quiet(p12Decrypt(context, &keyBag->algorithm, 
        &keyBag->encryptedData, &ptext), out);

    /* Decode PKCS#8 formatted private key */
    NSS_PrivateKeyInfo pki;
    memset(&pki, 0, sizeof(pki));
	require_noerr(decode_item(context, &ptext, kSecAsn1PrivateKeyInfoTemplate,
			&pki), out);
    DERItem algorithm = { pki.algorithm.algorithm.Data, pki.algorithm.algorithm.Length };
    require(DEROidCompare(&oidRsa, &algorithm), out);

    CFDataRef keyData = CFDataCreate(kCFAllocatorDefault, pki.privateKey.Data, pki.privateKey.Length);

    require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("key"), keyData), out);
    CFRelease(keyData);
    
    return 0;
out:
    return -1;
}
Example #3
0
/*
 * CertBag parser
 */
static int certBagParse(pkcs12_context * context, const NSS_P12_SafeBag *safeBag)
{
	p12DecodeLog("found certBag");
	NSS_P12_CertBag *certBag = safeBag->bagValue.certBag;

	switch(certBag->type) {
		case CT_X509:
        {
            /* certType = CSSM_CERT_X_509v3;
			   certEncoding = CSSM_CERT_ENCODING_DER; */
            CFDataRef certData = CFDataCreate(kCFAllocatorDefault, certBag->certValue.Data, 
                certBag->certValue.Length);
            require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("cert"), certData), out);
            CFRelease(certData);
            break;
        }
		case CT_SDSI:
            /* certType = CSSM_CERT_SDSIv1; */
			/* it's base64 encoded - no value for that in this enum */
			break;
		default:
            return -1;
	}
    return 0;
out:
    return -1;
}
Example #4
0
/*
 * Parse a ContentInfo in the context of (i.e., as an element of)
 * an AuthenticatedSafe.
 */
static int authSafeElementParse(pkcs12_context * context, const NSS_P7_DecodedContentInfo *info)
{
	p12DecodeLog("authSafeElementParse");
	switch(info->type) {
		case CT_Data:
			/* unencrypted SafeContents */
			require_noerr(safeContentsParse(context, info->content.data), out);
			break;
			
		case CT_EncryptedData:
		{
			/* 
			 * Decrypt contents to get a SafeContents and
			 * then parse that.
			 */
			SecAsn1Item ptext = {0, NULL};
            NSS_P7_EncryptedData *edata = info->content.encryptData;
            require_noerr_quiet(p12Decrypt(context, &edata->contentInfo.encrAlg, 
                &edata->contentInfo.encrContent, &ptext), out);
			require_noerr(safeContentsParse(context, &ptext), out);
			break;
		}	
		default:
            break;
	}
    return 0;
out:
    return -1;
}
/*
 * Parse an CSSM_X509_ALGORITHM_IDENTIFIER specific to P12.
 * Decode the alg params as a NSS_P12_PBE_Params and parse and 
 * return the result if the pbeParams is non-NULL.
 */
void P12Coder::algIdParse(
	const CSSM_X509_ALGORITHM_IDENTIFIER &algId,
	NSS_P12_PBE_Params *pbeParams,		// optional
	SecNssCoder &localCdr)
{
	p12DecodeLog("algIdParse");

	const CSSM_DATA &param = algId.parameters;
	if(pbeParams == NULL) {
		/* alg params are uninterpreted */
		return;
	}
	
	if(param.Length == 0) {
		p12ErrorLog("algIdParse: no alg parameters\n");
		P12_THROW_DECODE;
	}
	
	memset(pbeParams, 0, sizeof(*pbeParams));
	if(localCdr.decodeItem(param, 
			NSS_P12_PBE_ParamsTemplate, pbeParams)) {
		p12ErrorLog("Error decoding NSS_P12_PBE_Params\n");
		P12_THROW_DECODE;
	}
}
/*
 * Given a P12KeyBag, find a matching P12CertBag. Keys and certs
 * "match" if their localKeyIds match. Returns NULL if not found.
 */
P12CertBag *P12Coder::findCertForKey(
	P12KeyBag *keyBag)
{
	assert(keyBag != NULL);
	CSSM_DATA &keyKeyId = keyBag->localKeyIdCssm();
	
	for(unsigned dex=0; dex<numCerts(); dex++) {
		P12CertBag *certBag = mCerts[dex];
		CSSM_DATA &certKeyId = certBag->localKeyIdCssm();
		if(nssCompareCssmData(&keyKeyId, &certKeyId)) {
			p12DecodeLog("findCertForKey SUCCESS");
			return certBag;
		}
	}
	p12DecodeLog("findCertForKey FAILURE");
	return NULL;
}
/*
 * Assign appropriate public key hash attribute to each 
 * private key. 
 */
void P12Coder::setPrivateKeyHashes()
{
	CSSM_KEY_PTR newKey;
	
	for(unsigned dex=0; dex<numKeys(); dex++) {
		P12KeyBag *keyBag = mKeys[dex];
		
		CSSM_DATA newLabel = {0, NULL};
		CFStringRef friendlyName = keyBag->friendlyName();
		newKey = NULL;
		CSSM_RETURN crtn = p12SetPubKeyHash(mCspHand,
			mDlDbHand,
			keyBag->label(),
			p12StringToUtf8(friendlyName, mCoder),
			mCoder,
			newLabel,
			newKey);
		if(friendlyName) {
			CFRelease(friendlyName);
		}
		switch(crtn) {
			case CSSM_OK:
				/* update key's label in case we have to delete on error */
				keyBag->setLabel(newLabel);
				p12DecodeLog("set pub key hash for private key");
				break;
			case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
				/*
				 * Special case: update keyBag's CSSM_KEY and proceed without error
				 */
				p12DecodeLog("ignoring dup private key");
				assert(newKey != NULL);
				keyBag->setKey(newKey);
				keyBag->dupKey(true);
				/* update key's label in case we have to delete on error */
				keyBag->setLabel(newLabel);
				break;
			default:
				p12ErrorLog("p12SetPubKeyHash failure\n");
				CssmError::throwMe(crtn);
		}
	}
}
/*
 * Parse an encoded NSS_P12_SafeContents. This could be either 
 * present as plaintext in an AuthSafe or decrypted. 
 */
void P12Coder::safeContentsParse(
	const CSSM_DATA &contentsBlob,
	SecNssCoder &localCdr)
{
	p12DecodeLog("safeContentsParse");

	NSS_P12_SafeContents sc;
	memset(&sc, 0, sizeof(sc));
	if(localCdr.decodeItem(contentsBlob, NSS_P12_SafeContentsTemplate,
			&sc)) {
		p12ErrorLog("Error decoding SafeContents\n");
		P12_THROW_DECODE;
	}
	unsigned numBags = nssArraySize((const void **)sc.bags);
	for(unsigned dex=0; dex<numBags; dex++) {
		NSS_P12_SafeBag *bag = sc.bags[dex];
		assert(bag != NULL);
		
		/* ensure that *something* is there */
		if(bag->bagValue.keyBag == NULL) {
			p12ErrorLog("safeContentsParse: Empty SafeBag\n");
			P12_THROW_DECODE;
		}
		
		/*
		 * Break out to individual bag type
		 */
		switch(bag->type) {
			case BT_KeyBag:
				keyBagParse(*bag, localCdr);
				break;
			case BT_ShroudedKeyBag:
				shroudedKeyBagParse(*bag, localCdr);
				break;
			case BT_CertBag:
				certBagParse(*bag, localCdr);
				break;
			case BT_CrlBag:
				crlBagParse(*bag, localCdr);
				break;
			case BT_SecretBag:
				secretBagParse(*bag ,localCdr);
				break;
			case BT_SafeContentsBag:
				safeContentsBagParse(*bag, localCdr);
				break;
			default:
				p12ErrorLog("unknown  p12 BagType (%u)\n",
					(unsigned)bag->type);
				P12_THROW_DECODE;
		}
	}
}
/*
 * Decrypt the contents of a NSS_P7_EncryptedData
 */
void P12Coder::encryptedDataDecrypt(
	const NSS_P7_EncryptedData &edata,
	SecNssCoder &localCdr,
	NSS_P12_PBE_Params *pbep,	// preparsed
	CSSM_DATA &ptext)			// result goes here in localCdr space
{
	p12DecodeLog("encryptedDataDecrypt");

	/* see if we can grok the encr alg */
	CSSM_ALGORITHMS		keyAlg;			// e.g., CSSM_ALGID_DES
	CSSM_ALGORITHMS		encrAlg;		// e.g., CSSM_ALGID_3DES_3KEY_EDE
	CSSM_ALGORITHMS		pbeHashAlg;		// SHA1 or MD5
	uint32				keySizeInBits;
	uint32				blockSizeInBytes;	// for IV, optional
	CSSM_PADDING		padding;		// CSSM_PADDING_PKCS7, etc.
	CSSM_ENCRYPT_MODE	mode;			// CSSM_ALGMODE_CBCPadIV8, etc.
	PKCS_Which			pkcs;
	
	bool found = pkcsOidToParams(&edata.contentInfo.encrAlg.algorithm,
		keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
		padding, mode, pkcs);
	if(!found || (pkcs != PW_PKCS12)) {
		p12ErrorLog("EncryptedData encrAlg not understood\n");
		CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
	}
		
	uint32 iterCount;
	if(!p12DataToInt(pbep->iterations, iterCount)) {
		p12ErrorLog("encryptedDataDecrypt: badly formed iterCount\n");
		P12_THROW_DECODE;
	}
	const CSSM_DATA *pwd = getEncrPassPhrase();
	const CSSM_KEY *passKey = getEncrPassKey();
	if((pwd == NULL) && (passKey == NULL)) {
		p12ErrorLog("no passphrase set\n");
		CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE);
	}
	
	/* go */
	CSSM_RETURN crtn = p12Decrypt(mCspHand,
		edata.contentInfo.encrContent,
		keyAlg, encrAlg, pbeHashAlg,
		keySizeInBits, blockSizeInBytes,
		padding, mode,
		iterCount, pbep->salt,
		pwd,
		passKey, 
		localCdr, 
		ptext);
	if(crtn) {
		CssmError::throwMe(crtn);
	}
}
/* top-level PKCS12 PFX decoder */
void P12Coder::decode(
	CFDataRef				cdpfx)
{
	SecNssCoder localCdr;
	NSS_P12_DecodedPFX pfx;

	p12DecodeLog("decode");
	memset(&pfx, 0, sizeof(pfx));
	const CSSM_DATA rawBlob = {int_cast<CFIndex, CSSM_SIZE>(CFDataGetLength(cdpfx)),
		(uint8 *)CFDataGetBytePtr(cdpfx)};
		
	if(localCdr.decodeItem(rawBlob, NSS_P12_DecodedPFXTemplate, &pfx)) {
		p12ErrorLog("Error on top-level decode of NSS_P12_DecodedPFX\n");
		P12_THROW_DECODE;
	}
	NSS_P7_DecodedContentInfo &dci = pfx.authSafe;
	if(dci.type != CT_Data) {
		/* no other types supported yet */
		p12ErrorLog("bad top-level contentType\n");
		P12_THROW_DECODE;
	}
	mIntegrityMode = kSecPkcs12ModePassword;

	if(pfx.macData == NULL) {
		/* not present is an error in kSecPkcs12ModePassword */
		p12ErrorLog("no MAC in PFX\n");
		P12_THROW_DECODE;
	}
	macParse(*pfx.macData, localCdr);

	const CSSM_DATA *macPhrase = getMacPassPhrase();
	const CSSM_KEY *macPassKey = getMacPassKey();
	if((macPhrase == NULL) && (macPassKey == NULL)) {
		p12ErrorLog("no passphrase set\n");
		CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE);
	}
	CSSM_RETURN crtn = p12VerifyMac(pfx, mCspHand, macPhrase, 
		macPassKey, localCdr);
	if(crtn) {
		p12LogCssmError("p12VerifyMac", crtn);
		CssmError::throwMe(errSecPkcs12VerifyFailure);
	}
	
	authSafeParse(*dci.content.data, localCdr);

	/*
	 * On success, if we have a keychain, store certs and CRLs there
	 */
	if(mKeychain != NULL) {
		storeDecodeResults();
	}
}
/*
 * SafeContentsBag parser
 */
void P12Coder::safeContentsBagParse(
	const NSS_P12_SafeBag &safeBag,
	SecNssCoder &localCdr)
{
	p12DecodeLog("found SafeContents safe bag");
	NSS_P12_SafeContentsBag *scBag = safeBag.bagValue.safeContentsBag;
	P12OpaqueBag *p12Bag = new P12OpaqueBag(safeBag.bagId, 
		/* this breaks when NSS_P12_SafeContentsBag is not a CSSM_DATA */
		*scBag,			
		safeBag.bagAttrs,
		mCoder);
	addOpaque(p12Bag);
}
void P12Coder::macParse(
	const NSS_P12_MacData &macData, 
	SecNssCoder &localCdr)
{
	p12DecodeLog("macParse");
	algIdParse(macData.mac.digestAlgorithm, NULL, localCdr);
	const CSSM_DATA &iter = macData.iterations;
	if(iter.Length > 4) {
		p12ErrorLog("malformed iteration length (%u)\n",
				(unsigned)iter.Length);
		P12_THROW_DECODE;
	}
}
Example #13
0
/*
 * Parse an SecAsn1AlgId specific to P12.
 * Decode the alg params as a NSS_P12_PBE_Params and parse and 
 * return the result if the pbeParams is non-NULL.
 */
static int algIdParse(pkcs12_context * context, 
	const SecAsn1AlgId *algId, NSS_P12_PBE_Params *pbeParams/*optional*/)
{
	p12DecodeLog("algIdParse");
	const SecAsn1Item *param = &algId->parameters;
	require(pbeParams, out);
    require(param && param->Length, out);
	memset(pbeParams, 0, sizeof(*pbeParams));
	require_noerr(decode_item(context, param, NSS_P12_PBE_ParamsTemplate, pbeParams), out);
    
    return 0;
out:
    return -1;
}
/*
 * Parse a NSS_P7_EncryptedData - specifically in the context
 * of a P12 in password privacy mode. (The latter assumption is
 * to enable us to infer CSSM_X509_ALGORITHM_IDENTIFIER.parameters
 * format). 
 */
void P12Coder::encryptedDataParse(
	const NSS_P7_EncryptedData &edata,
	SecNssCoder &localCdr,
	NSS_P12_PBE_Params *pbep)		// optional, RETURNED
{
	p12DecodeLog("encryptedDataParse");

	/*
	 * Parse the alg ID, save PBE params for when we do the decrypt
	 * key unwrap
	 */
	const NSS_P7_EncrContentInfo &ci = edata.contentInfo;
	const CSSM_X509_ALGORITHM_IDENTIFIER &algId = ci.encrAlg;
	algIdParse(algId, pbep, localCdr);
}
Example #15
0
/*
 * Parse an encoded NSS_P12_SafeContents. This could be either 
 * present as plaintext in an AuthSafe or decrypted. 
 */
static int safeContentsParse(pkcs12_context * context, const SecAsn1Item *contentsBlob)
{
	p12DecodeLog("safeContentsParse");

	NSS_P12_SafeContents sc;
	memset(&sc, 0, sizeof(sc));
	require_noerr(decode_item(context, contentsBlob, NSS_P12_SafeContentsTemplate,
			&sc), out);

	unsigned numBags = nssArraySize((const void **)sc.bags);
    unsigned int dex;
	for(dex=0; dex<numBags; dex++) {
		NSS_P12_SafeBag *bag = sc.bags[dex];
		assert(bag != NULL);
		
		/* ensure that *something* is there */
		require(bag->bagValue.keyBag != NULL, out);
		
		/*
		 * Break out to individual bag type
		 */
		switch(bag->type) {
			case BT_ShroudedKeyBag:
				require_noerr(shroudedKeyBagParse(context, bag), out);
				break;
			case BT_CertBag:
				require_noerr(certBagParse(context, bag), out);
				break;

			case BT_KeyBag:
				/* keyBagParse(bag); */
                p12DecodeLog("Unhandled BT_KeyBag");
				break;
			case BT_CrlBag:
				/* crlBagParse(bag); */
                p12DecodeLog("Unhandled BT_CrlBag");
				break;
			case BT_SecretBag:
				/* secretBagParse(bag); */
                p12DecodeLog("Unhandled BT_SecretBag");
				break;
			case BT_SafeContentsBag:
				/* safeContentsBagParse(bag); */
                p12DecodeLog("Unhandled BT_SafeContentsBag");
				break;
			default:
                p12DecodeLog("Unknown bag type");
                goto out;
                break;
		}
	}
    return 0;
out:
    return -1;
}
Example #16
0
/*
 * Parse an encoded NSS_P12_AuthenticatedSafe
 */
static int authSafeParse(pkcs12_context * context, const SecAsn1Item *authSafeBlob)
{
    p12DecodeLog("authSafeParse");
    NSS_P12_AuthenticatedSafe authSafe;
    memset(&authSafe, 0, sizeof(authSafe));
    require_noerr(decode_item(context, authSafeBlob, 
        NSS_P12_AuthenticatedSafeTemplate, &authSafe), out);

    unsigned numInfos = nssArraySize((const void **)authSafe.info);
    unsigned int dex;
    for (dex=0; dex<numInfos; dex++) {
        NSS_P7_DecodedContentInfo *info = authSafe.info[dex];
        require_noerr_quiet(authSafeElementParse(context, info), out);
    }
    return 0;
out:
    return -1;
}
/*
 * (unshrouded) KeyBag parser
 */
void P12Coder::keyBagParse(
	const NSS_P12_SafeBag &safeBag,
	SecNssCoder &localCdr)
{
	if(mPrivKeyImportState == PKIS_NoMore) {
		CssmError::throwMe(errSecMultiplePrivKeys);	
	}
	
	/* FIXME - should be able to parse and handle this.... */
	p12DecodeLog("found keyBag");
	NSS_P12_KeyBag *keyBag = safeBag.bagValue.keyBag;
	P12OpaqueBag *p12Bag = new P12OpaqueBag(safeBag.bagId, 
		/* this breaks when NSS_P12_KeyBag is not a CSSM_DATA */
		*keyBag,			
		safeBag.bagAttrs,
		mCoder);
	addOpaque(p12Bag);
}
/*
 * CrlBag parser
 */
void P12Coder::crlBagParse(
	const NSS_P12_SafeBag &safeBag,
	SecNssCoder &localCdr)
{
	p12DecodeLog("found crlBag");
	NSS_P12_CrlBag *crlBag = safeBag.bagValue.crlBag;
	switch(crlBag->type) {
		case CRT_X509:
			break;
		default:
			p12ErrorLog("crlBagParse: unknown CRL type\n");
			P12_THROW_DECODE;
	}
	P12CrlBag *p12Bag = new P12CrlBag(crlBag->type, 
		crlBag->crlValue,
		safeBag.bagAttrs,
		mCoder);
	addCrl(p12Bag);
}
/*
 * CertBag parser
 */
void P12Coder::certBagParse(
	const NSS_P12_SafeBag &safeBag,
	SecNssCoder &localCdr)
{
	p12DecodeLog("found certBag");
	NSS_P12_CertBag *certBag = safeBag.bagValue.certBag;
	switch(certBag->type) {
		case CT_X509:
		case CT_SDSI:
			break;
		default:
			p12ErrorLog("certBagParse: unknown cert type\n");
			P12_THROW_DECODE;
	}
	P12CertBag *p12Bag = new P12CertBag(certBag->type, 
		certBag->certValue,
		safeBag.bagAttrs,
		mCoder);
	addCert(p12Bag);
}
/*
 * Parse an encoded NSS_P12_AuthenticatedSafe
 */
void P12Coder::authSafeParse(
	const CSSM_DATA &authSafeBlob,
	SecNssCoder &localCdr)
{
	p12DecodeLog("authSafeParse");

	NSS_P12_AuthenticatedSafe authSafe;
	
	memset(&authSafe, 0, sizeof(authSafe));
	if(localCdr.decodeItem(authSafeBlob,
			NSS_P12_AuthenticatedSafeTemplate,
			&authSafe)) {
		p12ErrorLog("Error decoding authSafe\n");
		P12_THROW_DECODE;
	}
	unsigned numInfos = nssArraySize((const void **)authSafe.info);
	for(unsigned dex=0; dex<numInfos; dex++) {
		NSS_P7_DecodedContentInfo *info = authSafe.info[dex];
		authSafeElementParse(info, localCdr);
	}
}
/*
 * Parse a ContentInfo in the context of (i.e., as an element of)
 * an AuthenticatedSafe.
 */
void P12Coder::authSafeElementParse(
	const NSS_P7_DecodedContentInfo *info,
	SecNssCoder &localCdr)
{
	p12DecodeLog("authSafeElementParse");
	switch(info->type) {
		case CT_Data:
			/* unencrypted SafeContents */
			safeContentsParse(*info->content.data, localCdr);
			break;
			
		case CT_EncryptedData:
		{
			NSS_P12_PBE_Params pbep;
			encryptedDataParse(*info->content.encryptData, localCdr, &pbep);

			/* 
			 * Decrypt contents to get a SafeContents and
			 * then parse that.
			 */
			CSSM_DATA ptext = {0, NULL};
			encryptedDataDecrypt(*info->content.encryptData,
				localCdr, &pbep, ptext);
			safeContentsParse(ptext, localCdr);
			break;
		}	
		default:
			p12ErrorLog("authSafeElementParse: unknown sage type (%u)\n",
				(unsigned)info->type);
				
			/* well, save it as an opaque bag for now */
			P12OpaqueBag *opaque = new P12OpaqueBag(
				info->contentType, *info->content.data,
				NULL, 	// no attrs
				localCdr);
			addOpaque(opaque);
			break;
	}
}
/*
 * ShroudedKeyBag parser w/decrypt
 */
void P12Coder::shroudedKeyBagParse(
	const NSS_P12_SafeBag &safeBag,
	SecNssCoder &localCdr)
{
	p12DecodeLog("Found shrouded key bag");
	if(mPrivKeyImportState == PKIS_NoMore) {
		CssmError::throwMe(errSecMultiplePrivKeys);	
	}

	const NSS_P12_ShroudedKeyBag *keyBag = safeBag.bagValue.shroudedKeyBag;
	const CSSM_X509_ALGORITHM_IDENTIFIER &algId = keyBag->algorithm;
	NSS_P12_PBE_Params pbep;
	algIdParse(algId, &pbep, localCdr);

	/*
	 * Prepare for decryption
	 */
	CSSM_ALGORITHMS		keyAlg;			// e.g., CSSM_ALGID_DES
	CSSM_ALGORITHMS		encrAlg;		// e.g., CSSM_ALGID_3DES_3KEY_EDE
	CSSM_ALGORITHMS		pbeHashAlg;		// SHA1 or MD5
	uint32				keySizeInBits;
	uint32				blockSizeInBytes;	// for IV, optional
	CSSM_PADDING		padding;		// CSSM_PADDING_PKCS7, etc.
	CSSM_ENCRYPT_MODE	mode;			// CSSM_ALGMODE_CBCPadIV8, etc.
	PKCS_Which			pkcs;
	
	bool found = pkcsOidToParams(&algId.algorithm,
		keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
		padding, mode, pkcs);
	if(!found || (pkcs != PW_PKCS12)) {
		p12ErrorLog("ShroudedKeyBag encrAlg not understood\n");
		CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
	}

	uint32 iterCount;
	if(!p12DataToInt(pbep.iterations, iterCount)) {
		p12ErrorLog("ShroudedKeyBag: badly formed iterCount\n");
		P12_THROW_DECODE;
	}
	const CSSM_DATA *encrPhrase = getEncrPassPhrase();
	const CSSM_KEY *passKey = getEncrPassKey();
	if((encrPhrase == NULL) && (passKey == NULL)) {
		p12ErrorLog("no passphrase set\n");
		CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE);
	}
	
	/* We'll own the actual CSSM_KEY memory */
	CSSM_KEY_PTR privKey = (CSSM_KEY_PTR)mCoder.malloc(sizeof(CSSM_KEY));
	memset(privKey, 0, sizeof(CSSM_KEY));
	
	CSSM_DATA labelData;
	p12GenLabel(labelData, localCdr);	
	
	CSSM_RETURN crtn = p12UnwrapKey(mCspHand,
		mDlDbHand.DLHandle ? &mDlDbHand : NULL,
		mImportFlags & kSecImportKeys,
		keyBag->encryptedData,
		keyAlg, encrAlg, pbeHashAlg,
		keySizeInBits, blockSizeInBytes,
		padding, mode,
		iterCount, pbep.salt,
		encrPhrase,
		passKey,
		localCdr, 
		labelData,
		mAccess,
		mNoAcl,
		mKeyUsage,
		mKeyAttrs,
		privKey);
	if(crtn) {
		p12ErrorLog("Error unwrapping private key\n");
		CssmError::throwMe(crtn);
	}
	p12DecodeLog("unwrapped shrouded key bag");

	P12KeyBag *p12bag = new P12KeyBag(privKey, mCspHand,
		safeBag.bagAttrs, labelData, mCoder);
	addKey(p12bag);
	
	if(mPrivKeyImportState == PKIS_AllowOne) {
		mPrivKeyImportState = PKIS_NoMore;	
	}
}