| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074 | 
							- /**
 
-  * Javascript implementation of PKCS#12.
 
-  *
 
-  * @author Dave Longley
 
-  * @author Stefan Siegl <stesie@brokenpipe.de>
 
-  *
 
-  * Copyright (c) 2010-2014 Digital Bazaar, Inc.
 
-  * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
 
-  *
 
-  * The ASN.1 representation of PKCS#12 is as follows
 
-  * (see ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12-tc1.pdf for details)
 
-  *
 
-  * PFX ::= SEQUENCE {
 
-  *   version  INTEGER {v3(3)}(v3,...),
 
-  *   authSafe ContentInfo,
 
-  *   macData  MacData OPTIONAL
 
-  * }
 
-  *
 
-  * MacData ::= SEQUENCE {
 
-  *   mac DigestInfo,
 
-  *   macSalt OCTET STRING,
 
-  *   iterations INTEGER DEFAULT 1
 
-  * }
 
-  * Note: The iterations default is for historical reasons and its use is
 
-  * deprecated. A higher value, like 1024, is recommended.
 
-  *
 
-  * DigestInfo is defined in PKCS#7 as follows:
 
-  *
 
-  * DigestInfo ::= SEQUENCE {
 
-  *   digestAlgorithm DigestAlgorithmIdentifier,
 
-  *   digest Digest
 
-  * }
 
-  *
 
-  * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
 
-  *
 
-  * The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
 
-  * for the algorithm, if any. In the case of SHA1 there is none.
 
-  *
 
-  * AlgorithmIdentifer ::= SEQUENCE {
 
-  *    algorithm OBJECT IDENTIFIER,
 
-  *    parameters ANY DEFINED BY algorithm OPTIONAL
 
-  * }
 
-  *
 
-  * Digest ::= OCTET STRING
 
-  *
 
-  *
 
-  * ContentInfo ::= SEQUENCE {
 
-  *   contentType ContentType,
 
-  *   content     [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
 
-  * }
 
-  *
 
-  * ContentType ::= OBJECT IDENTIFIER
 
-  *
 
-  * AuthenticatedSafe ::= SEQUENCE OF ContentInfo
 
-  * -- Data if unencrypted
 
-  * -- EncryptedData if password-encrypted
 
-  * -- EnvelopedData if public key-encrypted
 
-  *
 
-  *
 
-  * SafeContents ::= SEQUENCE OF SafeBag
 
-  *
 
-  * SafeBag ::= SEQUENCE {
 
-  *   bagId     BAG-TYPE.&id ({PKCS12BagSet})
 
-  *   bagValue  [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
 
-  *   bagAttributes SET OF PKCS12Attribute OPTIONAL
 
-  * }
 
-  *
 
-  * PKCS12Attribute ::= SEQUENCE {
 
-  *   attrId ATTRIBUTE.&id ({PKCS12AttrSet}),
 
-  *   attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId})
 
-  * } -- This type is compatible with the X.500 type 'Attribute'
 
-  *
 
-  * PKCS12AttrSet ATTRIBUTE ::= {
 
-  *   friendlyName | -- from PKCS #9
 
-  *   localKeyId, -- from PKCS #9
 
-  *   ... -- Other attributes are allowed
 
-  * }
 
-  *
 
-  * CertBag ::= SEQUENCE {
 
-  *   certId    BAG-TYPE.&id   ({CertTypes}),
 
-  *   certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId})
 
-  * }
 
-  *
 
-  * x509Certificate BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}}
 
-  *   -- DER-encoded X.509 certificate stored in OCTET STRING
 
-  *
 
-  * sdsiCertificate BAG-TYPE ::= {IA5String IDENTIFIED BY {certTypes 2}}
 
-  * -- Base64-encoded SDSI certificate stored in IA5String
 
-  *
 
-  * CertTypes BAG-TYPE ::= {
 
-  *   x509Certificate |
 
-  *   sdsiCertificate,
 
-  *   ... -- For future extensions
 
-  * }
 
-  */
 
- var forge = require('./forge');
 
- require('./asn1');
 
- require('./hmac');
 
- require('./oids');
 
- require('./pkcs7asn1');
 
- require('./pbe');
 
- require('./random');
 
- require('./rsa');
 
- require('./sha1');
 
- require('./util');
 
- require('./x509');
 
- // shortcut for asn.1 & PKI API
 
- var asn1 = forge.asn1;
 
- var pki = forge.pki;
 
- // shortcut for PKCS#12 API
 
- var p12 = module.exports = forge.pkcs12 = forge.pkcs12 || {};
 
- var contentInfoValidator = {
 
-   name: 'ContentInfo',
 
-   tagClass: asn1.Class.UNIVERSAL,
 
-   type: asn1.Type.SEQUENCE,  // a ContentInfo
 
-   constructed: true,
 
-   value: [{
 
-     name: 'ContentInfo.contentType',
 
-     tagClass: asn1.Class.UNIVERSAL,
 
-     type: asn1.Type.OID,
 
-     constructed: false,
 
-     capture: 'contentType'
 
-   }, {
 
-     name: 'ContentInfo.content',
 
-     tagClass: asn1.Class.CONTEXT_SPECIFIC,
 
-     constructed: true,
 
-     captureAsn1: 'content'
 
-   }]
 
- };
 
