diff options
Diffstat (limited to 'security/nss/lib/pkcs12/p12e.c')
-rw-r--r-- | security/nss/lib/pkcs12/p12e.c | 2254 |
1 files changed, 2254 insertions, 0 deletions
diff --git a/security/nss/lib/pkcs12/p12e.c b/security/nss/lib/pkcs12/p12e.c new file mode 100644 index 000000000..8cc683667 --- /dev/null +++ b/security/nss/lib/pkcs12/p12e.c @@ -0,0 +1,2254 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +#include "p12t.h" +#include "p12.h" +#include "plarena.h" +#include "secitem.h" +#include "secoid.h" +#include "seccomon.h" +#include "secport.h" +#include "cert.h" +#include "secpkcs7.h" +#include "secasn1.h" +#include "secerr.h" +#include "pk11func.h" +#include "p12plcy.h" +#include "p12local.h" +#include "alghmac.h" +#include "prcpucfg.h" + +/********************************* + * Structures used in exporting the PKCS 12 blob + *********************************/ + +/* A SafeInfo is used for each ContentInfo which makes up the + * sequence of safes in the AuthenticatedSafe portion of the + * PFX structure. + */ +struct SEC_PKCS12SafeInfoStr { + PRArenaPool *arena; + + /* information for setting up password encryption */ + SECItem pwitem; + SECOidTag algorithm; + PK11SymKey *encryptionKey; + + /* how many items have been stored in this safe, + * we will skip any safe which does not contain any + * items + */ + unsigned int itemCount; + + /* the content info for the safe */ + SEC_PKCS7ContentInfo *cinfo; + + sec_PKCS12SafeContents *safe; +}; + +/* An opaque structure which contains information needed for exporting + * certificates and keys through PKCS 12. + */ +struct SEC_PKCS12ExportContextStr { + PRArenaPool *arena; + PK11SlotInfo *slot; + void *wincx; + + /* integrity information */ + PRBool integrityEnabled; + PRBool pwdIntegrity; + union { + struct sec_PKCS12PasswordModeInfo pwdInfo; + struct sec_PKCS12PublicKeyModeInfo pubkeyInfo; + } integrityInfo; + + /* helper functions */ + /* retrieve the password call back */ + SECKEYGetPasswordKey pwfn; + void *pwfnarg; + + /* safe contents bags */ + SEC_PKCS12SafeInfo **safeInfos; + unsigned int safeInfoCount; + + /* the sequence of safes */ + sec_PKCS12AuthenticatedSafe authSafe; + + /* information needing deletion */ + CERTCertificate **certList; +}; + +/* structures for passing information to encoder callbacks when processing + * data through the ASN1 engine. + */ +struct sec_pkcs12_encoder_output { + SEC_PKCS12EncoderOutputCallback outputfn; + void *outputarg; +}; + +struct sec_pkcs12_hmac_and_output_info { + void *arg; + struct sec_pkcs12_encoder_output output; +}; + +/* An encoder context which is used for the actual encoding + * portion of PKCS 12. + */ +typedef struct sec_PKCS12EncoderContextStr { + PRArenaPool *arena; + SEC_PKCS12ExportContext *p12exp; + PK11SymKey *encryptionKey; + + /* encoder information - this is set up based on whether + * password based or public key pased privacy is being used + */ + SEC_ASN1EncoderContext *ecx; + union { + struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo; + struct sec_pkcs12_encoder_output encOutput; + } output; + + /* structures for encoding of PFX and MAC */ + sec_PKCS12PFXItem pfx; + sec_PKCS12MacData mac; + + /* authenticated safe encoding tracking information */ + SEC_PKCS7ContentInfo *aSafeCinfo; + SEC_PKCS7EncoderContext *aSafeP7Ecx; + SEC_ASN1EncoderContext *aSafeEcx; + unsigned int currentSafe; + + /* hmac context */ + void *hmacCx; +} sec_PKCS12EncoderContext; + + +/********************************* + * Export setup routines + *********************************/ + +/* SEC_PKCS12CreateExportContext + * Creates an export context and sets the unicode and password retrieval + * callbacks. This is the first call which must be made when exporting + * a PKCS 12 blob. + * + * pwfn, pwfnarg - password retrieval callback and argument. these are + * required for password-authentication mode. + */ +SEC_PKCS12ExportContext * +SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg, + PK11SlotInfo *slot, void *wincx) +{ + PRArenaPool *arena = NULL; + SEC_PKCS12ExportContext *p12ctxt = NULL; + + /* allocate the arena and create the context */ + arena = PORT_NewArena(4096); + if(!arena) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena, + sizeof(SEC_PKCS12ExportContext)); + if(!p12ctxt) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* password callback for key retrieval */ + p12ctxt->pwfn = pwfn; + p12ctxt->pwfnarg = pwfnarg; + + p12ctxt->integrityEnabled = PR_FALSE; + p12ctxt->arena = arena; + p12ctxt->wincx = wincx; + p12ctxt->slot = (slot) ? slot : PK11_GetInternalSlot(); + + return p12ctxt; + +loser: + if(arena) { + PORT_FreeArena(arena, PR_TRUE); + } + + return NULL; +} + +/* + * Adding integrity mode + */ + +/* SEC_PKCS12AddPasswordIntegrity + * Add password integrity to the exported data. If an integrity method + * has already been set, then return an error. + * + * p12ctxt - the export context + * pwitem - the password for integrity mode + * integAlg - the integrity algorithm to use for authentication. + */ +SECStatus +SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag integAlg) +{ + if(!p12ctxt || p12ctxt->integrityEnabled) { + return SECFailure; + } + + /* set up integrity information */ + p12ctxt->pwdIntegrity = PR_TRUE; + p12ctxt->integrityInfo.pwdInfo.password = + (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); + if(!p12ctxt->integrityInfo.pwdInfo.password) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + if(SECITEM_CopyItem(p12ctxt->arena, + p12ctxt->integrityInfo.pwdInfo.password, pwitem) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg; + p12ctxt->integrityEnabled = PR_TRUE; + + return SECSuccess; +} + +/* SEC_PKCS12AddPublicKeyIntegrity + * Add public key integrity to the exported data. If an integrity method + * has already been set, then return an error. The certificate must be + * allowed to be used as a signing cert. + * + * p12ctxt - the export context + * cert - signer certificate + * certDb - the certificate database + * algorithm - signing algorithm + * keySize - size of the signing key (?) + */ +SECStatus +SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt, + CERTCertificate *cert, CERTCertDBHandle *certDb, + SECOidTag algorithm, int keySize) +{ + if(!p12ctxt) { + return SECFailure; + } + + p12ctxt->integrityInfo.pubkeyInfo.cert = cert; + p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb; + p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm; + p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize; + p12ctxt->integrityEnabled = PR_TRUE; + + return SECSuccess; +} + + +/* + * Adding safes - encrypted (password/public key) or unencrypted + * Each of the safe creation routines return an opaque pointer which + * are later passed into the routines for exporting certificates and + * keys. + */ + +/* append the newly created safeInfo to list of safeInfos in the export + * context. + */ +static SECStatus +sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info) +{ + void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL; + + if(!p12ctxt || !info) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* if no safeInfos have been set, create the list, otherwise expand it. */ + if(!p12ctxt->safeInfoCount) { + p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(SEC_PKCS12SafeInfo *)); + dummy1 = p12ctxt->safeInfos; + p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(SECItem *)); + dummy2 = p12ctxt->authSafe.encodedSafes; + } else { + dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos, + (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *), + (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *)); + p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1; + dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes, + (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *), + (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *)); + p12ctxt->authSafe.encodedSafes = (SECItem**)dummy2; + } + if(!dummy1 || !dummy2) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* append the new safeInfo and null terminate the list */ + p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info; + p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL; + p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] = + (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); + if(!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL; + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(p12ctxt->arena, mark); + return SECFailure; +} + +/* SEC_PKCS12CreatePasswordPrivSafe + * Create a password privacy safe to store exported information in. + * + * p12ctxt - export context + * pwitem - password for encryption + * privAlg - pbe algorithm through which encryption is done. + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, + SECItem *pwitem, SECOidTag privAlg) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + PK11SlotInfo *slot; + SECAlgorithmID *algId; + SECItem uniPwitem = {siBuffer, NULL, 0}; + + if(!p12ctxt) { + return NULL; + } + + /* allocate the safe info */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; + } + + safeInfo->itemCount = 0; + + /* create the encrypted safe */ + safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn, + p12ctxt->pwfnarg); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + safeInfo->arena = p12ctxt->arena; + + /* convert the password to unicode */ + if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem, + PR_TRUE, PR_TRUE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* generate the encryption key */ + slot = p12ctxt->slot; + if(!slot) { + slot = PK11_GetInternalKeySlot(); + if(!slot) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + } + + algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo); + safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem, + PR_FALSE, p12ctxt->wincx); + if(!safeInfo->encryptionKey) { + goto loser; + } + + safeInfo->arena = p12ctxt->arena; + safeInfo->safe = NULL; + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + if(uniPwitem.data) { + SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); + } + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + } + + if(uniPwitem.data) { + SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/* SEC_PKCS12CreateUnencryptedSafe + * Creates an unencrypted safe within the export context. + * + * p12ctxt - the export context + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + + if(!p12ctxt) { + return NULL; + } + + /* create the safe info */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeInfo->itemCount = 0; + + /* create the safe content */ + safeInfo->cinfo = SEC_PKCS7CreateData(); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/* SEC_PKCS12CreatePubKeyEncryptedSafe + * Creates a safe which is protected by public key encryption. + * + * p12ctxt - the export context + * certDb - the certificate database + * signer - the signer's certificate + * recipients - the list of recipient certificates. + * algorithm - the encryption algorithm to use + * keysize - the algorithms key size (?) + */ +SEC_PKCS12SafeInfo * +SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt, + CERTCertDBHandle *certDb, + CERTCertificate *signer, + CERTCertificate **recipients, + SECOidTag algorithm, int keysize) +{ + SEC_PKCS12SafeInfo *safeInfo = NULL; + void *mark = NULL; + + if(!p12ctxt || !signer || !recipients || !(*recipients)) { + return NULL; + } + + /* allocate the safeInfo */ + mark = PORT_ArenaMark(p12ctxt->arena); + safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SEC_PKCS12SafeInfo)); + if(!safeInfo) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeInfo->itemCount = 0; + safeInfo->arena = p12ctxt->arena; + + /* create the enveloped content info using certUsageEmailSigner currently. + * XXX We need to eventually use something other than certUsageEmailSigner + */ + safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner, + certDb, algorithm, keysize, + p12ctxt->pwfn, p12ctxt->pwfnarg); + if(!safeInfo->cinfo) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* add recipients */ + if(recipients) { + unsigned int i = 0; + while(recipients[i] != NULL) { + SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i], + certUsageEmailRecipient, certDb); + if(rv != SECSuccess) { + goto loser; + } + i++; + } + } + + if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return safeInfo; + +loser: + if(safeInfo->cinfo) { + SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); + safeInfo->cinfo = NULL; + } + + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/********************************* + * Routines to handle the exporting of the keys and certificates + *********************************/ + +/* creates a safe contents which safeBags will be appended to */ +sec_PKCS12SafeContents * +sec_PKCS12CreateSafeContents(PRArenaPool *arena) +{ + sec_PKCS12SafeContents *safeContents; + + if(arena == NULL) { + return NULL; + } + + /* create the safe contents */ + safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12SafeContents)); + if(!safeContents) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the internal contents info */ + safeContents->safeBags = NULL; + safeContents->arena = arena; + safeContents->bagCount = 0; + + return safeContents; + +loser: + return NULL; +} + +/* appends a safe bag to a safeContents using the specified arena. + */ +SECStatus +sec_pkcs12_append_bag_to_safe_contents(PRArenaPool *arena, + sec_PKCS12SafeContents *safeContents, + sec_PKCS12SafeBag *safeBag) +{ + void *mark = NULL, *dummy = NULL; + + if(!arena || !safeBag || !safeContents) { + return SECFailure; + } + + mark = PORT_ArenaMark(arena); + if(!mark) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* allocate space for the list, or reallocate to increase space */ + if(!safeContents->safeBags) { + safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena, + (2 * sizeof(sec_PKCS12SafeBag *))); + dummy = safeContents->safeBags; + safeContents->bagCount = 0; + } else { + dummy = PORT_ArenaGrow(arena, safeContents->safeBags, + (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *), + (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *)); + safeContents->safeBags = (sec_PKCS12SafeBag **)dummy; + } + + if(!dummy) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + /* append the bag at the end and null terminate the list */ + safeContents->safeBags[safeContents->bagCount++] = safeBag; + safeContents->safeBags[safeContents->bagCount] = NULL; + + PORT_ArenaUnmark(arena, mark); + + return SECSuccess; +} + +/* appends a safeBag to a specific safeInfo. + */ +SECStatus +sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt, + SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag) +{ + sec_PKCS12SafeContents *dest; + SECStatus rv = SECFailure; + + if(!p12ctxt || !safeBag || !safeInfo) { + return SECFailure; + } + + if(!safeInfo->safe) { + safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena); + if(!safeInfo->safe) { + return SECFailure; + } + } + + dest = safeInfo->safe; + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag); + if(rv == SECSuccess) { + safeInfo->itemCount++; + } + + return rv; +} + +/* Creates a safeBag of the specified type, and if bagData is specified, + * the contents are set. The contents could be set later by the calling + * routine. + */ +sec_PKCS12SafeBag * +sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType, + void *bagData) +{ + sec_PKCS12SafeBag *safeBag; + PRBool setName = PR_TRUE; + void *mark = NULL; + SECStatus rv = SECSuccess; + SECOidData *oidData = NULL; + + if(!p12ctxt) { + return NULL; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + if(!mark) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(sec_PKCS12SafeBag)); + if(!safeBag) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* set the bags content based upon bag type */ + switch(bagType) { + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + safeBag->safeBagContent.pkcs8KeyBag = + (SECKEYPrivateKeyInfo *)bagData; + break; + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData; + break; + case SEC_OID_PKCS12_V1_CRL_BAG_ID: + safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData; + break; + case SEC_OID_PKCS12_V1_SECRET_BAG_ID: + safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData; + break; + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + safeBag->safeBagContent.pkcs8ShroudedKeyBag = + (SECKEYEncryptedPrivateKeyInfo *)bagData; + break; + case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: + safeBag->safeBagContent.safeContents = + (sec_PKCS12SafeContents *)bagData; + setName = PR_FALSE; + break; + default: + goto loser; + } + + oidData = SECOID_FindOIDByTag(bagType); + if(oidData) { + rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + } else { + goto loser; + } + + safeBag->arena = p12ctxt->arena; + PORT_ArenaUnmark(p12ctxt->arena, mark); + + return safeBag; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return NULL; +} + +/* Creates a new certificate bag and returns a pointer to it. If an error + * occurs NULL is returned. + */ +sec_PKCS12CertBag * +sec_PKCS12NewCertBag(PRArenaPool *arena, SECOidTag certType) +{ + sec_PKCS12CertBag *certBag = NULL; + SECOidData *bagType = NULL; + SECStatus rv; + void *mark = NULL; + + if(!arena) { + return NULL; + } + + mark = PORT_ArenaMark(arena); + certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12CertBag)); + if(!certBag) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + bagType = SECOID_FindOIDByTag(certType); + if(!bagType) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + return certBag; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +/* Creates a new CRL bag and returns a pointer to it. If an error + * occurs NULL is returned. + */ +sec_PKCS12CRLBag * +sec_PKCS12NewCRLBag(PRArenaPool *arena, SECOidTag crlType) +{ + sec_PKCS12CRLBag *crlBag = NULL; + SECOidData *bagType = NULL; + SECStatus rv; + void *mark = NULL; + + if(!arena) { + return NULL; + } + + mark = PORT_ArenaMark(arena); + crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena, + sizeof(sec_PKCS12CRLBag)); + if(!crlBag) { + PORT_ArenaRelease(arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + bagType = SECOID_FindOIDByTag(crlType); + if(!bagType) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + return crlBag; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +/* sec_PKCS12AddAttributeToBag + * adds an attribute to a safeBag. currently, the only attributes supported + * are those which are specified within PKCS 12. + * + * p12ctxt - the export context + * safeBag - the safeBag to which attributes are appended + * attrType - the attribute type + * attrData - the attribute data + */ +SECStatus +sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt, + sec_PKCS12SafeBag *safeBag, SECOidTag attrType, + SECItem *attrData) +{ + sec_PKCS12Attribute *attribute; + void *mark = NULL, *dummy = NULL; + SECOidData *oiddata = NULL; + SECItem unicodeName = { siBuffer, NULL, 0}; + void *src = NULL; + unsigned int nItems = 0; + SECStatus rv; + + if(!safeBag || !p12ctxt) { + return SECFailure; + } + + mark = PORT_ArenaMark(safeBag->arena); + + /* allocate the attribute */ + attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena, + sizeof(sec_PKCS12Attribute)); + if(!attribute) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the attribute */ + oiddata = SECOID_FindOIDByTag(attrType); + if(!oiddata) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) != + SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + nItems = 1; + switch(attrType) { + case SEC_OID_PKCS9_LOCAL_KEY_ID: + { + src = attrData; + break; + } + case SEC_OID_PKCS9_FRIENDLY_NAME: + { + if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, + &unicodeName, attrData, PR_FALSE, + PR_FALSE, PR_TRUE)) { + goto loser; + } + src = &unicodeName; + break; + } + default: + goto loser; + } + + /* append the attribute to the attribute value list */ + attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, + ((nItems + 1) * sizeof(SECItem *))); + if(!attribute->attrValue) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* XXX this will need to be changed if attributes requiring more than + * one element are ever used. + */ + attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECItem)); + if(!attribute->attrValue[0]) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + attribute->attrValue[1] = NULL; + + rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0], + (SECItem*)src); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* append the attribute to the safeBag attributes */ + if(safeBag->nAttribs) { + dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs, + ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)), + ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *))); + safeBag->attribs = (sec_PKCS12Attribute **)dummy; + } else { + safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena, + 2 * sizeof(sec_PKCS12Attribute *)); + dummy = safeBag->attribs; + } + if(!dummy) { + goto loser; + } + + safeBag->attribs[safeBag->nAttribs] = attribute; + safeBag->attribs[++safeBag->nAttribs] = NULL; + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return SECFailure; +} + +/* SEC_PKCS12AddCert + * Adds a certificate to the data being exported. + * + * p12ctxt - the export context + * safe - the safeInfo to which the certificate is placed + * nestedDest - if the cert is to be placed within a nested safeContents then, + * this value is to be specified with the destination + * cert - the cert to export + * certDb - the certificate database handle + * keyId - a unique identifier to associate a certificate/key pair + * includeCertChain - PR_TRUE if the certificate chain is to be included. + */ +SECStatus +SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, + void *nestedDest, CERTCertificate *cert, + CERTCertDBHandle *certDb, SECItem *keyId, + PRBool includeCertChain) +{ + sec_PKCS12CertBag *certBag; + sec_PKCS12SafeBag *safeBag; + void *mark; + SECStatus rv; + SECItem nick = {siBuffer, NULL,0}; + + if(!p12ctxt || !cert) { + return SECFailure; + } + mark = PORT_ArenaMark(p12ctxt->arena); + + /* allocate the cert bag */ + certBag = sec_PKCS12NewCertBag(p12ctxt->arena, + SEC_OID_PKCS9_X509_CERT); + if(!certBag) { + goto loser; + } + + if(SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert, + &cert->derCert) != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* if the cert chain is to be included, we should only be exporting + * the cert from our internal database. + */ + if(includeCertChain) { + CERTCertificateList *certList = CERT_CertChainFromCert(cert, + certUsageSSLClient, + PR_TRUE); + unsigned int count = 0; + if(!certList) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* add cert chain */ + for(count = 0; count < (unsigned int)certList->len; count++) { + if(SECITEM_CompareItem(&certList->certs[count], &cert->derCert) + != SECEqual) { + CERTCertificate *tempCert; + + /* decode the certificate */ + tempCert = CERT_NewTempCertificate(certDb, + &certList->certs[count], NULL, + PR_FALSE, PR_TRUE); + if(!tempCert) { + CERT_DestroyCertificateList(certList); + goto loser; + } + + /* add the certificate */ + if(SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert, certDb, + NULL, PR_FALSE) != SECSuccess) { + CERT_DestroyCertificate(tempCert); + CERT_DestroyCertificateList(certList); + goto loser; + } + CERT_DestroyCertificate(tempCert); + } + } + CERT_DestroyCertificateList(certList); + } + + /* if the certificate has a nickname, we will set the friendly name + * to that. + */ + if(cert->nickname) { + if (cert->slot && !PK11_IsInternal(cert->slot)) { + /* + * The cert is coming off of an external token, + * let's strip the token name from the nickname + * and only add what comes after the colon as the + * nickname. -javi + */ + char *delimit; + + delimit = PORT_Strchr(cert->nickname,':'); + if (delimit == NULL) { + nick.data = (unsigned char *)cert->nickname; + nick.len = PORT_Strlen(cert->nickname); + } else { + delimit++; + nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena, + delimit); + nick.len = PORT_Strlen(delimit); + } + } else { + nick.data = (unsigned char *)cert->nickname; + nick.len = PORT_Strlen(cert->nickname); + } + } + + safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID, + certBag); + if(!safeBag) { + goto loser; + } + + /* add the friendly name and keyId attributes, if necessary */ + if(nick.data) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, + SEC_OID_PKCS9_FRIENDLY_NAME, &nick) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + /* append the cert safeBag */ + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + safeBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag); + } + + if(rv != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + if(mark) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } + + return SECFailure; +} + +/* SEC_PKCS12AddEncryptedKey + * Extracts the key associated with a particular certificate and exports + * it. + * + * p12ctxt - the export context + * safe - the safeInfo to place the key in + * nestedDest - the nested safeContents to place a key + * cert - the certificate which the key belongs to + * shroudKey - encrypt the private key for export. This value should + * always be true. lower level code will not allow the export + * of unencrypted private keys. + * algorithm - the algorithm with which to encrypt the private key + * pwitem - the password to encrypted the private key with + * keyId - the keyID attribute + * nickName - the nickname attribute + */ +static SECStatus +SEC_PKCS12AddEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, + SECKEYEncryptedPrivateKeyInfo *epki, SEC_PKCS12SafeInfo *safe, + void *nestedDest, SECItem *keyId, SECItem *nickName) +{ + void *mark; + void *keyItem; + SECOidTag keyType; + SECStatus rv = SECFailure; + sec_PKCS12SafeBag *returnBag; + + if(!p12ctxt || !safe || !epki) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, + (SECKEYEncryptedPrivateKeyInfo *)keyItem, + epki); + keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; + + if(rv != SECSuccess) { + goto loser; + } + + /* create the safe bag and set any attributes */ + returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); + if(!returnBag) { + rv = SECFailure; + goto loser; + } + + if(nickName) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, + SEC_OID_PKCS9_FRIENDLY_NAME, nickName) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + returnBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); + } + +loser: + + if (rv != SECSuccess) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } else { + PORT_ArenaUnmark(p12ctxt->arena, mark); + } + + return rv; +} + +/* SEC_PKCS12AddKeyForCert + * Extracts the key associated with a particular certificate and exports + * it. + * + * p12ctxt - the export context + * safe - the safeInfo to place the key in + * nestedDest - the nested safeContents to place a key + * cert - the certificate which the key belongs to + * shroudKey - encrypt the private key for export. This value should + * always be true. lower level code will not allow the export + * of unencrypted private keys. + * algorithm - the algorithm with which to encrypt the private key + * pwitem - the password to encrypt the private key with + * keyId - the keyID attribute + * nickName - the nickname attribute + */ +SECStatus +SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, + void *nestedDest, CERTCertificate *cert, + PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem, + SECItem *keyId, SECItem *nickName) +{ + void *mark; + void *keyItem; + SECOidTag keyType; + SECStatus rv = SECFailure; + SECItem nickname = {siBuffer,NULL,0}, uniPwitem = {siBuffer, NULL, 0}; + sec_PKCS12SafeBag *returnBag; + + if(!p12ctxt || !cert || !safe) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* retrieve the key based upon the type that it is and + * specify the type of safeBag to store the key in + */ + if(!shroudKey) { + + /* extract the key unencrypted. this will most likely go away */ + SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert, + p12ctxt->wincx); + if(!pki) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + return SECFailure; + } + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena, + (SECKEYPrivateKeyInfo *)keyItem, pki); + keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID; + SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); + } else { + + /* extract the key encrypted */ + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + PK11SlotInfo *slot = p12ctxt->slot; + + if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem, + pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* we want to make sure to take the key out of the key slot */ + if(PK11_IsInternal(p12ctxt->slot)) { + slot = PK11_GetInternalKeySlot(); + } + + epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, + &uniPwitem, cert, 1, + p12ctxt->wincx); + if(PK11_IsInternal(p12ctxt->slot)) { + PK11_FreeSlot(slot); + } + + keyItem = PORT_ArenaZAlloc(p12ctxt->arena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(!keyItem) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(!epki) { + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); + return SECFailure; + } + rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, + (SECKEYEncryptedPrivateKeyInfo *)keyItem, + epki); + keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; + SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); + } + + if(rv != SECSuccess) { + goto loser; + } + + /* if no nickname specified, let's see if the certificate has a + * nickname. + */ + if(!nickName) { + if(cert->nickname) { + nickname.data = (unsigned char *)cert->nickname; + nickname.len = PORT_Strlen(cert->nickname); + nickName = &nickname; + } + } + + /* create the safe bag and set any attributes */ + returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); + if(!returnBag) { + rv = SECFailure; + goto loser; + } + + if(nickName) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, + SEC_OID_PKCS9_FRIENDLY_NAME, nickName) + != SECSuccess) { + goto loser; + } + } + + if(keyId) { + if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID, + keyId) != SECSuccess) { + goto loser; + } + } + + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + returnBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); + } + +loser: + + if (rv != SECSuccess) { + PORT_ArenaRelease(p12ctxt->arena, mark); + } else { + PORT_ArenaUnmark(p12ctxt->arena, mark); + } + + return rv; +} + +/* SEC_PKCS12AddCertAndEncryptedKey + * Add a certificate and key pair to be exported. + * + * p12ctxt - the export context + * certSafe - the safeInfo where the cert is stored + * certNestedDest - the nested safeContents to store the cert + * keySafe - the safeInfo where the key is stored + * keyNestedDest - the nested safeContents to store the key + * shroudKey - extract the private key encrypted? + * pwitem - the password with which the key is encrypted + * algorithm - the algorithm with which the key is encrypted + */ +SECStatus +SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, + SECItem *derCert, void *keySafe, + void *keyNestedDest, SECKEYEncryptedPrivateKeyInfo *epki, + char *nickname) +{ + SECStatus rv = SECFailure; + SGNDigestInfo *digest = NULL; + void *mark = NULL; + CERTCertificate *cert; + SECItem nick = {siBuffer, NULL,0}, *nickPtr = NULL; + + if(!p12ctxt || !certSafe || !keySafe || !derCert) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert, + NULL, PR_FALSE, PR_TRUE); + if(!cert) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + cert->nickname = nickname; + + /* generate the thumbprint of the cert to use as a keyId */ + digest = sec_pkcs12_compute_thumbprint(&cert->derCert); + if(!digest) { + CERT_DestroyCertificate(cert); + return SECFailure; + } + + /* add the certificate */ + rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, + certNestedDest, cert, NULL, + &digest->digest, PR_FALSE); + if(rv != SECSuccess) { + goto loser; + } + + if(nickname) { + nick.data = (unsigned char *)nickname; + nick.len = PORT_Strlen(nickname); + nickPtr = &nick; + } else { + nickPtr = NULL; + } + + /* add the key */ + rv = SEC_PKCS12AddEncryptedKey(p12ctxt, epki, (SEC_PKCS12SafeInfo*)keySafe, + keyNestedDest, &digest->digest, nickPtr ); + if(rv != SECSuccess) { + goto loser; + } + + SGN_DestroyDigestInfo(digest); + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + SGN_DestroyDigestInfo(digest); + CERT_DestroyCertificate(cert); + PORT_ArenaRelease(p12ctxt->arena, mark); + + return SECFailure; +} + +/* SEC_PKCS12AddCertAndKey + * Add a certificate and key pair to be exported. + * + * p12ctxt - the export context + * certSafe - the safeInfo where the cert is stored + * certNestedDest - the nested safeContents to store the cert + * keySafe - the safeInfo where the key is stored + * keyNestedDest - the nested safeContents to store the key + * shroudKey - extract the private key encrypted? + * pwitem - the password with which the key is encrypted + * algorithm - the algorithm with which the key is encrypted + */ +SECStatus +SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, + void *certSafe, void *certNestedDest, + CERTCertificate *cert, CERTCertDBHandle *certDb, + void *keySafe, void *keyNestedDest, + PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm) +{ + SECStatus rv = SECFailure; + SGNDigestInfo *digest = NULL; + void *mark = NULL; + + if(!p12ctxt || !certSafe || !keySafe || !cert) { + return SECFailure; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + /* generate the thumbprint of the cert to use as a keyId */ + digest = sec_pkcs12_compute_thumbprint(&cert->derCert); + if(!digest) { + PORT_ArenaRelease(p12ctxt->arena, mark); + return SECFailure; + } + + /* add the certificate */ + rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, + (SEC_PKCS12SafeInfo*)certNestedDest, cert, certDb, + &digest->digest, PR_TRUE); + if(rv != SECSuccess) { + goto loser; + } + + /* add the key */ + rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo*)keySafe, + keyNestedDest, cert, + shroudKey, algorithm, pwitem, + &digest->digest, NULL ); + if(rv != SECSuccess) { + goto loser; + } + + SGN_DestroyDigestInfo(digest); + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return SECSuccess; + +loser: + SGN_DestroyDigestInfo(digest); + PORT_ArenaRelease(p12ctxt->arena, mark); + + return SECFailure; +} + +/* SEC_PKCS12CreateNestedSafeContents + * Allows nesting of safe contents to be implemented. No limit imposed on + * depth. + * + * p12ctxt - the export context + * baseSafe - the base safeInfo + * nestedDest - a parent safeContents (?) + */ +void * +SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt, + void *baseSafe, void *nestedDest) +{ + sec_PKCS12SafeContents *newSafe; + sec_PKCS12SafeBag *safeContentsBag; + void *mark; + SECStatus rv; + + if(!p12ctxt || !baseSafe) { + return NULL; + } + + mark = PORT_ArenaMark(p12ctxt->arena); + + newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena); + if(!newSafe) { + PORT_ArenaRelease(p12ctxt->arena, mark); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + /* create the safeContents safeBag */ + safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt, + SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID, + newSafe); + if(!safeContentsBag) { + goto loser; + } + + /* append the safeContents to the appropriate area */ + if(nestedDest) { + rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, + (sec_PKCS12SafeContents*)nestedDest, + safeContentsBag); + } else { + rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo*)baseSafe, + safeContentsBag); + } + if(rv != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(p12ctxt->arena, mark); + return newSafe; + +loser: + PORT_ArenaRelease(p12ctxt->arena, mark); + return NULL; +} + +/********************************* + * Encoding routines + *********************************/ + +/* set up the encoder context based on information in the export context + * and return the newly allocated enocoder context. A return of NULL + * indicates an error occurred. + */ +sec_PKCS12EncoderContext * +sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp) +{ + sec_PKCS12EncoderContext *p12enc = NULL; + unsigned int i, nonEmptyCnt; + + if(!p12exp || !p12exp->safeInfos) { + return NULL; + } + + /* check for any empty safes and skip them */ + i = nonEmptyCnt = 0; + while(p12exp->safeInfos[i]) { + if(p12exp->safeInfos[i]->itemCount) { + nonEmptyCnt++; + } + i++; + } + if(nonEmptyCnt == 0) { + return NULL; + } + p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL; + + /* allocate the encoder context */ + p12enc = (sec_PKCS12EncoderContext*)PORT_ArenaZAlloc(p12exp->arena, + sizeof(sec_PKCS12EncoderContext)); + if(!p12enc) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + p12enc->arena = p12exp->arena; + p12enc->p12exp = p12exp; + + /* set up the PFX version and information */ + PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem)); + if(!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version), + SEC_PKCS12_VERSION) ) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* set up the authenticated safe content info based on the + * type of integrity being used. this should be changed to + * enforce integrity mode, but will not be implemented until + * it is confirmed that integrity must be in place + */ + if(p12exp->integrityEnabled && !p12exp->pwdIntegrity) { + SECStatus rv; + + /* create public key integrity mode */ + p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData( + p12exp->integrityInfo.pubkeyInfo.cert, + certUsageEmailSigner, + p12exp->integrityInfo.pubkeyInfo.certDb, + p12exp->integrityInfo.pubkeyInfo.algorithm, + NULL, + p12exp->pwfn, + p12exp->pwfnarg); + if(!p12enc->aSafeCinfo) { + goto loser; + } + if(SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo,NULL) != SECSuccess) { + SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo); + goto loser; + } + rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo); + PORT_Assert(rv == SECSuccess); + } else { + p12enc->aSafeCinfo = SEC_PKCS7CreateData(); + + /* init password pased integrity mode */ + if(p12exp->integrityEnabled) { + SECItem pwd = {siBuffer,NULL, 0}, *key; + SECItem *salt = sec_pkcs12_generate_salt(); + PBEBitGenContext *pbeCtxt = NULL; + + /* zero out macData and set values */ + PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData)); + + if(!salt) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + if(SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) + != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + SECITEM_ZfreeItem(salt, PR_TRUE); + + /* generate HMAC key */ + if(!sec_pkcs12_convert_item_to_unicode(p12exp->arena, &pwd, + p12exp->integrityInfo.pwdInfo.password, PR_TRUE, + PR_TRUE, PR_TRUE)) { + goto loser; + } + pbeCtxt = PBE_CreateContext(p12exp->integrityInfo.pwdInfo.algorithm, + pbeBitGenIntegrityKey, &pwd, + &(p12enc->mac.macSalt), 160, 1); + if(!pbeCtxt) { + goto loser; + } + key = PBE_GenerateBits(pbeCtxt); + PBE_DestroyContext(pbeCtxt); + if(!key) { + goto loser; + } + + /* initialize hmac */ + p12enc->hmacCx = HMAC_Create( + p12exp->integrityInfo.pwdInfo.algorithm, + key->data, key->len); + SECITEM_ZfreeItem(key, PR_TRUE); + if(!p12enc->hmacCx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + HMAC_Begin((HMACContext*)p12enc->hmacCx); + } + } + + if(!p12enc->aSafeCinfo) { + goto loser; + } + + return p12enc; + +loser: + if(p12enc) { + if(p12enc->aSafeCinfo) { + SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo); + } + + PORT_Free(p12enc); + } + + return NULL; +} + +/* callback wrapper to allow the ASN1 engine to call the PKCS 12 + * output routines. + */ +static void +sec_pkcs12_encoder_out(void *arg, const char *buf, unsigned long len, + int depth, SEC_ASN1EncodingPart data_kind) +{ + struct sec_pkcs12_encoder_output *output; + + output = (struct sec_pkcs12_encoder_output*)arg; + (* output->outputfn)(output->outputarg, buf, len); +} + +/* callback wrapper to wrap SEC_PKCS7EncoderUpdate for ASN1 encoder + */ +static void +sec_pkcs12_wrap_pkcs7_encoder_update(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + SEC_PKCS7EncoderContext *ecx; + if(!buf || !len) { + return; + } + + ecx = (SEC_PKCS7EncoderContext*)arg; + SEC_PKCS7EncoderUpdate(ecx, buf, len); +} + +/* callback wrapper to wrap SEC_ASN1EncoderUpdate for PKCS 7 encoding + */ +static void +sec_pkcs12_wrap_asn1_update_for_p7_update(void *arg, const char *buf, + unsigned long len) +{ + if(!buf && !len) return; + + SEC_ASN1EncoderUpdate((SEC_ASN1EncoderContext*)arg, buf, len); +} + +/* callback wrapper which updates the HMAC and passes on bytes to the + * appropriate output function. + */ +static void +sec_pkcs12_asafe_update_hmac_and_encode_bits(void *arg, const char *buf, + unsigned long len, int depth, + SEC_ASN1EncodingPart data_kind) +{ + sec_PKCS12EncoderContext *p12ecx; + + p12ecx = (sec_PKCS12EncoderContext*)arg; + HMAC_Update((HMACContext*)p12ecx->hmacCx, (unsigned char *)buf, len); + sec_pkcs12_wrap_pkcs7_encoder_update(p12ecx->aSafeP7Ecx, buf, len, + depth, data_kind); +} + +/* this function encodes content infos which are part of the + * sequence of content infos labeled AuthenticatedSafes + */ +static SECStatus +sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx) +{ + SECStatus rv = SECSuccess; + SEC_PKCS5KeyAndPassword keyPwd; + SEC_PKCS7EncoderContext *p7ecx; + SEC_PKCS7ContentInfo *cinfo; + SEC_ASN1EncoderContext *ecx = NULL; + + void *arg = NULL; + + if(p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) { + SEC_PKCS12SafeInfo *safeInfo; + SECOidTag cinfoType; + + safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe]; + + /* skip empty safes */ + if(safeInfo->itemCount == 0) { + return SECSuccess; + } + + cinfo = safeInfo->cinfo; + cinfoType = SEC_PKCS7ContentType(cinfo); + + /* determine the safe type and set the appropriate argument */ + switch(cinfoType) { + case SEC_OID_PKCS7_DATA: + arg = NULL; + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + keyPwd.pwitem = &safeInfo->pwitem; + keyPwd.key = safeInfo->encryptionKey; + arg = &keyPwd; + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + arg = NULL; + break; + default: + return SECFailure; + + } + + /* start the PKCS7 encoder */ + p7ecx = SEC_PKCS7EncoderStart(cinfo, + sec_pkcs12_wrap_asn1_update_for_p7_update, + p12ecx->aSafeEcx, (PK11SymKey *)arg); + if(!p7ecx) { + goto loser; + } + + /* encode safe contents */ + ecx = SEC_ASN1EncoderStart(safeInfo->safe, sec_PKCS12SafeContentsTemplate, + sec_pkcs12_wrap_pkcs7_encoder_update, p7ecx); + if(!ecx) { + goto loser; + } + rv = SEC_ASN1EncoderUpdate(ecx, NULL, 0); + SEC_ASN1EncoderFinish(ecx); + ecx = NULL; + if(rv != SECSuccess) { + goto loser; + } + + + /* finish up safe content info */ + rv = SEC_PKCS7EncoderFinish(p7ecx, p12ecx->p12exp->pwfn, + p12ecx->p12exp->pwfnarg); + } + + return SECSuccess; + +loser: + if(p7ecx) { + SEC_PKCS7EncoderFinish(p7ecx, p12ecx->p12exp->pwfn, + p12ecx->p12exp->pwfnarg); + } + + if(ecx) { + SEC_ASN1EncoderFinish(ecx); + } + + return SECFailure; +} + +/* finish the HMAC and encode the macData so that it can be + * encoded. + */ +static SECStatus +sec_pkcs12_update_mac(sec_PKCS12EncoderContext *p12ecx) +{ + SECItem hmac = { siBuffer, NULL, 0 }; + SECStatus rv; + SGNDigestInfo *di = NULL; + void *dummy; + + if(!p12ecx) { + return SECFailure; + } + + /* make sure we are using password integrity mode */ + if(!p12ecx->p12exp->integrityEnabled) { + return SECSuccess; + } + + if(!p12ecx->p12exp->pwdIntegrity) { + return SECSuccess; + } + + /* finish the hmac */ + hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH); + if(!hmac.data) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + rv = HMAC_Finish((HMACContext*)p12ecx->hmacCx, + hmac.data, &hmac.len, SHA1_LENGTH); + + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* create the digest info */ + di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm, + hmac.data, hmac.len); + if(!di) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + + rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di); + if(rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + /* encode the mac data */ + dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData, + &p12ecx->mac, sec_PKCS12MacDataTemplate); + if(!dummy) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + +loser: + if(di) { + SGN_DestroyDigestInfo(di); + } + if(hmac.data) { + SECITEM_ZfreeItem(&hmac, PR_FALSE); + } + HMAC_Destroy((HMACContext*)p12ecx->hmacCx); + p12ecx->hmacCx = NULL; + + return rv; +} + +/* wraps the ASN1 encoder update for PKCS 7 encoder */ +static void +sec_pkcs12_wrap_asn1_encoder_update(void *arg, const char *buf, + unsigned long len) +{ + SEC_ASN1EncoderContext *cx; + + cx = (SEC_ASN1EncoderContext*)arg; + SEC_ASN1EncoderUpdate(cx, buf, len); +} + +/* pfx notify function for ASN1 encoder. we want to stop encoding, once we reach + * the authenticated safe. at that point, the encoder will be updated via streaming + * as the authenticated safe is encoded. + */ +static void +sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth) +{ + sec_PKCS12EncoderContext *p12ecx; + + if(!before) { + return; + } + + /* look for authenticated safe */ + p12ecx = (sec_PKCS12EncoderContext*)arg; + if(dest != &p12ecx->pfx.encodedAuthSafe) { + return; + } + + SEC_ASN1EncoderSetTakeFromBuf(p12ecx->ecx); + SEC_ASN1EncoderSetStreaming(p12ecx->ecx); + SEC_ASN1EncoderClearNotifyProc(p12ecx->ecx); +} + +/* SEC_PKCS12Encode + * Encodes the PFX item and returns it to the output function, via + * callback. the output function must be capable of multiple updates. + * + * p12exp - the export context + * output - the output function callback, will be called more than once, + * must be able to accept streaming data. + * outputarg - argument for the output callback. + */ +SECStatus +SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, + SEC_PKCS12EncoderOutputCallback output, void *outputarg) +{ + sec_PKCS12EncoderContext *p12enc; + struct sec_pkcs12_encoder_output outInfo; + SECStatus rv; + + if(!p12exp || !output) { + return SECFailure; + } + + /* get the encoder context */ + p12enc = sec_pkcs12_encoder_start_context(p12exp); + if(!p12enc) { + return SECFailure; + } + + outInfo.outputfn = output; + outInfo.outputarg = outputarg; + + /* set up PFX encoder. Set it for streaming */ + p12enc->ecx = SEC_ASN1EncoderStart(&p12enc->pfx, sec_PKCS12PFXItemTemplate, + sec_pkcs12_encoder_out, + &outInfo); + if(!p12enc->ecx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + goto loser; + } + SEC_ASN1EncoderSetStreaming(p12enc->ecx); + SEC_ASN1EncoderSetNotifyProc(p12enc->ecx, sec_pkcs12_encoder_pfx_notify, p12enc); + rv = SEC_ASN1EncoderUpdate(p12enc->ecx, NULL, 0); + if(rv != SECSuccess) { + rv = SECFailure; + goto loser; + } + + /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */ + p12enc->aSafeP7Ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo, + sec_pkcs12_wrap_asn1_encoder_update, + p12enc->ecx, NULL); + if(!p12enc->aSafeP7Ecx) { + rv = SECFailure; + goto loser; + } + + /* encode asafe */ + if(p12enc->p12exp->integrityEnabled && p12enc->p12exp->pwdIntegrity) { + p12enc->aSafeEcx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, + sec_PKCS12AuthenticatedSafeTemplate, + sec_pkcs12_asafe_update_hmac_and_encode_bits, + p12enc); + } else { + p12enc->aSafeEcx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, + sec_PKCS12AuthenticatedSafeTemplate, + sec_pkcs12_wrap_pkcs7_encoder_update, + p12enc->aSafeP7Ecx); + } + if(!p12enc->aSafeEcx) { + rv = SECFailure; + goto loser; + } + SEC_ASN1EncoderSetStreaming(p12enc->aSafeEcx); + SEC_ASN1EncoderSetTakeFromBuf(p12enc->aSafeEcx); + + /* encode each of the safes */ + while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) { + sec_pkcs12_encoder_asafe_process(p12enc); + p12enc->currentSafe++; + } + SEC_ASN1EncoderClearTakeFromBuf(p12enc->aSafeEcx); + SEC_ASN1EncoderClearStreaming(p12enc->aSafeEcx); + SEC_ASN1EncoderUpdate(p12enc->aSafeEcx, NULL, 0); + SEC_ASN1EncoderFinish(p12enc->aSafeEcx); + + /* finish the encoding of the authenticated safes */ + rv = SEC_PKCS7EncoderFinish(p12enc->aSafeP7Ecx, p12exp->pwfn, + p12exp->pwfnarg); + if(rv != SECSuccess) { + goto loser; + } + + SEC_ASN1EncoderClearTakeFromBuf(p12enc->ecx); + SEC_ASN1EncoderClearStreaming(p12enc->ecx); + + /* update the mac, if necessary */ + rv = sec_pkcs12_update_mac(p12enc); + if(rv != SECSuccess) { + goto loser; + } + + /* finish encoding the pfx */ + rv = SEC_ASN1EncoderUpdate(p12enc->ecx, NULL, 0); + + SEC_ASN1EncoderFinish(p12enc->ecx); + +loser: + return rv; +} + +void +SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx) +{ + int i = 0; + + if(!p12ecx) { + return; + } + + if(p12ecx->safeInfos) { + i = 0; + while(p12ecx->safeInfos[i] != NULL) { + if(p12ecx->safeInfos[i]->encryptionKey) { + PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey); + } + if(p12ecx->safeInfos[i]->cinfo) { + SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo); + } + i++; + } + } + + PORT_FreeArena(p12ecx->arena, PR_TRUE); +} + + +/********************************* + * All-in-one routines for exporting certificates + *********************************/ +struct inPlaceEncodeInfo { + PRBool error; + SECItem outItem; +}; + +static void +sec_pkcs12_in_place_encoder_output(void *arg, const char *buf, unsigned long len) +{ + struct inPlaceEncodeInfo *outInfo = (struct inPlaceEncodeInfo*)arg; + + if(!outInfo || !len || outInfo->error) { + return; + } + + if(!outInfo->outItem.data) { + outInfo->outItem.data = (unsigned char*)PORT_ZAlloc(len); + outInfo->outItem.len = 0; + } else { + if(!PORT_Realloc(&(outInfo->outItem.data), (outInfo->outItem.len + len))) { + SECITEM_ZfreeItem(&(outInfo->outItem), PR_FALSE); + outInfo->outItem.data = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + outInfo->error = PR_TRUE; + return; + } + } + + PORT_Memcpy(&(outInfo->outItem.data[outInfo->outItem.len]), buf, len); + outInfo->outItem.len += len; + + return; +} + +/* + * SEC_PKCS12ExportCertifcateAndKeyUsingPassword + * Exports a certificate/key pair using password-based encryption and + * authentication. + * + * pwfn, pwfnarg - password function and argument for the key database + * cert - the certificate to export + * certDb - certificate database + * pwitem - the password to use + * shroudKey - encrypt the key externally, + * keyShroudAlg - encryption algorithm for key + * encryptionAlg - the algorithm with which data is encrypted + * integrityAlg - the algorithm for integrity + */ +SECItem * +SEC_PKCS12ExportCertificateAndKeyUsingPassword( + SECKEYGetPasswordKey pwfn, void *pwfnarg, + CERTCertificate *cert, PK11SlotInfo *slot, + CERTCertDBHandle *certDb, SECItem *pwitem, + PRBool shroudKey, SECOidTag shroudAlg, + PRBool encryptCert, SECOidTag certEncAlg, + SECOidTag integrityAlg, void *wincx) +{ + struct inPlaceEncodeInfo outInfo; + SEC_PKCS12ExportContext *p12ecx = NULL; + SEC_PKCS12SafeInfo *keySafe, *certSafe; + SECItem *returnItem = NULL; + + if(!cert || !pwitem || !slot) { + return NULL; + } + + outInfo.error = PR_FALSE; + outInfo.outItem.data = NULL; + outInfo.outItem.len = 0; + + p12ecx = SEC_PKCS12CreateExportContext(pwfn, pwfnarg, slot, wincx); + if(!p12ecx) { + return NULL; + } + + /* set up cert safe */ + if(encryptCert) { + certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certEncAlg); + } else { + certSafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx); + } + if(!certSafe) { + goto loser; + } + + /* set up key safe */ + if(shroudKey) { + keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx); + } else { + keySafe = certSafe; + } + if(!keySafe) { + goto loser; + } + + /* add integrity mode */ + if(SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, integrityAlg) + != SECSuccess) { + goto loser; + } + + /* add cert and key pair */ + if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert, certDb, + keySafe, NULL, shroudKey, pwitem, shroudAlg) + != SECSuccess) { + goto loser; + } + + /* encode the puppy */ + if(SEC_PKCS12Encode(p12ecx, sec_pkcs12_in_place_encoder_output, &outInfo) + != SECSuccess) { + goto loser; + } + if(outInfo.error) { + goto loser; + } + + SEC_PKCS12DestroyExportContext(p12ecx); + + returnItem = SECITEM_DupItem(&outInfo.outItem); + SECITEM_ZfreeItem(&outInfo.outItem, PR_FALSE); + + return returnItem; + +loser: + if(outInfo.outItem.data) { + SECITEM_ZfreeItem(&(outInfo.outItem), PR_TRUE); + } + + if(p12ecx) { + SEC_PKCS12DestroyExportContext(p12ecx); + } + + return NULL; +} + + |