You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1075 lines
				
				33 KiB
			
		
		
			
		
	
	
					1075 lines
				
				33 KiB
			| 
								 
											2 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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;
							 |