- var pfxValidator = {
 
-   name: 'PFX',
 
-   tagClass: asn1.Class.UNIVERSAL,
 
-   type: asn1.Type.SEQUENCE,
 
-   constructed: true,
 
-   value: [{
 
-     name: 'PFX.version',
 
-     tagClass: asn1.Class.UNIVERSAL,
 
-     type: asn1.Type.INTEGER,
 
-     constructed: false,
 
-     capture: 'version'
 
-   },
 
-   contentInfoValidator, {
 
-     name: 'PFX.macData',
 
-     tagClass: asn1.Class.UNIVERSAL,
 
-     type: asn1.Type.SEQUENCE,
 
-     constructed: true,
 
-     optional: true,
 
-     captureAsn1: 'mac',
 
-     value: [{
 
-       name: 'PFX.macData.mac',
 
-       tagClass: asn1.Class.UNIVERSAL,
 
-       type: asn1.Type.SEQUENCE,  // DigestInfo
 
-       constructed: true,
 
-       value: [{
 
-         name: 'PFX.macData.mac.digestAlgorithm',
 
-         tagClass: asn1.Class.UNIVERSAL,
 
-         type: asn1.Type.SEQUENCE,  // DigestAlgorithmIdentifier
 
-         constructed: true,
 
-         value: [{
 
-           name: 'PFX.macData.mac.digestAlgorithm.algorithm',
 
-           tagClass: asn1.Class.UNIVERSAL,
 
-           type: asn1.Type.OID,
 
-           constructed: false,
 
-           capture: 'macAlgorithm'
 
-         }, {
 
-           name: 'PFX.macData.mac.digestAlgorithm.parameters',
 
-           tagClass: asn1.Class.UNIVERSAL,
 
-           captureAsn1: 'macAlgorithmParameters'
 
-         }]
 
-       }, {
 
-         name: 'PFX.macData.mac.digest',
 
-         tagClass: asn1.Class.UNIVERSAL,
 
-         type: asn1.Type.OCTETSTRING,
 
-         constructed: false,
 
-         capture: 'macDigest'
 
-       }]
 
-     }, {
 
-       name: 'PFX.macData.macSalt',
 
-       tagClass: asn1.Class.UNIVERSAL,
 
-       type: asn1.Type.OCTETSTRING,
 
-       constructed: false,
 
-       capture: 'macSalt'
 
-     }, {
 
-       name: 'PFX.macData.iterations',
 
-       tagClass: asn1.Class.UNIVERSAL,
 
-       type: asn1.Type.INTEGER,
 
-       constructed: false,
 
-       optional: true,
 
-       capture: 'macIterations'
 
-     }]
 
-   }]
 
- };
 
- var safeBagValidator = {
 
-   name: 'SafeBag',
 
-   tagClass: asn1.Class.UNIVERSAL,
 
-   type: asn1.Type.SEQUENCE,
 
-   constructed: true,
 
-   value: [{
 
-     name: 'SafeBag.bagId',
 
-     tagClass: asn1.Class.UNIVERSAL,
 
-     type: asn1.Type.OID,
 
-     constructed: false,
 
-     capture: 'bagId'
 
-   }, {
 
-     name: 'SafeBag.bagValue',
 
-     tagClass: asn1.Class.CONTEXT_SPECIFIC,
 
-     constructed: true,
 
-     captureAsn1: 'bagValue'
 
-   }, {
 
-     name: 'SafeBag.bagAttributes',
 
-     tagClass: asn1.Class.UNIVERSAL,
 
-     type: asn1.Type.SET,
 
-     constructed: true,
 
-     optional: true,
 
-     capture: 'bagAttributes'
 
-   }]
 
- };
 
- var attributeValidator = {
 
-   name: 'Attribute',
 
-   tagClass: asn1.Class.UNIVERSAL,
 
-   type: asn1.Type.SEQUENCE,
 
-   constructed: true,
 
-   value: [{
 
-     name: 'Attribute.attrId',
 
-     tagClass: asn1.Class.UNIVERSAL,
 
-     type: asn1.Type.OID,
 
-     constructed: false,
 
-     capture: 'oid'
 
-   }, {
 
-     name: 'Attribute.attrValues',
 
-     tagClass: asn1.Class.UNIVERSAL,
 
-     type: asn1.Type.SET,
 
-     constructed: true,
 
-     capture: 'values'
 
-   }]
 
- };
 
