| Index: nss_pkcs12/p12exp.c
|
| ===================================================================
|
| --- nss_pkcs12/p12exp.c (revision 0)
|
| +++ nss_pkcs12/p12exp.c (revision 0)
|
| @@ -0,0 +1,1378 @@
|
| +/* 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 "plarena.h"
|
| +#include "secitem.h"
|
| +#include "secoid.h"
|
| +#include "seccomon.h"
|
| +#include "secport.h"
|
| +#include "cert.h"
|
| +#include "pkcs12.h"
|
| +#include "p12local.h"
|
| +#include "secpkcs7.h"
|
| +#include "secasn1.h"
|
| +#include "secerr.h"
|
| +#include "p12plcy.h"
|
| +
|
| +/* release the memory taken up by the list of nicknames */
|
| +static void
|
| +sec_pkcs12_destroy_nickname_list(SECItem **nicknames)
|
| +{
|
| + int i = 0;
|
| +
|
| + if(nicknames == NULL) {
|
| + return;
|
| + }
|
| +
|
| + while(nicknames[i] != NULL) {
|
| + SECITEM_FreeItem(nicknames[i], PR_FALSE);
|
| + i++;
|
| + }
|
| +
|
| + PORT_Free(nicknames);
|
| +}
|
| +
|
| +/* release the memory taken up by the list of certificates */
|
| +static void
|
| +sec_pkcs12_destroy_certificate_list(CERTCertificate **ref_certs)
|
| +{
|
| + int i = 0;
|
| +
|
| + if(ref_certs == NULL) {
|
| + return;
|
| + }
|
| +
|
| + while(ref_certs[i] != NULL) {
|
| + CERT_DestroyCertificate(ref_certs[i]);
|
| + i++;
|
| + }
|
| +}
|
| +
|
| +static void
|
| +sec_pkcs12_destroy_cinfos_for_cert_bags(SEC_PKCS12CertAndCRLBag *certBag)
|
| +{
|
| + int j = 0;
|
| + j = 0;
|
| + while(certBag->certAndCRLs[j] != NULL) {
|
| + SECOidTag certType = SECOID_FindOIDTag(&certBag->certAndCRLs[j]->BagID);
|
| + if(certType == SEC_OID_PKCS12_X509_CERT_CRL_BAG) {
|
| + SEC_PKCS12X509CertCRL *x509;
|
| + x509 = certBag->certAndCRLs[j]->value.x509;
|
| + SEC_PKCS7DestroyContentInfo(&x509->certOrCRL);
|
| + }
|
| + j++;
|
| + }
|
| +}
|
| +
|
| +/* destroy all content infos since they were not allocated in common
|
| + * pool
|
| + */
|
| +static void
|
| +sec_pkcs12_destroy_cert_content_infos(SEC_PKCS12SafeContents *safe,
|
| + SEC_PKCS12Baggage *baggage)
|
| +{
|
| + int i, j;
|
| +
|
| + if((safe != NULL) && (safe->contents != NULL)) {
|
| + i = 0;
|
| + while(safe->contents[i] != NULL) {
|
| + SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType);
|
| + if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) {
|
| + SEC_PKCS12CertAndCRLBag *certBag;
|
| + certBag = safe->contents[i]->safeContent.certAndCRLBag;
|
| + sec_pkcs12_destroy_cinfos_for_cert_bags(certBag);
|
| + }
|
| + i++;
|
| + }
|
| + }
|
| +
|
| + if((baggage != NULL) && (baggage->bags != NULL)) {
|
| + i = 0;
|
| + while(baggage->bags[i] != NULL) {
|
| + if(baggage->bags[i]->unencSecrets != NULL) {
|
| + j = 0;
|
| + while(baggage->bags[i]->unencSecrets[j] != NULL) {
|
| + SECOidTag bagType;
|
| + bagType = SECOID_FindOIDTag(&baggage->bags[i]->unencSecrets[j]->safeBagType);
|
| + if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) {
|
| + SEC_PKCS12CertAndCRLBag *certBag;
|
| + certBag = baggage->bags[i]->unencSecrets[j]->safeContent.certAndCRLBag;
|
| + sec_pkcs12_destroy_cinfos_for_cert_bags(certBag);
|
| + }
|
| + j++;
|
| + }
|
| + }
|
| + i++;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/* convert the nickname list from a NULL termincated Char list
|
| + * to a NULL terminated SECItem list
|
| + */
|
| +static SECItem **
|
| +sec_pkcs12_convert_nickname_list(char **nicknames)
|
| +{
|
| + SECItem **nicks;
|
| + int i, j;
|
| + PRBool error = PR_FALSE;
|
| +
|
| + if(nicknames == NULL) {
|
| + return NULL;
|
| + }
|
| +
|
| + i = j = 0;
|
| + while(nicknames[i] != NULL) {
|
| + i++;
|
| + }
|
| +
|
| + /* allocate the space and copy the data */
|
| + nicks = (SECItem **)PORT_ZAlloc(sizeof(SECItem *) * (i + 1));
|
| + if(nicks != NULL) {
|
| + for(j = 0; ((j < i) && (error == PR_FALSE)); j++) {
|
| + nicks[j] = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
|
| + if(nicks[j] != NULL) {
|
| + nicks[j]->data =
|
| + (unsigned char *)PORT_ZAlloc(PORT_Strlen(nicknames[j])+1);
|
| + if(nicks[j]->data != NULL) {
|
| + nicks[j]->len = PORT_Strlen(nicknames[j]);
|
| + PORT_Memcpy(nicks[j]->data, nicknames[j], nicks[j]->len);
|
| + nicks[j]->data[nicks[j]->len] = 0;
|
| + } else {
|
| + error = PR_TRUE;
|
| + }
|
| + } else {
|
| + error = PR_TRUE;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if(error == PR_TRUE) {
|
| + for(i = 0; i < j; i++) {
|
| + SECITEM_FreeItem(nicks[i], PR_TRUE);
|
| + }
|
| + PORT_Free(nicks);
|
| + nicks = NULL;
|
| + }
|
| +
|
| + return nicks;
|
| +}
|
| +
|
| +/* package the certificate add_cert into PKCS12 structures,
|
| + * retrieve the certificate chain for the cert and return
|
| + * the packaged contents.
|
| + * poolp -- common memory pool;
|
| + * add_cert -- certificate to package up
|
| + * nickname for the certificate
|
| + * a return of NULL indicates an error
|
| + */
|
| +static SEC_PKCS12CertAndCRL *
|
| +sec_pkcs12_get_cert(PLArenaPool *poolp,
|
| + CERTCertificate *add_cert,
|
| + SECItem *nickname)
|
| +{
|
| + SEC_PKCS12CertAndCRL *cert;
|
| + SEC_PKCS7ContentInfo *cinfo;
|
| + SGNDigestInfo *t_di;
|
| + void *mark;
|
| + SECStatus rv;
|
| +
|
| + if((poolp == NULL) || (add_cert == NULL) || (nickname == NULL)) {
|
| + return NULL;
|
| + }
|
| + mark = PORT_ArenaMark(poolp);
|
| +
|
| + cert = sec_pkcs12_new_cert_crl(poolp, SEC_OID_PKCS12_X509_CERT_CRL_BAG);
|
| + if(cert != NULL) {
|
| +
|
| + /* copy the nickname */
|
| + rv = SECITEM_CopyItem(poolp, &cert->nickname, nickname);
|
| + if(rv != SECSuccess) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + cert = NULL;
|
| + } else {
|
| +
|
| + /* package the certificate and cert chain into a NULL signer
|
| + * PKCS 7 SignedData content Info and prepare it for encoding
|
| + * since we cannot use DER_ANY_TEMPLATE
|
| + */
|
| + cinfo = SEC_PKCS7CreateCertsOnly(add_cert, PR_TRUE, NULL);
|
| + rv = SEC_PKCS7PrepareForEncode(cinfo, NULL, NULL, NULL);
|
| +
|
| + /* thumbprint the certificate */
|
| + if((cinfo != NULL) && (rv == SECSuccess))
|
| + {
|
| + PORT_Memcpy(&cert->value.x509->certOrCRL, cinfo, sizeof(*cinfo));
|
| + t_di = sec_pkcs12_compute_thumbprint(&add_cert->derCert);
|
| + if(t_di != NULL)
|
| + {
|
| + /* test */
|
| + rv = SGN_CopyDigestInfo(poolp, &cert->value.x509->thumbprint,
|
| + t_di);
|
| + if(rv != SECSuccess) {
|
| + cert = NULL;
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + }
|
| + SGN_DestroyDigestInfo(t_di);
|
| + }
|
| + else
|
| + cert = NULL;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (cert == NULL) {
|
| + PORT_ArenaRelease(poolp, mark);
|
| + } else {
|
| + PORT_ArenaUnmark(poolp, mark);
|
| + }
|
| +
|
| + return cert;
|
| +}
|
| +
|
| +/* package the private key associated with the certificate and
|
| + * return the appropriate PKCS 12 structure
|
| + * poolp common memory pool
|
| + * nickname key nickname
|
| + * cert -- cert to look up
|
| + * wincx -- window handle
|
| + * an error is indicated by a return of NULL
|
| + */
|
| +static SEC_PKCS12PrivateKey *
|
| +sec_pkcs12_get_private_key(PLArenaPool *poolp,
|
| + SECItem *nickname,
|
| + CERTCertificate *cert,
|
| + void *wincx)
|
| +{
|
| + SECKEYPrivateKeyInfo *pki;
|
| + SEC_PKCS12PrivateKey *pk;
|
| + SECStatus rv;
|
| + void *mark;
|
| +
|
| + if((poolp == NULL) || (nickname == NULL)) {
|
| + return NULL;
|
| + }
|
| +
|
| + mark = PORT_ArenaMark(poolp);
|
| +
|
| + /* retrieve key from the data base */
|
| + pki = PK11_ExportPrivateKeyInfo(nickname, cert, wincx);
|
| + if(pki == NULL) {
|
| + PORT_ArenaRelease(poolp, mark);
|
| + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
|
| + return NULL;
|
| + }
|
| +
|
| + pk = (SEC_PKCS12PrivateKey *)PORT_ArenaZAlloc(poolp,
|
| + sizeof(SEC_PKCS12PrivateKey));
|
| + if(pk != NULL) {
|
| + rv = sec_pkcs12_init_pvk_data(poolp, &pk->pvkData);
|
| +
|
| + if(rv == SECSuccess) {
|
| + /* copy the key into poolp memory space */
|
| + rv = SECKEY_CopyPrivateKeyInfo(poolp, &pk->pkcs8data, pki);
|
| + if(rv == SECSuccess) {
|
| + rv = SECITEM_CopyItem(poolp, &pk->pvkData.nickname, nickname);
|
| + }
|
| + }
|
| +
|
| + if(rv != SECSuccess) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + pk = NULL;
|
| + }
|
| + } else {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + }
|
| +
|
| + /* destroy private key, zeroing out data */
|
| + SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
|
| + if (pk == NULL) {
|
| + PORT_ArenaRelease(poolp, mark);
|
| + } else {
|
| + PORT_ArenaUnmark(poolp, mark);
|
| + }
|
| +
|
| + return pk;
|
| +}
|
| +
|
| +/* get a shrouded key item associated with a certificate
|
| + * return the appropriate PKCS 12 structure
|
| + * poolp common memory pool
|
| + * nickname key nickname
|
| + * cert -- cert to look up
|
| + * wincx -- window handle
|
| + * an error is indicated by a return of NULL
|
| + */
|
| +static SEC_PKCS12ESPVKItem *
|
| +sec_pkcs12_get_shrouded_key(PLArenaPool *poolp,
|
| + SECItem *nickname,
|
| + CERTCertificate *cert,
|
| + SECOidTag algorithm,
|
| + SECItem *pwitem,
|
| + PKCS12UnicodeConvertFunction unicodeFn,
|
| + void *wincx)
|
| +{
|
| + SECKEYEncryptedPrivateKeyInfo *epki;
|
| + SEC_PKCS12ESPVKItem *pk;
|
| + void *mark;
|
| + SECStatus rv;
|
| + PK11SlotInfo *slot = NULL;
|
| + PRBool swapUnicodeBytes = PR_FALSE;
|
| +
|
| +#ifdef IS_LITTLE_ENDIAN
|
| + swapUnicodeBytes = PR_TRUE;
|
| +#endif
|
| +
|
| + if((poolp == NULL) || (nickname == NULL))
|
| + return NULL;
|
| +
|
| + mark = PORT_ArenaMark(poolp);
|
| +
|
| + /* use internal key slot */
|
| + slot = PK11_GetInternalKeySlot();
|
| +
|
| + /* retrieve encrypted prviate key */
|
| + epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, pwitem,
|
| + nickname, cert, 1, 0, NULL);
|
| + PK11_FreeSlot(slot);
|
| + if(epki == NULL) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
|
| + PORT_ArenaRelease(poolp, mark);
|
| + return NULL;
|
| + }
|
| +
|
| + /* create a private key and store the data into the poolp memory space */
|
| + pk = sec_pkcs12_create_espvk(poolp, SEC_OID_PKCS12_PKCS8_KEY_SHROUDING);
|
| + if(pk != NULL) {
|
| + rv = sec_pkcs12_init_pvk_data(poolp, &pk->espvkData);
|
| + rv = SECITEM_CopyItem(poolp, &pk->espvkData.nickname, nickname);
|
| + pk->espvkCipherText.pkcs8KeyShroud =
|
| + (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp,
|
| + sizeof(SECKEYEncryptedPrivateKeyInfo));
|
| + if((pk->espvkCipherText.pkcs8KeyShroud != NULL) && (rv == SECSuccess)) {
|
| + rv = SECKEY_CopyEncryptedPrivateKeyInfo(poolp,
|
| + pk->espvkCipherText.pkcs8KeyShroud, epki);
|
| + if(rv == SECSuccess) {
|
| + rv = (*unicodeFn)(poolp, &pk->espvkData.uniNickName, nickname,
|
| + PR_TRUE, swapUnicodeBytes);
|
| + }
|
| + }
|
| +
|
| + if(rv != SECSuccess) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + pk = NULL;
|
| + }
|
| + }
|
| +
|
| + SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
|
| + if(pk == NULL) {
|
| + PORT_ArenaRelease(poolp, mark);
|
| + } else {
|
| + PORT_ArenaUnmark(poolp, mark);
|
| + }
|
| +
|
| + return pk;
|
| +}
|
| +
|
| +/* add a thumbprint to a private key associated certs list
|
| + * pvk is the area where the list is stored
|
| + * thumb is the thumbprint to copy
|
| + * a return of SECFailure indicates an error
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_add_thumbprint(SEC_PKCS12PVKSupportingData *pvk,
|
| + SGNDigestInfo *thumb)
|
| +{
|
| + SGNDigestInfo **thumb_list = NULL;
|
| + int nthumbs, size;
|
| + void *mark, *dummy;
|
| + SECStatus rv = SECFailure;
|
| +
|
| + if((pvk == NULL) || (thumb == NULL)) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + mark = PORT_ArenaMark(pvk->poolp);
|
| +
|
| + thumb_list = pvk->assocCerts;
|
| + nthumbs = pvk->nThumbs;
|
| +
|
| + /* allocate list space needed -- either growing or allocating
|
| + * list must be NULL terminated
|
| + */
|
| + size = sizeof(SGNDigestInfo *);
|
| + dummy = PORT_ArenaGrow(pvk->poolp, thumb_list, (size * (nthumbs + 1)),
|
| + (size * (nthumbs + 2)));
|
| + thumb_list = dummy;
|
| + if(dummy != NULL) {
|
| + thumb_list[nthumbs] = (SGNDigestInfo *)PORT_ArenaZAlloc(pvk->poolp,
|
| + sizeof(SGNDigestInfo));
|
| + if(thumb_list[nthumbs] != NULL) {
|
| + SGN_CopyDigestInfo(pvk->poolp, thumb_list[nthumbs], thumb);
|
| + nthumbs += 1;
|
| + thumb_list[nthumbs] = 0;
|
| + } else {
|
| + dummy = NULL;
|
| + }
|
| + }
|
| +
|
| + if(dummy == NULL) {
|
| + PORT_ArenaRelease(pvk->poolp, mark);
|
| + return SECFailure;
|
| + }
|
| +
|
| + pvk->assocCerts = thumb_list;
|
| + pvk->nThumbs = nthumbs;
|
| +
|
| + PORT_ArenaUnmark(pvk->poolp, mark);
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* search the list of shrouded keys in the baggage for the desired
|
| + * name. return a pointer to the item. a return of NULL indicates
|
| + * that no match was present or that an error occurred.
|
| + */
|
| +static SEC_PKCS12ESPVKItem *
|
| +sec_pkcs12_get_espvk_by_name(SEC_PKCS12Baggage *luggage,
|
| + SECItem *name)
|
| +{
|
| + PRBool found = PR_FALSE;
|
| + SEC_PKCS12ESPVKItem *espvk = NULL;
|
| + int i, j;
|
| + SECComparison rv = SECEqual;
|
| + SECItem *t_name;
|
| + SEC_PKCS12BaggageItem *bag;
|
| +
|
| + if((luggage == NULL) || (name == NULL)) {
|
| + return NULL;
|
| + }
|
| +
|
| + i = 0;
|
| + while((found == PR_FALSE) && (i < luggage->luggage_size)) {
|
| + j = 0;
|
| + bag = luggage->bags[i];
|
| + while((found == PR_FALSE) && (j < bag->nEspvks)) {
|
| + espvk = bag->espvks[j];
|
| + if(espvk->poolp == NULL) {
|
| + espvk->poolp = luggage->poolp;
|
| + }
|
| + t_name = SECITEM_DupItem(&espvk->espvkData.nickname);
|
| + if(t_name != NULL) {
|
| + rv = SECITEM_CompareItem(name, t_name);
|
| + if(rv == SECEqual) {
|
| + found = PR_TRUE;
|
| + }
|
| + SECITEM_FreeItem(t_name, PR_TRUE);
|
| + } else {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return NULL;
|
| + }
|
| + j++;
|
| + }
|
| + i++;
|
| + }
|
| +
|
| + if(found != PR_TRUE) {
|
| + PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME);
|
| + return NULL;
|
| + }
|
| +
|
| + return espvk;
|
| +}
|
| +
|
| +/* locates a certificate and copies the thumbprint to the
|
| + * appropriate private key
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_propagate_thumbprints(SECItem **nicknames,
|
| + CERTCertificate **ref_certs,
|
| + SEC_PKCS12SafeContents *safe,
|
| + SEC_PKCS12Baggage *baggage)
|
| +{
|
| + SEC_PKCS12CertAndCRL *cert;
|
| + SEC_PKCS12PrivateKey *key;
|
| + SEC_PKCS12ESPVKItem *espvk;
|
| + int i;
|
| + PRBool error = PR_FALSE;
|
| + SECStatus rv = SECFailure;
|
| +
|
| + if((nicknames == NULL) || (safe == NULL)) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + i = 0;
|
| + while((nicknames[i] != NULL) && (error == PR_FALSE)) {
|
| + /* process all certs */
|
| + cert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage,
|
| + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID,
|
| + nicknames[i], NULL);
|
| + if(cert != NULL) {
|
| + /* locate key and copy thumbprint */
|
| + key = (SEC_PKCS12PrivateKey *)sec_pkcs12_find_object(safe, baggage,
|
| + SEC_OID_PKCS12_KEY_BAG_ID,
|
| + nicknames[i], NULL);
|
| + if(key != NULL) {
|
| + key->pvkData.poolp = key->poolp;
|
| + rv = sec_pkcs12_add_thumbprint(&key->pvkData,
|
| + &cert->value.x509->thumbprint);
|
| + if(rv == SECFailure)
|
| + error = PR_TRUE; /* XXX Set error? */
|
| + }
|
| +
|
| + /* look in the baggage as well...*/
|
| + if((baggage != NULL) && (error == PR_FALSE)) {
|
| + espvk = sec_pkcs12_get_espvk_by_name(baggage, nicknames[i]);
|
| + if(espvk != NULL) {
|
| + espvk->espvkData.poolp = espvk->poolp;
|
| + rv = sec_pkcs12_add_thumbprint(&espvk->espvkData,
|
| + &cert->value.x509->thumbprint);
|
| + if(rv == SECFailure)
|
| + error = PR_TRUE; /* XXX Set error? */
|
| + }
|
| + }
|
| + }
|
| + i++;
|
| + }
|
| +
|
| + if(error == PR_TRUE) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + return SECSuccess;
|
| +}
|
| +
|
| +/* append a safe bag to the end of the safe contents list */
|
| +SECStatus
|
| +sec_pkcs12_append_safe_bag(SEC_PKCS12SafeContents *safe,
|
| + SEC_PKCS12SafeBag *bag)
|
| +{
|
| + int size;
|
| + void *mark = NULL, *dummy = NULL;
|
| +
|
| + if((bag == NULL) || (safe == NULL))
|
| + return SECFailure;
|
| +
|
| + mark = PORT_ArenaMark(safe->poolp);
|
| +
|
| + size = (safe->safe_size * sizeof(SEC_PKCS12SafeBag *));
|
| +
|
| + if(safe->safe_size > 0) {
|
| + dummy = (SEC_PKCS12SafeBag **)PORT_ArenaGrow(safe->poolp,
|
| + safe->contents,
|
| + size,
|
| + (size + sizeof(SEC_PKCS12SafeBag *)));
|
| + safe->contents = dummy;
|
| + } else {
|
| + safe->contents = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(safe->poolp,
|
| + (2 * sizeof(SEC_PKCS12SafeBag *)));
|
| + dummy = safe->contents;
|
| + }
|
| +
|
| + if(dummy == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + }
|
| +
|
| + safe->contents[safe->safe_size] = bag;
|
| + safe->safe_size++;
|
| + safe->contents[safe->safe_size] = NULL;
|
| +
|
| + PORT_ArenaUnmark(safe->poolp, mark);
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + PORT_ArenaRelease(safe->poolp, mark);
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* append a certificate onto the end of a cert bag */
|
| +static SECStatus
|
| +sec_pkcs12_append_cert_to_bag(PLArenaPool *arena,
|
| + SEC_PKCS12SafeBag *safebag,
|
| + CERTCertificate *cert,
|
| + SECItem *nickname)
|
| +{
|
| + int size;
|
| + void *dummy = NULL, *mark = NULL;
|
| + SEC_PKCS12CertAndCRL *p12cert;
|
| + SEC_PKCS12CertAndCRLBag *bag;
|
| +
|
| + if((arena == NULL) || (safebag == NULL) ||
|
| + (cert == NULL) || (nickname == NULL)) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + bag = safebag->safeContent.certAndCRLBag;
|
| + if(bag == NULL) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + mark = PORT_ArenaMark(arena);
|
| +
|
| + p12cert = sec_pkcs12_get_cert(arena, cert, nickname);
|
| + if(p12cert == NULL) {
|
| + PORT_ArenaRelease(bag->poolp, mark);
|
| + return SECFailure;
|
| + }
|
| +
|
| + size = bag->bag_size * sizeof(SEC_PKCS12CertAndCRL *);
|
| + if(bag->bag_size > 0) {
|
| + dummy = (SEC_PKCS12CertAndCRL **)PORT_ArenaGrow(bag->poolp,
|
| + bag->certAndCRLs, size, size + sizeof(SEC_PKCS12CertAndCRL *));
|
| + bag->certAndCRLs = dummy;
|
| + } else {
|
| + bag->certAndCRLs = (SEC_PKCS12CertAndCRL **)PORT_ArenaZAlloc(bag->poolp,
|
| + (2 * sizeof(SEC_PKCS12CertAndCRL *)));
|
| + dummy = bag->certAndCRLs;
|
| + }
|
| +
|
| + if(dummy == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + }
|
| +
|
| + bag->certAndCRLs[bag->bag_size] = p12cert;
|
| + bag->bag_size++;
|
| + bag->certAndCRLs[bag->bag_size] = NULL;
|
| +
|
| + PORT_ArenaUnmark(bag->poolp, mark);
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + PORT_ArenaRelease(bag->poolp, mark);
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* append a key onto the end of a list of keys in a key bag */
|
| +SECStatus
|
| +sec_pkcs12_append_key_to_bag(SEC_PKCS12SafeBag *safebag,
|
| + SEC_PKCS12PrivateKey *pk)
|
| +{
|
| + void *mark, *dummy;
|
| + SEC_PKCS12PrivateKeyBag *bag;
|
| + int size;
|
| +
|
| + if((safebag == NULL) || (pk == NULL))
|
| + return SECFailure;
|
| +
|
| + bag = safebag->safeContent.keyBag;
|
| + if(bag == NULL) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + mark = PORT_ArenaMark(bag->poolp);
|
| +
|
| + size = (bag->bag_size * sizeof(SEC_PKCS12PrivateKey *));
|
| +
|
| + if(bag->bag_size > 0) {
|
| + dummy = (SEC_PKCS12PrivateKey **)PORT_ArenaGrow(bag->poolp,
|
| + bag->privateKeys,
|
| + size,
|
| + size + sizeof(SEC_PKCS12PrivateKey *));
|
| + bag->privateKeys = dummy;
|
| + } else {
|
| + bag->privateKeys = (SEC_PKCS12PrivateKey **)PORT_ArenaZAlloc(bag->poolp,
|
| + (2 * sizeof(SEC_PKCS12PrivateKey *)));
|
| + dummy = bag->privateKeys;
|
| + }
|
| +
|
| + if(dummy == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + }
|
| +
|
| + bag->privateKeys[bag->bag_size] = pk;
|
| + bag->bag_size++;
|
| + bag->privateKeys[bag->bag_size] = NULL;
|
| +
|
| + PORT_ArenaUnmark(bag->poolp, mark);
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + /* XXX Free memory? */
|
| + PORT_ArenaRelease(bag->poolp, mark);
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* append a safe bag to the baggage area */
|
| +static SECStatus
|
| +sec_pkcs12_append_unshrouded_bag(SEC_PKCS12BaggageItem *bag,
|
| + SEC_PKCS12SafeBag *u_bag)
|
| +{
|
| + int size;
|
| + void *mark = NULL, *dummy = NULL;
|
| +
|
| + if((bag == NULL) || (u_bag == NULL))
|
| + return SECFailure;
|
| +
|
| + mark = PORT_ArenaMark(bag->poolp);
|
| +
|
| + /* dump things into the first bag */
|
| + size = (bag->nSecrets + 1) * sizeof(SEC_PKCS12SafeBag *);
|
| + dummy = PORT_ArenaGrow(bag->poolp,
|
| + bag->unencSecrets, size,
|
| + size + sizeof(SEC_PKCS12SafeBag *));
|
| + bag->unencSecrets = dummy;
|
| + if(dummy == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + }
|
| +
|
| + bag->unencSecrets[bag->nSecrets] = u_bag;
|
| + bag->nSecrets++;
|
| + bag->unencSecrets[bag->nSecrets] = NULL;
|
| +
|
| + PORT_ArenaUnmark(bag->poolp, mark);
|
| + return SECSuccess;
|
| +
|
| +loser:
|
| + PORT_ArenaRelease(bag->poolp, mark);
|
| + return SECFailure;
|
| +}
|
| +
|
| +/* gather up all certificates and keys and package them up
|
| + * in the safe, baggage, or both.
|
| + * nicknames is the list of nicknames and corresponding certs in ref_certs
|
| + * ref_certs a null terminated list of certificates
|
| + * rSafe, rBaggage -- return areas for safe and baggage
|
| + * shroud_keys -- store keys externally
|
| + * pwitem -- password for computing integrity mac and encrypting contents
|
| + * wincx -- window handle
|
| + *
|
| + * if a failure occurs, an error is set and SECFailure returned.
|
| + */
|
| +static SECStatus
|
| +sec_pkcs12_package_certs_and_keys(SECItem **nicknames,
|
| + CERTCertificate **ref_certs,
|
| + PRBool unencryptedCerts,
|
| + SEC_PKCS12SafeContents **rSafe,
|
| + SEC_PKCS12Baggage **rBaggage,
|
| + PRBool shroud_keys,
|
| + SECOidTag shroud_alg,
|
| + SECItem *pwitem,
|
| + PKCS12UnicodeConvertFunction unicodeFn,
|
| + void *wincx)
|
| +{
|
| + PLArenaPool *permArena;
|
| + SEC_PKCS12SafeContents *safe = NULL;
|
| + SEC_PKCS12Baggage *baggage = NULL;
|
| +
|
| + SECStatus rv = SECFailure;
|
| + PRBool problem = PR_FALSE;
|
| +
|
| + SEC_PKCS12ESPVKItem *espvk = NULL;
|
| + SEC_PKCS12PrivateKey *pk = NULL;
|
| + CERTCertificate *add_cert = NULL;
|
| + SEC_PKCS12SafeBag *certbag = NULL, *keybag = NULL;
|
| + SEC_PKCS12BaggageItem *external_bag = NULL;
|
| + int ncerts = 0, nkeys = 0;
|
| + int i;
|
| +
|
| + if((nicknames == NULL) || (rSafe == NULL) || (rBaggage == NULL)) {
|
| + return SECFailure;
|
| + }
|
| +
|
| + *rBaggage = baggage;
|
| + *rSafe = safe;
|
| +
|
| + permArena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
|
| + if(permArena == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return SECFailure;
|
| + }
|
| +
|
| + /* allocate structures */
|
| + safe = sec_pkcs12_create_safe_contents(permArena);
|
| + if(safe == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| +
|
| + certbag = sec_pkcs12_create_safe_bag(permArena,
|
| + SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID);
|
| + if(certbag == NULL) {
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| +
|
| + if(shroud_keys != PR_TRUE) {
|
| + keybag = sec_pkcs12_create_safe_bag(permArena,
|
| + SEC_OID_PKCS12_KEY_BAG_ID);
|
| + if(keybag == NULL) {
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| + }
|
| +
|
| + if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) {
|
| + baggage = sec_pkcs12_create_baggage(permArena);
|
| + if(baggage == NULL) {
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| + external_bag = sec_pkcs12_create_external_bag(baggage);
|
| + }
|
| +
|
| + /* package keys and certs */
|
| + i = 0;
|
| + while((nicknames[i] != NULL) && (problem == PR_FALSE)) {
|
| + if(ref_certs[i] != NULL) {
|
| + /* append cert to bag o certs */
|
| + rv = sec_pkcs12_append_cert_to_bag(permArena, certbag,
|
| + ref_certs[i],
|
| + nicknames[i]);
|
| + if(rv == SECFailure) {
|
| + problem = PR_FALSE;
|
| + } else {
|
| + ncerts++;
|
| + }
|
| +
|
| + if(rv == SECSuccess) {
|
| + /* package up them keys */
|
| + if(shroud_keys == PR_TRUE) {
|
| + espvk = sec_pkcs12_get_shrouded_key(permArena,
|
| + nicknames[i],
|
| + ref_certs[i],
|
| + shroud_alg,
|
| + pwitem, unicodeFn,
|
| + wincx);
|
| + if(espvk != NULL) {
|
| + rv = sec_pkcs12_append_shrouded_key(external_bag, espvk);
|
| + SECITEM_CopyItem(permArena, &espvk->derCert,
|
| + &ref_certs[i]->derCert);
|
| + } else {
|
| + rv = SECFailure;
|
| + }
|
| + } else {
|
| + pk = sec_pkcs12_get_private_key(permArena, nicknames[i],
|
| + ref_certs[i], wincx);
|
| + if(pk != NULL) {
|
| + rv = sec_pkcs12_append_key_to_bag(keybag, pk);
|
| + SECITEM_CopyItem(permArena, &espvk->derCert,
|
| + &ref_certs[i]->derCert);
|
| + } else {
|
| + rv = SECFailure;
|
| + }
|
| + }
|
| +
|
| + if(rv == SECFailure) {
|
| + problem = PR_TRUE;
|
| + } else {
|
| + nkeys++;
|
| + }
|
| + }
|
| + } else {
|
| + /* handle only keys here ? */
|
| + problem = PR_TRUE;
|
| + }
|
| + i++;
|
| + }
|
| +
|
| + /* let success fall through */
|
| +loser:
|
| + if(problem == PR_FALSE) {
|
| + /* if we have certs, we want to append the cert bag to the
|
| + * appropriate area
|
| + */
|
| + if(ncerts > 0) {
|
| + if(unencryptedCerts != PR_TRUE) {
|
| + rv = sec_pkcs12_append_safe_bag(safe, certbag);
|
| + } else {
|
| + rv = sec_pkcs12_append_unshrouded_bag(external_bag, certbag);
|
| + }
|
| + } else {
|
| + rv = SECSuccess;
|
| + }
|
| +
|
| + /* append key bag, if they are stored in safe contents */
|
| + if((rv == SECSuccess) && (shroud_keys == PR_FALSE) && (nkeys > 0)) {
|
| + rv = sec_pkcs12_append_safe_bag(safe, keybag);
|
| + }
|
| + } else {
|
| + rv = SECFailure;
|
| + }
|
| +
|
| + /* if baggage not used, NULLify it */
|
| + if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) {
|
| + if(((unencryptedCerts == PR_TRUE) && (ncerts == 0)) &&
|
| + ((shroud_keys == PR_TRUE) && (nkeys == 0)))
|
| + baggage = NULL;
|
| + } else {
|
| + baggage = NULL;
|
| + }
|
| +
|
| + if((problem == PR_TRUE) || (rv == SECFailure)) {
|
| + PORT_FreeArena(permArena, PR_TRUE);
|
| + rv = SECFailure;
|
| + baggage = NULL;
|
| + safe = NULL;
|
| + }
|
| +
|
| + *rBaggage = baggage;
|
| + *rSafe = safe;
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +/* DER encode the safe contents and return a SECItem. if an error
|
| + * occurs, NULL is returned.
|
| + */
|
| +static SECItem *
|
| +sec_pkcs12_encode_safe_contents(SEC_PKCS12SafeContents *safe)
|
| +{
|
| + SECItem *dsafe = NULL, *tsafe;
|
| + void *dummy = NULL;
|
| + PLArenaPool *arena;
|
| +
|
| + if(safe == NULL) {
|
| + return NULL;
|
| + }
|
| +
|
| +/* rv = sec_pkcs12_prepare_for_der_code_safe(safe, PR_TRUE);
|
| + if(rv != SECSuccess) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return NULL;
|
| + }*/
|
| +
|
| + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
|
| + if(arena == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return NULL;
|
| + }
|
| +
|
| + tsafe = (SECItem *)PORT_ArenaZAlloc(arena, sizeof(SECItem));
|
| + if(tsafe != NULL) {
|
| + dummy = SEC_ASN1EncodeItem(arena, tsafe, safe,
|
| + SEC_PKCS12SafeContentsTemplate);
|
| + if(dummy != NULL) {
|
| + dsafe = SECITEM_DupItem(tsafe);
|
| + } else {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + }
|
| + } else {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + }
|
| +
|
| + PORT_FreeArena(arena, PR_TRUE);
|
| +
|
| + return dsafe;
|
| +}
|
| +
|
| +/* prepare the authenicated safe for encoding and encode it.
|
| + * baggage is copied to the appropriate area, safe is encoded and
|
| + * encrypted. the version and transport mode are set on the asafe.
|
| + * the whole ball of wax is then der encoded and packaged up into
|
| + * data content info
|
| + * safe -- container of certs and keys, is encrypted.
|
| + * baggage -- container of certs and keys, keys assumed to be encrypted by
|
| + * another method, certs are in the clear
|
| + * algorithm -- algorithm by which to encrypt safe
|
| + * pwitem -- password for encryption
|
| + * wincx - window handle
|
| + *
|
| + * return of NULL is an error condition.
|
| + */
|
| +static SEC_PKCS7ContentInfo *
|
| +sec_pkcs12_get_auth_safe(SEC_PKCS12SafeContents *safe,
|
| + SEC_PKCS12Baggage *baggage,
|
| + SECOidTag algorithm,
|
| + SECItem *pwitem,
|
| + PKCS12UnicodeConvertFunction unicodeFn,
|
| + void *wincx)
|
| +{
|
| + SECItem *src = NULL, *dest = NULL, *psalt = NULL;
|
| + PLArenaPool *poolp;
|
| + SEC_PKCS12AuthenticatedSafe *asafe;
|
| + SEC_PKCS7ContentInfo *safe_cinfo = NULL;
|
| + SEC_PKCS7ContentInfo *asafe_cinfo = NULL;
|
| + void *dummy;
|
| + SECStatus rv = SECSuccess;
|
| + PRBool swapUnicodeBytes = PR_FALSE;
|
| +
|
| +#ifdef IS_LITTLE_ENDIAN
|
| + swapUnicodeBytes = PR_TRUE;
|
| +#endif
|
| +
|
| + if(((safe != NULL) && (pwitem == NULL)) && (baggage == NULL))
|
| + return NULL;
|
| +
|
| + poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
|
| + if(poolp == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return NULL;
|
| + }
|
| +
|
| + /* prepare authenticated safe for encode */
|
| + asafe = sec_pkcs12_new_asafe(poolp);
|
| + if(asafe != NULL) {
|
| +
|
| + /* set version */
|
| + dummy = SEC_ASN1EncodeInteger(asafe->poolp, &asafe->version,
|
| + SEC_PKCS12_PFX_VERSION);
|
| + if(dummy == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| +
|
| + /* generate the privacy salt used to create virtual pwd */
|
| + psalt = sec_pkcs12_generate_salt();
|
| + if(psalt != NULL) {
|
| + rv = SECITEM_CopyItem(asafe->poolp, &asafe->privacySalt,
|
| + psalt);
|
| + if(rv == SECSuccess) {
|
| + asafe->privacySalt.len *= 8;
|
| + }
|
| + else {
|
| + SECITEM_ZfreeItem(psalt, PR_TRUE);
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + }
|
| + }
|
| +
|
| + if((psalt == NULL) || (rv == SECFailure)) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + rv = SECFailure;
|
| + goto loser;
|
| + }
|
| +
|
| + /* package up safe contents */
|
| + if(safe != NULL)
|
| + {
|
| + safe_cinfo = SEC_PKCS7CreateEncryptedData(algorithm, NULL, wincx);
|
| + if((safe_cinfo != NULL) && (safe->safe_size > 0)) {
|
| + /* encode the safe and encrypt the contents of the
|
| + * content info
|
| + */
|
| + src = sec_pkcs12_encode_safe_contents(safe);
|
| +
|
| + if(src != NULL) {
|
| + rv = SEC_PKCS7SetContent(safe_cinfo, (char *)src->data, src->len);
|
| + SECITEM_ZfreeItem(src, PR_TRUE);
|
| + if(rv == SECSuccess) {
|
| + SECItem *vpwd;
|
| + vpwd = sec_pkcs12_create_virtual_password(pwitem, psalt,
|
| + unicodeFn, swapUnicodeBytes);
|
| + if(vpwd != NULL) {
|
| + rv = SEC_PKCS7EncryptContents(NULL, safe_cinfo,
|
| + vpwd, wincx);
|
| + SECITEM_ZfreeItem(vpwd, PR_TRUE);
|
| + } else {
|
| + rv = SECFailure;
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + }
|
| + } else {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + }
|
| + } else {
|
| + rv = SECFailure;
|
| + }
|
| + } else if(safe->safe_size > 0) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + goto loser;
|
| + } else {
|
| + /* case where there is NULL content in the safe contents */
|
| + rv = SEC_PKCS7SetContent(safe_cinfo, NULL, 0);
|
| + if(rv != SECFailure) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + }
|
| + }
|
| +
|
| + if(rv != SECSuccess) {
|
| + SEC_PKCS7DestroyContentInfo(safe_cinfo);
|
| + safe_cinfo = NULL;
|
| + goto loser;
|
| + }
|
| +
|
| + asafe->safe = safe_cinfo;
|
| + /*
|
| + PORT_Memcpy(&asafe->safe, safe_cinfo, sizeof(*safe_cinfo));
|
| + */
|
| + }
|
| +
|
| + /* copy the baggage to the authenticated safe baggage if present */
|
| + if(baggage != NULL) {
|
| + PORT_Memcpy(&asafe->baggage, baggage, sizeof(*baggage));
|
| + }
|
| +
|
| + /* encode authenticated safe and store it in a Data content info */
|
| + dest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem));
|
| + if(dest != NULL) {
|
| + dummy = SEC_ASN1EncodeItem(poolp, dest, asafe,
|
| + SEC_PKCS12AuthenticatedSafeTemplate);
|
| + if(dummy != NULL) {
|
| + asafe_cinfo = SEC_PKCS7CreateData();
|
| + if(asafe_cinfo != NULL) {
|
| + rv = SEC_PKCS7SetContent(asafe_cinfo,
|
| + (char *)dest->data,
|
| + dest->len);
|
| + if(rv != SECSuccess) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + SEC_PKCS7DestroyContentInfo(asafe_cinfo);
|
| + asafe_cinfo = NULL;
|
| + }
|
| + }
|
| + } else {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + rv = SECFailure;
|
| + }
|
| + }
|
| + }
|
| +
|
| +loser:
|
| + PORT_FreeArena(poolp, PR_TRUE);
|
| + if(safe_cinfo != NULL) {
|
| + SEC_PKCS7DestroyContentInfo(safe_cinfo);
|
| + }
|
| + if(psalt != NULL) {
|
| + SECITEM_ZfreeItem(psalt, PR_TRUE);
|
| + }
|
| +
|
| + if(rv == SECFailure) {
|
| + return NULL;
|
| + }
|
| +
|
| + return asafe_cinfo;
|
| +}
|
| +
|
| +/* generates the PFX and computes the mac on the authenticated safe
|
| + * NULL implies an error
|
| + */
|
| +static SEC_PKCS12PFXItem *
|
| +sec_pkcs12_get_pfx(SEC_PKCS7ContentInfo *cinfo,
|
| + PRBool do_mac,
|
| + SECItem *pwitem, PKCS12UnicodeConvertFunction unicodeFn)
|
| +{
|
| + SECItem *dest = NULL, *mac = NULL, *salt = NULL, *key = NULL;
|
| + SEC_PKCS12PFXItem *pfx;
|
| + SECStatus rv = SECFailure;
|
| + SGNDigestInfo *di;
|
| + SECItem *vpwd;
|
| + PRBool swapUnicodeBytes = PR_FALSE;
|
| +
|
| +#ifdef IS_LITTLE_ENDIAN
|
| + swapUnicodeBytes = PR_TRUE;
|
| +#endif
|
| +
|
| + if((cinfo == NULL) || ((do_mac == PR_TRUE) && (pwitem == NULL))) {
|
| + return NULL;
|
| + }
|
| +
|
| + /* allocate new pfx structure */
|
| + pfx = sec_pkcs12_new_pfx();
|
| + if(pfx == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return NULL;
|
| + }
|
| +
|
| + PORT_Memcpy(&pfx->authSafe, cinfo, sizeof(*cinfo));
|
| + if(do_mac == PR_TRUE) {
|
| +
|
| + /* salt for computing mac */
|
| + salt = sec_pkcs12_generate_salt();
|
| + if(salt != NULL) {
|
| + rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, salt);
|
| + pfx->macData.macSalt.len *= 8;
|
| +
|
| + vpwd = sec_pkcs12_create_virtual_password(pwitem, salt,
|
| + unicodeFn, swapUnicodeBytes);
|
| + if(vpwd == NULL) {
|
| + rv = SECFailure;
|
| + key = NULL;
|
| + } else {
|
| + key = sec_pkcs12_generate_key_from_password(SEC_OID_SHA1,
|
| + salt, vpwd);
|
| + SECITEM_ZfreeItem(vpwd, PR_TRUE);
|
| + }
|
| +
|
| + if((key != NULL) && (rv == SECSuccess)) {
|
| + dest = SEC_PKCS7GetContent(cinfo);
|
| + if(dest != NULL) {
|
| +
|
| + /* compute mac on data -- for password integrity mode */
|
| + mac = sec_pkcs12_generate_mac(key, dest, PR_FALSE);
|
| + if(mac != NULL) {
|
| + di = SGN_CreateDigestInfo(SEC_OID_SHA1,
|
| + mac->data, mac->len);
|
| + if(di != NULL) {
|
| + rv = SGN_CopyDigestInfo(pfx->poolp,
|
| + &pfx->macData.safeMac, di);
|
| + SGN_DestroyDigestInfo(di);
|
| + } else {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + }
|
| + SECITEM_ZfreeItem(mac, PR_TRUE);
|
| + }
|
| + } else {
|
| + rv = SECFailure;
|
| + }
|
| + } else {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + rv = SECFailure;
|
| + }
|
| +
|
| + if(key != NULL) {
|
| + SECITEM_ZfreeItem(key, PR_TRUE);
|
| + }
|
| + SECITEM_ZfreeItem(salt, PR_TRUE);
|
| + }
|
| + }
|
| +
|
| + if(rv == SECFailure) {
|
| + SEC_PKCS12DestroyPFX(pfx);
|
| + pfx = NULL;
|
| + }
|
| +
|
| + return pfx;
|
| +}
|
| +
|
| +/* der encode the pfx */
|
| +static SECItem *
|
| +sec_pkcs12_encode_pfx(SEC_PKCS12PFXItem *pfx)
|
| +{
|
| + SECItem *dest;
|
| + void *dummy;
|
| +
|
| + if(pfx == NULL) {
|
| + return NULL;
|
| + }
|
| +
|
| + dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
|
| + if(dest == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + return NULL;
|
| + }
|
| +
|
| + dummy = SEC_ASN1EncodeItem(NULL, dest, pfx, SEC_PKCS12PFXItemTemplate);
|
| + if(dummy == NULL) {
|
| + PORT_SetError(SEC_ERROR_NO_MEMORY);
|
| + SECITEM_ZfreeItem(dest, PR_TRUE);
|
| + dest = NULL;
|
| + }
|
| +
|
| + return dest;
|
| +}
|
| +
|
| +SECItem *
|
| +SEC_PKCS12GetPFX(char **nicknames,
|
| + CERTCertificate **ref_certs,
|
| + PRBool shroud_keys,
|
| + SEC_PKCS5GetPBEPassword pbef,
|
| + void *pbearg,
|
| + PKCS12UnicodeConvertFunction unicodeFn,
|
| + void *wincx)
|
| +{
|
| + SECItem **nicks = NULL;
|
| + SEC_PKCS12PFXItem *pfx = NULL;
|
| + SEC_PKCS12Baggage *baggage = NULL;
|
| + SEC_PKCS12SafeContents *safe = NULL;
|
| + SEC_PKCS7ContentInfo *cinfo = NULL;
|
| + SECStatus rv = SECFailure;
|
| + SECItem *dest = NULL, *pwitem = NULL;
|
| + PRBool problem = PR_FALSE;
|
| + PRBool unencryptedCerts;
|
| + SECOidTag shroud_alg, safe_alg;
|
| +
|
| + /* how should we encrypt certs ? */
|
| + unencryptedCerts = !SEC_PKCS12IsEncryptionAllowed();
|
| + if(!unencryptedCerts) {
|
| + safe_alg = SEC_PKCS12GetPreferredEncryptionAlgorithm();
|
| + if(safe_alg == SEC_OID_UNKNOWN) {
|
| + safe_alg = SEC_PKCS12GetStrongestAllowedAlgorithm();
|
| + }
|
| + if(safe_alg == SEC_OID_UNKNOWN) {
|
| + unencryptedCerts = PR_TRUE;
|
| + /* for export where no encryption is allowed, we still need
|
| + * to encrypt the NULL contents per the spec. encrypted info
|
| + * is known plaintext, so it shouldn't be a problem.
|
| + */
|
| + safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
|
| + }
|
| + } else {
|
| + /* for export where no encryption is allowed, we still need
|
| + * to encrypt the NULL contents per the spec. encrypted info
|
| + * is known plaintext, so it shouldn't be a problem.
|
| + */
|
| + safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
|
| + }
|
| +
|
| + /* keys are always stored with triple DES */
|
| + shroud_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
|
| +
|
| + /* check for FIPS, if so, do not encrypt certs */
|
| + if(PK11_IsFIPS() && !unencryptedCerts) {
|
| + unencryptedCerts = PR_TRUE;
|
| + }
|
| +
|
| + if((nicknames == NULL) || (pbef == NULL) || (ref_certs == NULL)) {
|
| + problem = PR_TRUE;
|
| + goto loser;
|
| + }
|
| +
|
| +
|
| + /* get password */
|
| + pwitem = (*pbef)(pbearg);
|
| + if(pwitem == NULL) {
|
| + problem = PR_TRUE;
|
| + goto loser;
|
| + }
|
| + nicks = sec_pkcs12_convert_nickname_list(nicknames);
|
| +
|
| + /* get safe and baggage */
|
| + rv = sec_pkcs12_package_certs_and_keys(nicks, ref_certs, unencryptedCerts,
|
| + &safe, &baggage, shroud_keys,
|
| + shroud_alg, pwitem, unicodeFn, wincx);
|
| + if(rv == SECFailure) {
|
| + problem = PR_TRUE;
|
| + }
|
| +
|
| + if((safe != NULL) && (problem == PR_FALSE)) {
|
| + /* copy thumbprints */
|
| + rv = sec_pkcs12_propagate_thumbprints(nicks, ref_certs, safe, baggage);
|
| +
|
| + /* package everything up into AuthenticatedSafe */
|
| + cinfo = sec_pkcs12_get_auth_safe(safe, baggage,
|
| + safe_alg, pwitem, unicodeFn, wincx);
|
| +
|
| + sec_pkcs12_destroy_cert_content_infos(safe, baggage);
|
| +
|
| + /* get the pfx and mac it */
|
| + if(cinfo != NULL) {
|
| + pfx = sec_pkcs12_get_pfx(cinfo, PR_TRUE, pwitem, unicodeFn);
|
| + if(pfx != NULL) {
|
| + dest = sec_pkcs12_encode_pfx(pfx);
|
| + SEC_PKCS12DestroyPFX(pfx);
|
| + }
|
| + SEC_PKCS7DestroyContentInfo(cinfo);
|
| + }
|
| +
|
| + if(safe != NULL) {
|
| + PORT_FreeArena(safe->poolp, PR_TRUE);
|
| + }
|
| + } else {
|
| + if(safe != NULL) {
|
| + PORT_FreeArena(safe->poolp, PR_TRUE);
|
| + }
|
| + }
|
| +
|
| +loser:
|
| + if(nicks != NULL) {
|
| + sec_pkcs12_destroy_nickname_list(nicks);
|
| + }
|
| +
|
| + if(ref_certs != NULL) {
|
| + sec_pkcs12_destroy_certificate_list(ref_certs);
|
| + }
|
| +
|
| + if(pwitem != NULL) {
|
| + SECITEM_ZfreeItem(pwitem, PR_TRUE);
|
| + }
|
| +
|
| + if(problem == PR_TRUE) {
|
| + dest = NULL;
|
| + }
|
| +
|
| + return dest;
|
| +}
|
|
|