| Index: nss_pkcs12/p12e.c
|
| ===================================================================
|
| --- nss_pkcs12/p12e.c (revision 0)
|
| +++ nss_pkcs12/p12e.c (revision 0)
|
| @@ -0,0 +1,2079 @@
|
| +/* This Source Code Form is subject to the terms of the Mozilla Public
|
| + * License, v. 2.0. If a copy of the MPL was not distributed with this
|
| + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
| +
|
| +#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 "prcpucfg.h"
|
| +
|
| +extern const int NSS_PBE_DEFAULT_ITERATION_COUNT; /* defined in p7create.c */
|
| +
|
| +/*
|
| +** This PKCS12 file encoder uses numerous nested ASN.1 and PKCS7 encoder
|
| +** contexts. It can be difficult to keep straight. Here's a picture:
|
| +**
|
| +** "outer" ASN.1 encoder. The output goes to the library caller's CB.
|
| +** "middle" PKCS7 encoder. Feeds the "outer" ASN.1 encoder.
|
| +** "middle" ASN1 encoder. Encodes the encrypted aSafes.
|
| +** Feeds the "middle" P7 encoder above.
|
| +** "inner" PKCS7 encoder. Encrypts the "authenticated Safes" (aSafes)
|
| +** Feeds the "middle" ASN.1 encoder above.
|
| +** "inner" ASN.1 encoder. Encodes the unencrypted aSafes.
|
| +** Feeds the "inner" P7 enocder above.
|
| +**
|
| +** Buffering has been added at each point where the output of an ASN.1
|
| +** encoder feeds the input of a PKCS7 encoder.
|
| +*/
|
| +
|
| +/*********************************
|
| + * Output buffer object, used to buffer output from ASN.1 encoder
|
| + * before passing data on down to the next PKCS7 encoder.
|
| + *********************************/
|
| +
|
| +#define PK12_OUTPUT_BUFFER_SIZE 8192
|
| +
|
| +struct sec_pkcs12OutputBufferStr {
|
| + SEC_PKCS7EncoderContext * p7eCx;
|
| + PK11Context * hmacCx;
|
| + unsigned int numBytes;
|
| + unsigned int bufBytes;
|
| + char buf[PK12_OUTPUT_BUFFER_SIZE];
|
| +};
|
| +typedef struct sec_pkcs12OutputBufferStr sec_pkcs12OutputBuffer;
|
| +
|
| +/*********************************
|
| + * 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 {
|
| + PLArenaPool *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 {
|
| + PLArenaPool *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 {
|
| + PLArenaPool *arena;
|
| + SEC_PKCS12ExportContext *p12exp;
|
| +
|
| + /* encoder information - this is set up based on whether
|
| + * password based or public key pased privacy is being used
|
| + */
|
| + SEC_ASN1EncoderContext *outerA1ecx;
|
| + 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 *middleP7ecx;
|
| + SEC_ASN1EncoderContext *middleA1ecx;
|
| + unsigned int currentSafe;
|
| +
|
| + /* hmac context */
|
| + PK11Context *hmacCx;
|
| +
|
| + /* output buffers */
|
| + sec_pkcs12OutputBuffer middleBuf;
|
| + sec_pkcs12OutputBuffer innerBuf;
|
| +
|
| +} 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)
|
| +{
|
| + PLArenaPool *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) ? PK11_ReferenceSlot(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 = NULL;
|
| + 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 = PK11_ReferenceSlot(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);
|
| +
|
| + if (slot) {
|
| + PK11_FreeSlot(slot);
|
| + }
|
| + return safeInfo;
|
| +
|
| +loser:
|
| + if (slot) {
|
| + PK11_FreeSlot(slot);
|
| + }
|
| + 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(PLArenaPool *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(PLArenaPool *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(PLArenaPool *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(PLArenaPool *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 */
|
| + /* XXX
|
| + * This was rather silly. The chain is constructed above
|
| + * by finding all of the CERTCertificate's in the database.
|
| + * Then the chain is put into a CERTCertificateList, which only
|
| + * contains the DER. Finally, the DER was decoded, and the
|
| + * decoded cert was sent recursively back to this function.
|
| + * Beyond being inefficent, this causes data loss (specifically,
|
| + * the nickname). Instead, for 3.4, we'll do a lookup by the
|
| + * DER, which should return the cached entry.
|
| + */
|
| + tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(),
|
| + &certList->certs[count]);
|
| + 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_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 = NULL;
|
| +
|
| + 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();
|
| + } else {
|
| + slot = PK11_ReferenceSlot(p12ctxt->slot);
|
| + }
|
| +
|
| + epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm,
|
| + &uniPwitem, cert,
|
| + NSS_PBE_DEFAULT_ITERATION_COUNT,
|
| + p12ctxt->wincx);
|
| + PK11_FreeSlot(slot);
|
| + if(!epki) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
|
| + goto loser;
|
| + }
|
| +
|
| + 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;
|
| + 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_PKCS12AddCertOrChainAndKey
|
| + * 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
|
| + * includeCertChain - also add certs from chain to bag.
|
| + */
|
| +SECStatus
|
| +SEC_PKCS12AddCertOrChainAndKey(SEC_PKCS12ExportContext *p12ctxt,
|
| + void *certSafe, void *certNestedDest,
|
| + CERTCertificate *cert, CERTCertDBHandle *certDb,
|
| + void *keySafe, void *keyNestedDest,
|
| + PRBool shroudKey, SECItem *pwitem,
|
| + SECOidTag algorithm, PRBool includeCertChain)
|
| +{
|
| + 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, includeCertChain);
|
| + 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;
|
| +}
|
| +
|
| +/* like SEC_PKCS12AddCertOrChainAndKey, but always adds cert chain */
|
| +SECStatus
|
| +SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt,
|
| + void *certSafe, void *certNestedDest,
|
| + CERTCertificate *cert, CERTCertDBHandle *certDb,
|
| + void *keySafe, void *keyNestedDest,
|
| + PRBool shroudKey, SECItem *pwItem, SECOidTag algorithm)
|
| +{
|
| + return SEC_PKCS12AddCertOrChainAndKey(p12ctxt, certSafe, certNestedDest,
|
| + cert, certDb, keySafe, keyNestedDest, shroudKey, pwItem,
|
| + algorithm, PR_TRUE);
|
| +}
|
| +
|
| +
|
| +/* 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
|
| + *********************************/
|
| +
|
| +/* Clean up the resources allocated by a sec_PKCS12EncoderContext. */
|
| +static void
|
| +sec_pkcs12_encoder_destroy_context(sec_PKCS12EncoderContext *p12enc)
|
| +{
|
| + if(p12enc) {
|
| + if(p12enc->outerA1ecx) {
|
| + SEC_ASN1EncoderFinish(p12enc->outerA1ecx);
|
| + p12enc->outerA1ecx = NULL;
|
| + }
|
| + if(p12enc->aSafeCinfo) {
|
| + SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo);
|
| + p12enc->aSafeCinfo = NULL;
|
| + }
|
| + if(p12enc->middleP7ecx) {
|
| + SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12enc->p12exp->pwfn,
|
| + p12enc->p12exp->pwfnarg);
|
| + p12enc->middleP7ecx = NULL;
|
| + }
|
| + if(p12enc->middleA1ecx) {
|
| + SEC_ASN1EncoderFinish(p12enc->middleA1ecx);
|
| + p12enc->middleA1ecx = NULL;
|
| + }
|
| + if(p12enc->hmacCx) {
|
| + PK11_DestroyContext(p12enc->hmacCx, PR_TRUE);
|
| + p12enc->hmacCx = NULL;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/* 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.
|
| + */
|
| +static sec_PKCS12EncoderContext *
|
| +sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp)
|
| +{
|
| + sec_PKCS12EncoderContext *p12enc = NULL;
|
| + unsigned int i, nonEmptyCnt;
|
| + SECStatus rv;
|
| + SECItem ignore = {0};
|
| + void *mark;
|
| +
|
| + 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 */
|
| + mark = PORT_ArenaMark(p12exp->arena);
|
| + p12enc = PORT_ArenaZNew(p12exp->arena, 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) {
|
| + 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};
|
| + SECItem *salt = sec_pkcs12_generate_salt();
|
| + PK11SymKey *symKey;
|
| + SECItem *params;
|
| + CK_MECHANISM_TYPE integrityMechType;
|
| + CK_MECHANISM_TYPE hmacMechType;
|
| +
|
| + /* 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) {
|
| + /* XXX salt is leaked */
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + }
|
| + if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->mac.iter),
|
| + NSS_PBE_DEFAULT_ITERATION_COUNT)) {
|
| + /* XXX salt is leaked */
|
| + goto loser;
|
| + }
|
| +
|
| + /* generate HMAC key */
|
| + if(!sec_pkcs12_convert_item_to_unicode(NULL, &pwd,
|
| + p12exp->integrityInfo.pwdInfo.password, PR_TRUE,
|
| + PR_TRUE, PR_TRUE)) {
|
| + /* XXX salt is leaked */
|
| + goto loser;
|
| + }
|
| + /*
|
| + * This code only works with PKCS #12 Mac using PKCS #5 v1
|
| + * PBA keygens. PKCS #5 v2 support will require a change to
|
| + * the PKCS #12 spec.
|
| + */
|
| + params = PK11_CreatePBEParams(salt, &pwd,
|
| + NSS_PBE_DEFAULT_ITERATION_COUNT);
|
| + SECITEM_ZfreeItem(salt, PR_TRUE);
|
| + SECITEM_ZfreeItem(&pwd, PR_FALSE);
|
| +
|
| + /* get the PBA Mechanism to generate the key */
|
| + switch (p12exp->integrityInfo.pwdInfo.algorithm) {
|
| + case SEC_OID_SHA1:
|
| + integrityMechType = CKM_PBA_SHA1_WITH_SHA1_HMAC; break;
|
| + case SEC_OID_MD5:
|
| + integrityMechType = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN; break;
|
| + case SEC_OID_MD2:
|
| + integrityMechType = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; break;
|
| + default:
|
| + /* XXX params is leaked */
|
| + goto loser;
|
| + }
|
| +
|
| + /* generate the key */
|
| + symKey = PK11_KeyGen(NULL, integrityMechType, params, 20, NULL);
|
| + PK11_DestroyPBEParams(params);
|
| + if(!symKey) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* initialize HMAC */
|
| + /* Get the HMAC mechanism from the hash OID */
|
| + hmacMechType= sec_pkcs12_algtag_to_mech(
|
| + p12exp->integrityInfo.pwdInfo.algorithm);
|
| +
|
| + p12enc->hmacCx = PK11_CreateContextBySymKey( hmacMechType,
|
| + CKA_SIGN, symKey, &ignore);
|
| +
|
| + PK11_FreeSymKey(symKey);
|
| + if(!p12enc->hmacCx) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + }
|
| + rv = PK11_DigestBegin(p12enc->hmacCx);
|
| + if (rv != SECSuccess)
|
| + goto loser;
|
| + }
|
| + }
|
| +
|
| + if(!p12enc->aSafeCinfo) {
|
| + goto loser;
|
| + }
|
| +
|
| + PORT_ArenaUnmark(p12exp->arena, mark);
|
| +
|
| + return p12enc;
|
| +
|
| +loser:
|
| + sec_pkcs12_encoder_destroy_context(p12enc);
|
| + if (p12exp->arena != NULL)
|
| + PORT_ArenaRelease(p12exp->arena, mark);
|
| +
|
| + return NULL;
|
| +}
|
| +
|
| +/* The outermost ASN.1 encoder calls this function for output.
|
| +** This function calls back to the library caller's output routine,
|
| +** which typically writes to a PKCS12 file.
|
| + */
|
| +static void
|
| +sec_P12A1OutputCB_Outer(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);
|
| +}
|
| +
|
| +/* The "middle" and "inner" ASN.1 encoders call this function to output.
|
| +** This function does HMACing, if appropriate, and then buffers the data.
|
| +** The buffered data is eventually passed down to the underlying PKCS7 encoder.
|
| + */
|
| +static void
|
| +sec_P12A1OutputCB_HmacP7Update(void *arg, const char *buf,
|
| + unsigned long len,
|
| + int depth,
|
| + SEC_ASN1EncodingPart data_kind)
|
| +{
|
| + sec_pkcs12OutputBuffer * bufcx = (sec_pkcs12OutputBuffer *)arg;
|
| +
|
| + if(!buf || !len)
|
| + return;
|
| +
|
| + if (bufcx->hmacCx) {
|
| + PK11_DigestOp(bufcx->hmacCx, (unsigned char *)buf, len);
|
| + }
|
| +
|
| + /* buffer */
|
| + if (bufcx->numBytes > 0) {
|
| + int toCopy;
|
| + if (len + bufcx->numBytes <= bufcx->bufBytes) {
|
| + memcpy(bufcx->buf + bufcx->numBytes, buf, len);
|
| + bufcx->numBytes += len;
|
| + if (bufcx->numBytes < bufcx->bufBytes)
|
| + return;
|
| + SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
|
| + bufcx->numBytes = 0;
|
| + return;
|
| + }
|
| + toCopy = bufcx->bufBytes - bufcx->numBytes;
|
| + memcpy(bufcx->buf + bufcx->numBytes, buf, toCopy);
|
| + SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
|
| + bufcx->numBytes = 0;
|
| + len -= toCopy;
|
| + buf += toCopy;
|
| + }
|
| + /* buffer is presently empty */
|
| + if (len >= bufcx->bufBytes) {
|
| + /* Just pass it through */
|
| + SEC_PKCS7EncoderUpdate(bufcx->p7eCx, buf, len);
|
| + } else {
|
| + /* copy it all into the buffer, and return */
|
| + memcpy(bufcx->buf, buf, len);
|
| + bufcx->numBytes = len;
|
| + }
|
| +}
|
| +
|
| +void
|
| +sec_FlushPkcs12OutputBuffer( sec_pkcs12OutputBuffer * bufcx)
|
| +{
|
| + if (bufcx->numBytes > 0) {
|
| + SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->numBytes);
|
| + bufcx->numBytes = 0;
|
| + }
|
| +}
|
| +
|
| +/* Feeds the output of a PKCS7 encoder into the next outward ASN.1 encoder.
|
| +** This function is used by both the inner and middle PCS7 encoders.
|
| +*/
|
| +static void
|
| +sec_P12P7OutputCB_CallA1Update(void *arg, const char *buf, unsigned long len)
|
| +{
|
| + SEC_ASN1EncoderContext *cx = (SEC_ASN1EncoderContext*)arg;
|
| +
|
| + if (!buf || !len)
|
| + return;
|
| +
|
| + SEC_ASN1EncoderUpdate(cx, buf, len);
|
| +}
|
| +
|
| +
|
| +/* 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)
|
| +{
|
| + SEC_PKCS7EncoderContext *innerP7ecx;
|
| + SEC_PKCS7ContentInfo *cinfo;
|
| + PK11SymKey *bulkKey = NULL;
|
| + SEC_ASN1EncoderContext *innerA1ecx = NULL;
|
| + SECStatus rv = SECSuccess;
|
| +
|
| + 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:
|
| + case SEC_OID_PKCS7_ENVELOPED_DATA:
|
| + break;
|
| + case SEC_OID_PKCS7_ENCRYPTED_DATA:
|
| + bulkKey = safeInfo->encryptionKey;
|
| + PK11_SetSymKeyUserData(bulkKey, &safeInfo->pwitem, NULL);
|
| + break;
|
| + default:
|
| + return SECFailure;
|
| +
|
| + }
|
| +
|
| + /* start the PKCS7 encoder */
|
| + innerP7ecx = SEC_PKCS7EncoderStart(cinfo,
|
| + sec_P12P7OutputCB_CallA1Update,
|
| + p12ecx->middleA1ecx, bulkKey);
|
| + if(!innerP7ecx) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* encode safe contents */
|
| + p12ecx->innerBuf.p7eCx = innerP7ecx;
|
| + p12ecx->innerBuf.hmacCx = NULL;
|
| + p12ecx->innerBuf.numBytes = 0;
|
| + p12ecx->innerBuf.bufBytes = sizeof p12ecx->innerBuf.buf;
|
| +
|
| + innerA1ecx = SEC_ASN1EncoderStart(safeInfo->safe,
|
| + sec_PKCS12SafeContentsTemplate,
|
| + sec_P12A1OutputCB_HmacP7Update,
|
| + &p12ecx->innerBuf);
|
| + if(!innerA1ecx) {
|
| + goto loser;
|
| + }
|
| + rv = SEC_ASN1EncoderUpdate(innerA1ecx, NULL, 0);
|
| + SEC_ASN1EncoderFinish(innerA1ecx);
|
| + sec_FlushPkcs12OutputBuffer( &p12ecx->innerBuf);
|
| + innerA1ecx = NULL;
|
| + if(rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| +
|
| + /* finish up safe content info */
|
| + rv = SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn,
|
| + p12ecx->p12exp->pwfnarg);
|
| + }
|
| + memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + if(innerP7ecx) {
|
| + SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn,
|
| + p12ecx->p12exp->pwfnarg);
|
| + }
|
| +
|
| + if(innerA1ecx) {
|
| + SEC_ASN1EncoderFinish(innerA1ecx);
|
| + }
|
| + memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* finish the HMAC and encode the macData so that it can be
|
| + * encoded.
|
| + */
|
| +static SECStatus
|
| +sec_Pkcs12FinishMac(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 = PK11_DigestFinal(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);
|
| + }
|
| + PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE);
|
| + p12ecx->hmacCx = NULL;
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +/* 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->outerA1ecx);
|
| + SEC_ASN1EncoderSetStreaming(p12ecx->outerA1ecx);
|
| + SEC_ASN1EncoderClearNotifyProc(p12ecx->outerA1ecx);
|
| +}
|
| +
|
| +/* 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, the "outer" encoder. Set it for streaming */
|
| + p12enc->outerA1ecx = SEC_ASN1EncoderStart(&p12enc->pfx,
|
| + sec_PKCS12PFXItemTemplate,
|
| + sec_P12A1OutputCB_Outer,
|
| + &outInfo);
|
| + if(!p12enc->outerA1ecx) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| + SEC_ASN1EncoderSetStreaming(p12enc->outerA1ecx);
|
| + SEC_ASN1EncoderSetNotifyProc(p12enc->outerA1ecx,
|
| + sec_pkcs12_encoder_pfx_notify, p12enc);
|
| + rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
|
| + if(rv != SECSuccess) {
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| +
|
| + /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */
|
| + p12enc->middleP7ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo,
|
| + sec_P12P7OutputCB_CallA1Update,
|
| + p12enc->outerA1ecx, NULL);
|
| + if(!p12enc->middleP7ecx) {
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| +
|
| + /* encode asafe */
|
| + p12enc->middleBuf.p7eCx = p12enc->middleP7ecx;
|
| + p12enc->middleBuf.hmacCx = NULL;
|
| + p12enc->middleBuf.numBytes = 0;
|
| + p12enc->middleBuf.bufBytes = sizeof p12enc->middleBuf.buf;
|
| +
|
| + /* Setup the "inner ASN.1 encoder for Authenticated Safes. */
|
| + if(p12enc->p12exp->integrityEnabled &&
|
| + p12enc->p12exp->pwdIntegrity) {
|
| + p12enc->middleBuf.hmacCx = p12enc->hmacCx;
|
| + }
|
| + p12enc->middleA1ecx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe,
|
| + sec_PKCS12AuthenticatedSafeTemplate,
|
| + sec_P12A1OutputCB_HmacP7Update,
|
| + &p12enc->middleBuf);
|
| + if(!p12enc->middleA1ecx) {
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| + SEC_ASN1EncoderSetStreaming(p12enc->middleA1ecx);
|
| + SEC_ASN1EncoderSetTakeFromBuf(p12enc->middleA1ecx);
|
| +
|
| + /* encode each of the safes */
|
| + while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) {
|
| + sec_pkcs12_encoder_asafe_process(p12enc);
|
| + p12enc->currentSafe++;
|
| + }
|
| + SEC_ASN1EncoderClearTakeFromBuf(p12enc->middleA1ecx);
|
| + SEC_ASN1EncoderClearStreaming(p12enc->middleA1ecx);
|
| + SEC_ASN1EncoderUpdate(p12enc->middleA1ecx, NULL, 0);
|
| + SEC_ASN1EncoderFinish(p12enc->middleA1ecx);
|
| + p12enc->middleA1ecx = NULL;
|
| +
|
| + sec_FlushPkcs12OutputBuffer( &p12enc->middleBuf);
|
| +
|
| + /* finish the encoding of the authenticated safes */
|
| + rv = SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12exp->pwfn,
|
| + p12exp->pwfnarg);
|
| + p12enc->middleP7ecx = NULL;
|
| + if(rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + SEC_ASN1EncoderClearTakeFromBuf(p12enc->outerA1ecx);
|
| + SEC_ASN1EncoderClearStreaming(p12enc->outerA1ecx);
|
| +
|
| + /* update the mac, if necessary */
|
| + rv = sec_Pkcs12FinishMac(p12enc);
|
| + if(rv != SECSuccess) {
|
| + goto loser;
|
| + }
|
| +
|
| + /* finish encoding the pfx */
|
| + rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
|
| +
|
| + SEC_ASN1EncoderFinish(p12enc->outerA1ecx);
|
| + p12enc->outerA1ecx = NULL;
|
| +
|
| +loser:
|
| + sec_pkcs12_encoder_destroy_context(p12enc);
|
| + 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++;
|
| + }
|
| + }
|
| +
|
| + PK11_FreeSlot(p12ecx->slot);
|
| +
|
| + PORT_FreeArena(p12ecx->arena, PR_TRUE);
|
| +}
|
|
|