- var certBagValidator = {
 
-   name: 'CertBag',
 
-   tagClass: asn1.Class.UNIVERSAL,
 
-   type: asn1.Type.SEQUENCE,
 
-   constructed: true,
 
-   value: [{
 
-     name: 'CertBag.certId',
 
-     tagClass: asn1.Class.UNIVERSAL,
 
-     type: asn1.Type.OID,
 
-     constructed: false,
 
-     capture: 'certId'
 
-   }, {
 
-     name: 'CertBag.certValue',
 
-     tagClass: asn1.Class.CONTEXT_SPECIFIC,
 
-     constructed: true,
 
-     /* So far we only support X.509 certificates (which are wrapped in
 
-        an OCTET STRING, hence hard code that here). */
 
-     value: [{
 
-       name: 'CertBag.certValue[0]',
 
-       tagClass: asn1.Class.UNIVERSAL,
 
-       type: asn1.Class.OCTETSTRING,
 
-       constructed: false,
 
-       capture: 'cert'
 
-     }]
 
-   }]
 
- };
 
- /**
 
-  * Search SafeContents structure for bags with matching attributes.
 
-  *
 
-  * The search can optionally be narrowed by a certain bag type.
 
-  *
 
-  * @param safeContents the SafeContents structure to search in.
 
-  * @param attrName the name of the attribute to compare against.
 
-  * @param attrValue the attribute value to search for.
 
-  * @param [bagType] bag type to narrow search by.
 
-  *
 
-  * @return an array of matching bags.
 
-  */
 
- function _getBagsByAttribute(safeContents, attrName, attrValue, bagType) {
 
-   var result = [];
 
-   for(var i = 0; i < safeContents.length; i++) {
 
-     for(var j = 0; j < safeContents[i].safeBags.length; j++) {
 
-       var bag = safeContents[i].safeBags[j];
 
-       if(bagType !== undefined && bag.type !== bagType) {
 
-         continue;
 
-       }
 
-       // only filter by bag type, no attribute specified
 
-       if(attrName === null) {
 
-         result.push(bag);
 
-         continue;
 
-       }
 
-       if(bag.attributes[attrName] !== undefined &&
 
-         bag.attributes[attrName].indexOf(attrValue) >= 0) {
 
-         result.push(bag);
 
-       }
 
-     }
 
-   }
 
-   return result;
 
- }
 
- /**
 
-  * Converts a PKCS#12 PFX in ASN.1 notation into a PFX object.
 
-  *
 
-  * @param obj The PKCS#12 PFX in ASN.1 notation.
 
-  * @param strict true to use strict DER decoding, false not to (default: true).
 
-  * @param {String} password Password to decrypt with (optional).
 
-  *
 
-  * @return PKCS#12 PFX object.
 
-  */
 
