/* * 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(); } }
/* * 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; }
/* * 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; }
/* * 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 ¶m = 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; } }
/* * 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); }
/* * 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; }
/* * 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; } }