/* 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/. */
externconstint 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.
*********************************/
/********************************* * 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
*/ unsignedint 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;
/* 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;
};
/* An encoder context which is used for the actual encoding * portion of PKCS 12.
*/ typedefstruct 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;
/* 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;
}
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;
}
/* * 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;
/* 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); goto loser;
}
safeInfo->itemCount = 0;
/* create the encrypted safe */ if (!SEC_PKCS5IsAlgorithmPBEAlgTag(privAlg)) {
SECOidTag prfAlg = SEC_OID_UNKNOWN; /* if we have password integrity set, use that to set the integrity * hash algorithm to set our password PRF. If we haven't set it, just
* let the low level code pick it */ if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
SECOidTag integrityAlg = p12ctxt->integrityInfo.pwdInfo.algorithm;
prfAlg = integrityAlg; /* verify that integrityAlg is an HMAC */ if (HASH_GetHashOidTagByHMACOidTag(integrityAlg) == SEC_OID_UNKNOWN) { /* it's not, find the hmac */
prfAlg = HASH_GetHMACOidTagByHashOidTag(integrityAlg); /* if prfAlg is SEC_OID_UNKNOWN at this point we'll
* default to SEC_OID_HMAC_SHA1 in the low level pbe code. */
}
} if (!SEC_PKCS12CipherAllowed(privAlg, prfAlg)) {
PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM); goto loser;
}
safeInfo->cinfo = SEC_PKCS7CreateEncryptedDataWithPBEV2(SEC_OID_PKCS5_PBES2,
privAlg,
prfAlg,
0,
p12ctxt->pwfn,
p12ctxt->pwfnarg);
} else { if (!SEC_PKCS12CipherAllowed(privAlg, SEC_OID_UNKNOWN)) {
PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM); goto loser;
}
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;
/* 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;
/* 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) { unsignedint 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;
}
/********************************* * 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; 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;
}
/* 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; break; default: goto loser;
}
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;
}
/* 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;
}
/* 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; unsignedint nItems = 0;
SECStatus rv;
/* 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;
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 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); unsignedint count = 0; if (!certList) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
/* 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;
}
/* 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;
/* 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;
}
}
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 {
if (!sec_pkcs12_encode_password(p12ctxt->arena, &uniPwitem, algorithm,
pwitem)) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
/* if we have password integrity set, use that to set the integrity * hash algorithm to set our password PRF. If we haven't set it, just
* let the low level code pick it */ if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
SECOidTag integrityAlg = p12ctxt->integrityInfo.pwdInfo.algorithm;
prfAlg = integrityAlg; /* verify that integrityAlg is an HMAC */ if (HASH_GetHashOidTagByHMACOidTag(integrityAlg) == SEC_OID_UNKNOWN) { /* it's not, find the hmac */
prfAlg = HASH_GetHMACOidTagByHashOidTag(integrityAlg); /* if prfAlg is SEC_OID_UNKNOWN at this point we'll
* default to SEC_OID_HMAC_SHA1 in the low level pbe code. */
}
}
if (!SEC_PKCS12CipherAllowed(algorithm, prfAlg)) {
PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM); 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);
}
/* passing algorithm as the pbe will force the PBE code to * automatically handle the selection between using the algorithm * as a the pbe algorithm, or using the algorithm as a cipher
* and building a pkcs5 pbe */
epki = PK11_ExportEncryptedPrivateKeyInfoV2(slot, algorithm,
SEC_OID_UNKNOWN, prfAlg,
&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;
}
/* if no nickname specified, let's see if the certificate has a * nickname.
*/ if (!nickName) { if (cert->nickname) {
nickname.data = (unsignedchar *)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;
}
}
/* 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;
/* 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;
}
/* Clean up the resources allocated by a sec_PKCS12EncoderContext. */ staticvoid
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; unsignedint i, nonEmptyCnt;
SECStatus rv;
SECItem ignore = { 0 }; void *mark;
SECItem *salt = NULL;
SECItem pwd = { siBuffer, NULL, 0 };
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;
}
/* 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) { /* 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;
}
PORT_CheckSuccess(SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo));
} else {
p12enc->aSafeCinfo = SEC_PKCS7CreateData();
/* create the digest info */
hmacAlgTag = p12exp->integrityInfo.pwdInfo.algorithm;
hashAlgTag = HASH_GetHashOidTagByHMACOidTag(hmacAlgTag); if (hashAlgTag != SEC_OID_UNKNOWN) { /* if the application asks for hmac explicitly, then use
* pkcs5v2 mac1 encoding */
SECAlgorithmID *algID; int keyLength;
keyLength = HASH_ResultLenByOidTag(hashAlgTag); if (keyLength == 0) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return NULL;
} /* create PB_MAC1 params */
algID = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBMAC1,
hmacAlgTag,
hmacAlgTag, keyLength,
NSS_PBE_DEFAULT_ITERATION_COUNT,
&p12enc->mac.macSalt); if (algID == NULL) { goto loser;
}
rv = SECOID_CopyAlgorithmID(p12enc->arena,
&p12enc->mac.safeMac.digestAlgorithm,
algID);
SECOID_DestroyAlgorithmID(algID, PR_TRUE); if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
} elseif (HASH_GetHashTypeByOidTag(hmacAlgTag) != HASH_AlgNULL) { /* encode the algid now for sec_pkcs12_integrity_key to use */ /* this must be a valid hash function, SECOID_SetAlgorithmID * knows to encode the hash algid with an explicit
* null parameter */
rv = SECOID_SetAlgorithmID(p12enc->arena,
&p12enc->mac.safeMac.digestAlgorithm,
hmacAlgTag, NULL); if (rv != SECSuccess) { goto loser;
}
} else {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); goto loser;
}
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);
} if (salt) {
SECITEM_ZfreeItem(salt, PR_TRUE);
} if (pwd.data) {
SECITEM_ZfreeItem(&pwd, PR_FALSE);
}
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.
*/ staticvoid
sec_P12A1OutputCB_Outer(void *arg, constchar *buf, unsignedlong len, int depth, SEC_ASN1EncodingPart data_kind)
{ struct sec_pkcs12_encoder_output *output;
/* 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.
*/ staticvoid
sec_P12A1OutputCB_HmacP7Update(void *arg, constchar *buf, unsignedlong len, int depth,
SEC_ASN1EncodingPart data_kind)
{
sec_pkcs12OutputBuffer *bufcx = (sec_pkcs12OutputBuffer *)arg;
if (!buf || !len) return;
if (bufcx->hmacCx) {
PK11_DigestOp(bufcx->hmacCx, (unsignedchar *)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;
}
}
/* 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.
*/ staticvoid
sec_P12P7OutputCB_CallA1Update(void *arg, constchar *buf, unsignedlong 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;
/* 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;
}
/* finish the HMAC and encode the macData so that it can be * encoded.
*/ static SECStatus
sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx)
{ unsignedchar hmacData[HASH_LENGTH_MAX]; unsignedint hmacLen;
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;
}
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
/* finish the digest info, algorithm ID is already set */
rv = SECITEM_MakeItem(p12ecx->arena, &p12ecx->mac.safeMac.digest,
hmacData, hmacLen); if (rv != SECSuccess) { 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;
}
/* 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.
*/ staticvoid
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_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
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.62Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.