- p12.pkcs12FromAsn1 = function(obj, strict, password) {
 
-   // handle args
 
-   if(typeof strict === 'string') {
 
-     password = strict;
 
-     strict = true;
 
-   } else if(strict === undefined) {
 
-     strict = true;
 
-   }
 
-   // validate PFX and capture data
 
-   var capture = {};
 
-   var errors = [];
 
-   if(!asn1.validate(obj, pfxValidator, capture, errors)) {
 
-     var error = new Error('Cannot read PKCS#12 PFX. ' +
 
-       'ASN.1 object is not an PKCS#12 PFX.');
 
-     error.errors = error;
 
-     throw error;
 
-   }
 
-   var pfx = {
 
-     version: capture.version.charCodeAt(0),
 
-     safeContents: [],
 
-     /**
 
-      * Gets bags with matching attributes.
 
-      *
 
-      * @param filter the attributes to filter by:
 
-      *          [localKeyId] the localKeyId to search for.
 
-      *          [localKeyIdHex] the localKeyId in hex to search for.
 
-      *          [friendlyName] the friendly name to search for.
 
-      *          [bagType] bag type to narrow each attribute search by.
 
-      *
 
-      * @return a map of attribute type to an array of matching bags or, if no
 
-      *           attribute was given but a bag type, the map key will be the
 
-      *           bag type.
 
-      */
 
-     getBags: function(filter) {
 
-       var rval = {};
 
-       var localKeyId;
 
-       if('localKeyId' in filter) {
 
-         localKeyId = filter.localKeyId;
 
-       } else if('localKeyIdHex' in filter) {
 
-         localKeyId = forge.util.hexToBytes(filter.localKeyIdHex);
 
-       }
 
-       // filter on bagType only
 
-       if(localKeyId === undefined && !('friendlyName' in filter) &&
 
-         'bagType' in filter) {
 
-         rval[filter.bagType] = _getBagsByAttribute(
 
-           pfx.safeContents, null, null, filter.bagType);
 
-       }
 
-       if(localKeyId !== undefined) {
 
-         rval.localKeyId = _getBagsByAttribute(
 
-           pfx.safeContents, 'localKeyId',
 
-           localKeyId, filter.bagType);
 
-       }
 
-       if('friendlyName' in filter) {
 
-         rval.friendlyName = _getBagsByAttribute(
 
-           pfx.safeContents, 'friendlyName',
 
-           filter.friendlyName, filter.bagType);
 
-       }
 
-       return rval;
 
-     },
 
-     /**
 
-      * DEPRECATED: use getBags() instead.
 
-      *
 
-      * Get bags with matching friendlyName attribute.
 
-      *
 
-      * @param friendlyName the friendly name to search for.
 
-      * @param [bagType] bag type to narrow search by.
 
-      *
 
-      * @return an array of bags with matching friendlyName attribute.
 
-      */
 
-     getBagsByFriendlyName: function(friendlyName, bagType) {
 
-       return _getBagsByAttribute(
 
-         pfx.safeContents, 'friendlyName', friendlyName, bagType);
 
-     },
 
-     /**
 
-      * DEPRECATED: use getBags() instead.
 
-      *
 
-      * Get bags with matching localKeyId attribute.
 
-      *
 
-      * @param localKeyId the localKeyId to search for.
 
-      * @param [bagType] bag type to narrow search by.
 
-      *
 
-      * @return an array of bags with matching localKeyId attribute.
 
-      */
 
-     getBagsByLocalKeyId: function(localKeyId, bagType) {
 
-       return _getBagsByAttribute(
 
-         pfx.safeContents, 'localKeyId', localKeyId, bagType);
 
-     }
 
-   };
 
-   if(capture.version.charCodeAt(0) !== 3) {
 
-     var error = new Error('PKCS#12 PFX of version other than 3 not supported.');
 
-     error.version = capture.version.charCodeAt(0);
 
-     throw error;
 
-   }
 
-   if(asn1.derToOid(capture.contentType) !== pki.oids.data) {
 
-     var error = new Error('Only PKCS#12 PFX in password integrity mode supported.');
 
-     error.oid = asn1.derToOid(capture.contentType);
 
-     throw error;
 
-   }
 
-   var data = capture.content.value[0];
 
-   if(data.tagClass !== asn1.Class.UNIVERSAL ||
 
-      data.type !== asn1.Type.OCTETSTRING) {
 
-     throw new Error('PKCS#12 authSafe content data is not an OCTET STRING.');
 
-   }
 
-   data = _decodePkcs7Data(data);
 
-   // check for MAC
 
-   if(capture.mac) {
 
-     var md = null;
 
-     var macKeyBytes = 0;
 
-     var macAlgorithm = asn1.derToOid(capture.macAlgorithm);
 
-     switch(macAlgorithm) {
 
-     case pki.oids.sha1:
 
-       md = forge.md.sha1.create();
 
-       macKeyBytes = 20;
 
-       break;
 
-     case pki.oids.sha256:
 
-       md = forge.md.sha256.create();
 
-       macKeyBytes = 32;
 
-       break;
 
-     case pki.oids.sha384:
 
-       md = forge.md.sha384.create();
 
-       macKeyBytes = 48;
 
-       break;
 
-     case pki.oids.sha512:
 
-       md = forge.md.sha512.create();
 
-       macKeyBytes = 64;
 
-       break;
 
-     case pki.oids.md5:
 
-       md = forge.md.md5.create();
 
-       macKeyBytes = 16;
 
-       break;
 
-     }
 
-     if(md === null) {
 
-       throw new Error('PKCS#12 uses unsupported MAC algorithm: ' + macAlgorithm);
 
-     }
 
-     // verify MAC (iterations default to 1)
 
-     var macSalt = new forge.util.ByteBuffer(capture.macSalt);
 
-     var macIterations = (('macIterations' in capture) ?
 
-       parseInt(forge.util.bytesToHex(capture.macIterations), 16) : 1);
 
-     var macKey = p12.generateKey(
 
-       password, macSalt, 3, macIterations, macKeyBytes, md);
 
-     var mac = forge.hmac.create();
 
-     mac.start(md, macKey);
 
-     mac.update(data.value);
 
-     var macValue = mac.getMac();
 
-     if(macValue.getBytes() !== capture.macDigest) {
 
-       throw new Error('PKCS#12 MAC could not be verified. Invalid password?');
 
-     }
 
-   }
 
-   _decodeAuthenticatedSafe(pfx, data.value, strict, password);
 
-   return pfx;
 
- };
 
- /**
 
-  * Decodes PKCS#7 Data. PKCS#7 (RFC 2315) defines "Data" as an OCTET STRING,
 
-  * but it is sometimes an OCTET STRING that is composed/constructed of chunks,
 
-  * each its own OCTET STRING. This is BER-encoding vs. DER-encoding. This
 
-  * function transforms this corner-case into the usual simple,
 
-  * non-composed/constructed OCTET STRING.
 
-  *
 
-  * This function may be moved to ASN.1 at some point to better deal with
 
-  * more BER-encoding issues, should they arise.
 
-  *
 
-  * @param data the ASN.1 Data object to transform.
 
-  */
 
- function _decodePkcs7Data(data) {
 
-   // handle special case of "chunked" data content: an octet string composed
 
-   // of other octet strings
 
-   if(data.composed || data.constructed) {
 
-     var value = forge.util.createBuffer();
 
-     for(var i = 0; i < data.value.length; ++i) {
 
-       value.putBytes(data.value[i].value);
 
-     }
 
-     data.composed = data.constructed = false;
 
-     data.value = value.getBytes();
 
-   }
 
-   return data;
 
- }
 
- /**
 
-  * Decode PKCS#12 AuthenticatedSafe (BER encoded) into PFX object.
 
-  *
 
-  * The AuthenticatedSafe is a BER-encoded SEQUENCE OF ContentInfo.
 
-  *
 
-  * @param pfx The PKCS#12 PFX object to fill.
 
-  * @param {String} authSafe BER-encoded AuthenticatedSafe.
 
-  * @param strict true to use strict DER decoding, false not to.
 
-  * @param {String} password Password to decrypt with (optional).
 
-  */
 
- function _decodeAuthenticatedSafe(pfx, authSafe, strict, password) {
 
-   authSafe = asn1.fromDer(authSafe, strict);  /* actually it's BER encoded */
 
-   if(authSafe.tagClass !== asn1.Class.UNIVERSAL ||
 
-      authSafe.type !== asn1.Type.SEQUENCE ||
 
-      authSafe.constructed !== true) {
 
-     throw new Error('PKCS#12 AuthenticatedSafe expected to be a ' +
 
-       'SEQUENCE OF ContentInfo');
 
-   }
 
-   for(var i = 0; i < authSafe.value.length; i++) {
 
-     var contentInfo = authSafe.value[i];
 
-     // validate contentInfo and capture data
 
-     var capture = {};
 
-     var errors = [];
 
-     if(!asn1.validate(contentInfo, contentInfoValidator, capture, errors)) {
 
-       var error = new Error('Cannot read ContentInfo.');
 
-       error.errors = errors;
 
-       throw error;
 
-     }
 
-     var obj = {
 
-       encrypted: false
 
-     };
 
-     var safeContents = null;
 
-     var data = capture.content.value[0];
 
-     switch(asn1.derToOid(capture.contentType)) {
 
-     case pki.oids.data:
 
-       if(data.tagClass !== asn1.Class.UNIVERSAL ||
 
-          data.type !== asn1.Type.OCTETSTRING) {
 
-         throw new Error('PKCS#12 SafeContents Data is not an OCTET STRING.');
 
-       }
 
-       safeContents = _decodePkcs7Data(data).value;
 
-       break;
 
-     case pki.oids.encryptedData:
 
-       safeContents = _decryptSafeContents(data, password);
 
-       obj.encrypted = true;
 
-       break;
 
-     default:
 
-       var error = new Error('Unsupported PKCS#12 contentType.');
 
-       error.contentType = asn1.derToOid(capture.contentType);
 
-       throw error;
 
-     }
 
-     obj.safeBags = _decodeSafeContents(safeContents, strict, password);
 
-     pfx.safeContents.push(obj);
 
-   }
 
- }
 
- /**
 
-  * Decrypt PKCS#7 EncryptedData structure.
 
-  *
 
-  * @param data ASN.1 encoded EncryptedContentInfo object.
 
-  * @param password The user-provided password.
 
-  *
 
-  * @return The decrypted SafeContents (ASN.1 object).
 
-  */
 
- function _decryptSafeContents(data, password) {
 
-   var capture = {};
 
-   var errors = [];
 
-   if(!asn1.validate(
 
-     data, forge.pkcs7.asn1.encryptedDataValidator, capture, errors)) {
 
-     var error = new Error('Cannot read EncryptedContentInfo.');
 
-     error.errors = errors;
 
-     throw error;
 
-   }
 
-   var oid = asn1.derToOid(capture.contentType);
 
-   if(oid !== pki.oids.data) {
 
-     var error = new Error(
 
-       'PKCS#12 EncryptedContentInfo ContentType is not Data.');
 
-     error.oid = oid;
 
-     throw error;
 
-   }
 
-   // get cipher
 
-   oid = asn1.derToOid(capture.encAlgorithm);
 
-   var cipher = pki.pbe.getCipher(oid, capture.encParameter, password);
 
-   // get encrypted data
 
-   var encryptedContentAsn1 = _decodePkcs7Data(capture.encryptedContentAsn1);
 
-   var encrypted = forge.util.createBuffer(encryptedContentAsn1.value);
 
-   cipher.update(encrypted);
 
-   if(!cipher.finish()) {
 
-     throw new Error('Failed to decrypt PKCS#12 SafeContents.');
 
-   }
 
-   return cipher.output.getBytes();
 
- }
 
- /**
 
-  * Decode PKCS#12 SafeContents (BER-encoded) into array of Bag objects.
 
-  *
 
-  * The safeContents is a BER-encoded SEQUENCE OF SafeBag.
 
-  *
 
-  * @param {String} safeContents BER-encoded safeContents.
 
-  * @param strict true to use strict DER decoding, false not to.
 
-  * @param {String} password Password to decrypt with (optional).
 
-  *
 
-  * @return {Array} Array of Bag objects.
 
-  */
 
- function _decodeSafeContents(safeContents, strict, password) {
 
-   // if strict and no safe contents, return empty safes
 
-   if(!strict && safeContents.length === 0) {
 
-     return [];
 
-   }
 
-   // actually it's BER-encoded
 
-   safeContents = asn1.fromDer(safeContents, strict);
 
-   if(safeContents.tagClass !== asn1.Class.UNIVERSAL ||
 
-     safeContents.type !== asn1.Type.SEQUENCE ||
 
-     safeContents.constructed !== true) {
 
-     throw new Error(
 
-       'PKCS#12 SafeContents expected to be a SEQUENCE OF SafeBag.');
 
-   }
 
-   var res = [];
 
-   for(var i = 0; i < safeContents.value.length; i++) {
 
-     var safeBag = safeContents.value[i];
 
-     // validate SafeBag and capture data
 
-     var capture = {};
 
-     var errors = [];
 
-     if(!asn1.validate(safeBag, safeBagValidator, capture, errors)) {
 
-       var error = new Error('Cannot read SafeBag.');
 
-       error.errors = errors;
 
-       throw error;
 
-     }
 
-     /* Create bag object and push to result array. */
 
-     var bag = {
 
-       type: asn1.derToOid(capture.bagId),
 
-       attributes: _decodeBagAttributes(capture.bagAttributes)
 
-     };
 
-     res.push(bag);
 
-     var validator, decoder;
 
-     var bagAsn1 = capture.bagValue.value[0];
 
-     switch(bag.type) {
 
-       case pki.oids.pkcs8ShroudedKeyBag:
 
-         /* bagAsn1 has a EncryptedPrivateKeyInfo, which we need to decrypt.
 
-            Afterwards we can handle it like a keyBag,
 
-            which is a PrivateKeyInfo. */
 
-         bagAsn1 = pki.decryptPrivateKeyInfo(bagAsn1, password);
 
-         if(bagAsn1 === null) {
 
-           throw new Error(
 
-             'Unable to decrypt PKCS#8 ShroudedKeyBag, wrong password?');
 
-         }
 
-         /* fall through */
 
-       case pki.oids.keyBag:
 
-         /* A PKCS#12 keyBag is a simple PrivateKeyInfo as understood by our
 
-            PKI module, hence we don't have to do validation/capturing here,
 
-            just pass what we already got. */
 
-         try {
 
-           bag.key = pki.privateKeyFromAsn1(bagAsn1);
 
-         } catch(e) {
 
-           // ignore unknown key type, pass asn1 value
 
-           bag.key = null;
 
-           bag.asn1 = bagAsn1;
 
-         }
 
-         continue;  /* Nothing more to do. */
 
-       case pki.oids.certBag:
 
-         /* A PKCS#12 certBag can wrap both X.509 and sdsi certificates.
 
-            Therefore put the SafeBag content through another validator to
 
-            capture the fields.  Afterwards check & store the results. */
 
-         validator = certBagValidator;
 
-         decoder = function() {
 
-           if(asn1.derToOid(capture.certId) !== pki.oids.x509Certificate) {
 
-             var error = new Error(
 
-               'Unsupported certificate type, only X.509 supported.');
 
-             error.oid = asn1.derToOid(capture.certId);
 
-             throw error;
 
-           }
 
-           // true=produce cert hash
 
-           var certAsn1 = asn1.fromDer(capture.cert, strict);
 
-           try {
 
-             bag.cert = pki.certificateFromAsn1(certAsn1, true);
 
-           } catch(e) {
 
-             // ignore unknown cert type, pass asn1 value
 
-             bag.cert = null;
 
-             bag.asn1 = certAsn1;
 
-           }
 
-         };
 
-         break;
 
-       default:
 
-         var error = new Error('Unsupported PKCS#12 SafeBag type.');
 
-         error.oid = bag.type;
 
-         throw error;
 
-     }
 
-     /* Validate SafeBag value (i.e. CertBag, etc.) and capture data if needed. */
 
-     if(validator !== undefined &&
 
-        !asn1.validate(bagAsn1, validator, capture, errors)) {
 
-       var error = new Error('Cannot read PKCS#12 ' + validator.name);
 
-       error.errors = errors;
 
-       throw error;
 
-     }
 
-     /* Call decoder function from above to store the results. */
 
-     decoder();
 
-   }
 
-   return res;
 
- }
 
- /**
 
-  * Decode PKCS#12 SET OF PKCS12Attribute into JavaScript object.
 
-  *
 
-  * @param attributes SET OF PKCS12Attribute (ASN.1 object).
 
-  *
 
-  * @return the decoded attributes.
 
-  */
 
- function _decodeBagAttributes(attributes) {
 
-   var decodedAttrs = {};
 
-   if(attributes !== undefined) {
 
-     for(var i = 0; i < attributes.length; ++i) {
 
-       var capture = {};
 
-       var errors = [];
 
-       if(!asn1.validate(attributes[i], attributeValidator, capture, errors)) {
 
-         var error = new Error('Cannot read PKCS#12 BagAttribute.');
 
-         error.errors = errors;
 
-         throw error;
 
-       }
 
-       var oid = asn1.derToOid(capture.oid);
 
-       if(pki.oids[oid] === undefined) {
 
-         // unsupported attribute type, ignore.
 
-         continue;
 
-       }
 
-       decodedAttrs[pki.oids[oid]] = [];
 
-       for(var j = 0; j < capture.values.length; ++j) {
 
-         decodedAttrs[pki.oids[oid]].push(capture.values[j].value);
 
-       }
 
-     }
 
-   }
 
-   return decodedAttrs;
 
- }
 
- /**
 
-  * Wraps a private key and certificate in a PKCS#12 PFX wrapper. If a
 
-  * password is provided then the private key will be encrypted.
 
-  *
 
-  * An entire certificate chain may also be included. To do this, pass
 
-  * an array for the "cert" parameter where the first certificate is
 
-  * the one that is paired with the private key and each subsequent one
 
-  * verifies the previous one. The certificates may be in PEM format or
 
-  * have been already parsed by Forge.
 
-  *
 
-  * @todo implement password-based-encryption for the whole package
 
-  *
 
-  * @param key the private key.
 
-  * @param cert the certificate (may be an array of certificates in order
 
-  *          to specify a certificate chain).
 
-  * @param password the password to use, null for none.
 
-  * @param options:
 
-  *          algorithm the encryption algorithm to use
 
-  *            ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.
 
-  *          count the iteration count to use.
 
-  *          saltSize the salt size to use.
 
-  *          useMac true to include a MAC, false not to, defaults to true.
 
-  *          localKeyId the local key ID to use, in hex.
 
-  *          friendlyName the friendly name to use.
 
-  *          generateLocalKeyId true to generate a random local key ID,
 
-  *            false not to, defaults to true.
 
-  *
 
-  * @return the PKCS#12 PFX ASN.1 object.
 
-  */
 
- p12.toPkcs12Asn1 = function(key, cert, password, options) {
 
-   // set default options
 
-   options = options || {};
 
-   options.saltSize = options.saltSize || 8;
 
-   options.count = options.count || 2048;
 
-   options.algorithm = options.algorithm || options.encAlgorithm || 'aes128';
 
-   if(!('useMac' in options)) {
 
-     options.useMac = true;
 
-   }
 
-   if(!('localKeyId' in options)) {
 
-     options.localKeyId = null;
 
-   }
 
-   if(!('generateLocalKeyId' in options)) {
 
-     options.generateLocalKeyId = true;
 
-   }
 
-   var localKeyId = options.localKeyId;
 
-   var bagAttrs;
 
-   if(localKeyId !== null) {
 
-     localKeyId = forge.util.hexToBytes(localKeyId);
 
-   } else if(options.generateLocalKeyId) {
 
-     // use SHA-1 of paired cert, if available
 
-     if(cert) {
 
-       var pairedCert = forge.util.isArray(cert) ? cert[0] : cert;
 
-       if(typeof pairedCert === 'string') {
 
-         pairedCert = pki.certificateFromPem(pairedCert);
 
-       }
 
-       var sha1 = forge.md.sha1.create();
 
-       sha1.update(asn1.toDer(pki.certificateToAsn1(pairedCert)).getBytes());
 
-       localKeyId = sha1.digest().getBytes();
 
-     } else {
 
-       // FIXME: consider using SHA-1 of public key (which can be generated
 
-       // from private key components), see: cert.generateSubjectKeyIdentifier
 
-       // generate random bytes
 
-       localKeyId = forge.random.getBytes(20);
 
-     }
 
-   }
 
-   var attrs = [];
 
-   if(localKeyId !== null) {
 
-     attrs.push(
 
-       // localKeyID
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-         // attrId
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-           asn1.oidToDer(pki.oids.localKeyId).getBytes()),
 
-         // attrValues
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
 
-           asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
 
-             localKeyId)
 
-         ])
 
-       ]));
 
-   }
 
-   if('friendlyName' in options) {
 
-     attrs.push(
 
-       // friendlyName
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-         // attrId
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-           asn1.oidToDer(pki.oids.friendlyName).getBytes()),
 
-         // attrValues
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
 
-           asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BMPSTRING, false,
 
-             options.friendlyName)
 
-         ])
 
-       ]));
 
-   }
 
-   if(attrs.length > 0) {
 
-     bagAttrs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs);
 
-   }
 
-   // collect contents for AuthenticatedSafe
 
-   var contents = [];
 
-   // create safe bag(s) for certificate chain
 
-   var chain = [];
 
-   if(cert !== null) {
 
-     if(forge.util.isArray(cert)) {
 
-       chain = cert;
 
-     } else {
 
-       chain = [cert];
 
-     }
 
-   }
 
-   var certSafeBags = [];
 
-   for(var i = 0; i < chain.length; ++i) {
 
-     // convert cert from PEM as necessary
 
-     cert = chain[i];
 
-     if(typeof cert === 'string') {
 
-       cert = pki.certificateFromPem(cert);
 
-     }
 
-     // SafeBag
 
-     var certBagAttrs = (i === 0) ? bagAttrs : undefined;
 
-     var certAsn1 = pki.certificateToAsn1(cert);
 
-     var certSafeBag =
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-         // bagId
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-           asn1.oidToDer(pki.oids.certBag).getBytes()),
 
-         // bagValue
 
-         asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
 
-           // CertBag
 
-           asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-             // certId
 
-             asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-               asn1.oidToDer(pki.oids.x509Certificate).getBytes()),
 
-             // certValue (x509Certificate)
 
-             asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
 
-               asn1.create(
 
-                 asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
 
-                 asn1.toDer(certAsn1).getBytes())
 
-             ])])]),
 
-         // bagAttributes (OPTIONAL)
 
-         certBagAttrs
 
-       ]);
 
-     certSafeBags.push(certSafeBag);
 
-   }
 
-   if(certSafeBags.length > 0) {
 
-     // SafeContents
 
-     var certSafeContents = asn1.create(
 
-       asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, certSafeBags);
 
-     // ContentInfo
 
-     var certCI =
 
-       // PKCS#7 ContentInfo
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-         // contentType
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-           // OID for the content type is 'data'
 
-           asn1.oidToDer(pki.oids.data).getBytes()),
 
-         // content
 
-         asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
 
-           asn1.create(
 
-             asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
 
-             asn1.toDer(certSafeContents).getBytes())
 
-         ])
 
-       ]);
 
-     contents.push(certCI);
 
-   }
 
-   // create safe contents for private key
 
-   var keyBag = null;
 
-   if(key !== null) {
 
-     // SafeBag
 
-     var pkAsn1 = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(key));
 
-     if(password === null) {
 
-       // no encryption
 
-       keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-         // bagId
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-           asn1.oidToDer(pki.oids.keyBag).getBytes()),
 
-         // bagValue
 
-         asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
 
-           // PrivateKeyInfo
 
-           pkAsn1
 
-         ]),
 
-         // bagAttributes (OPTIONAL)
 
-         bagAttrs
 
-       ]);
 
-     } else {
 
-       // encrypted PrivateKeyInfo
 
-       keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-         // bagId
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-           asn1.oidToDer(pki.oids.pkcs8ShroudedKeyBag).getBytes()),
 
-         // bagValue
 
-         asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
 
-           // EncryptedPrivateKeyInfo
 
-           pki.encryptPrivateKeyInfo(pkAsn1, password, options)
 
-         ]),
 
-         // bagAttributes (OPTIONAL)
 
-         bagAttrs
 
-       ]);
 
-     }
 
-     // SafeContents
 
-     var keySafeContents =
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [keyBag]);
 
-     // ContentInfo
 
-     var keyCI =
 
-       // PKCS#7 ContentInfo
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-         // contentType
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-           // OID for the content type is 'data'
 
-           asn1.oidToDer(pki.oids.data).getBytes()),
 
-         // content
 
-         asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
 
-           asn1.create(
 
-             asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
 
-             asn1.toDer(keySafeContents).getBytes())
 
-         ])
 
-       ]);
 
-     contents.push(keyCI);
 
-   }
 
-   // create AuthenticatedSafe by stringing together the contents
 
-   var safe = asn1.create(
 
-     asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, contents);
 
-   var macData;
 
-   if(options.useMac) {
 
-     // MacData
 
-     var sha1 = forge.md.sha1.create();
 
-     var macSalt = new forge.util.ByteBuffer(
 
-       forge.random.getBytes(options.saltSize));
 
-     var count = options.count;
 
-     // 160-bit key
 
-     var key = p12.generateKey(password, macSalt, 3, count, 20);
 
-     var mac = forge.hmac.create();
 
-     mac.start(sha1, key);
 
-     mac.update(asn1.toDer(safe).getBytes());
 
-     var macValue = mac.getMac();
 
-     macData = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-       // mac DigestInfo
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-         // digestAlgorithm
 
-         asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-           // algorithm = SHA-1
 
-           asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-             asn1.oidToDer(pki.oids.sha1).getBytes()),
 
-           // parameters = Null
 
-           asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
 
-         ]),
 
-         // digest
 
-         asn1.create(
 
-           asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING,
 
-           false, macValue.getBytes())
 
-       ]),
 
-       // macSalt OCTET STRING
 
-       asn1.create(
 
-         asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, macSalt.getBytes()),
 
-       // iterations INTEGER (XXX: Only support count < 65536)
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
 
-         asn1.integerToDer(count).getBytes()
 
-       )
 
-     ]);
 
-   }
 
-   // PFX
 
-   return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-     // version (3)
 
-     asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
 
-       asn1.integerToDer(3).getBytes()),
 
-     // PKCS#7 ContentInfo
 
-     asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
 
-       // contentType
 
-       asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
 
-         // OID for the content type is 'data'
 
-         asn1.oidToDer(pki.oids.data).getBytes()),
 
-       // content
 
-       asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
 
-         asn1.create(
 
-           asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
 
-           asn1.toDer(safe).getBytes())
 
-       ])
 
-     ]),
 
-     macData
 
-   ]);
 
- };
 
- /**
 
-  * Derives a PKCS#12 key.
 
-  *
 
-  * @param password the password to derive the key material from, null or
 
-  *          undefined for none.
 
-  * @param salt the salt, as a ByteBuffer, to use.
 
-  * @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).
 
-  * @param iter the iteration count.
 
-  * @param n the number of bytes to derive from the password.
 
-  * @param md the message digest to use, defaults to SHA-1.
 
-  *
 
-  * @return a ByteBuffer with the bytes derived from the password.
 
-  */
 
- p12.generateKey = forge.pbe.generatePkcs12Key;
 
 
  |