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.
		
		
		
		
		
			
		
			
				
					
					
						
							3333 lines
						
					
					
						
							103 KiB
						
					
					
				
			
		
		
	
	
							3333 lines
						
					
					
						
							103 KiB
						
					
					
				/** | 
						|
 * Javascript implementation of X.509 and related components (such as | 
						|
 * Certification Signing Requests) of a Public Key Infrastructure. | 
						|
 * | 
						|
 * @author Dave Longley | 
						|
 * | 
						|
 * Copyright (c) 2010-2014 Digital Bazaar, Inc. | 
						|
 * | 
						|
 * The ASN.1 representation of an X.509v3 certificate is as follows | 
						|
 * (see RFC 2459): | 
						|
 * | 
						|
 * Certificate ::= SEQUENCE { | 
						|
 *   tbsCertificate       TBSCertificate, | 
						|
 *   signatureAlgorithm   AlgorithmIdentifier, | 
						|
 *   signatureValue       BIT STRING | 
						|
 * } | 
						|
 * | 
						|
 * TBSCertificate ::= SEQUENCE { | 
						|
 *   version         [0]  EXPLICIT Version DEFAULT v1, | 
						|
 *   serialNumber         CertificateSerialNumber, | 
						|
 *   signature            AlgorithmIdentifier, | 
						|
 *   issuer               Name, | 
						|
 *   validity             Validity, | 
						|
 *   subject              Name, | 
						|
 *   subjectPublicKeyInfo SubjectPublicKeyInfo, | 
						|
 *   issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL, | 
						|
 *                        -- If present, version shall be v2 or v3 | 
						|
 *   subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL, | 
						|
 *                        -- If present, version shall be v2 or v3 | 
						|
 *   extensions      [3]  EXPLICIT Extensions OPTIONAL | 
						|
 *                        -- If present, version shall be v3 | 
						|
 * } | 
						|
 * | 
						|
 * Version ::= INTEGER  { v1(0), v2(1), v3(2) } | 
						|
 * | 
						|
 * CertificateSerialNumber ::= INTEGER | 
						|
 * | 
						|
 * Name ::= CHOICE { | 
						|
 *   // only one possible choice for now | 
						|
 *   RDNSequence | 
						|
 * } | 
						|
 * | 
						|
 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName | 
						|
 * | 
						|
 * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue | 
						|
 * | 
						|
 * AttributeTypeAndValue ::= SEQUENCE { | 
						|
 *   type     AttributeType, | 
						|
 *   value    AttributeValue | 
						|
 * } | 
						|
 * AttributeType ::= OBJECT IDENTIFIER | 
						|
 * AttributeValue ::= ANY DEFINED BY AttributeType | 
						|
 * | 
						|
 * Validity ::= SEQUENCE { | 
						|
 *   notBefore      Time, | 
						|
 *   notAfter       Time | 
						|
 * } | 
						|
 * | 
						|
 * Time ::= CHOICE { | 
						|
 *   utcTime        UTCTime, | 
						|
 *   generalTime    GeneralizedTime | 
						|
 * } | 
						|
 * | 
						|
 * UniqueIdentifier ::= BIT STRING | 
						|
 * | 
						|
 * SubjectPublicKeyInfo ::= SEQUENCE { | 
						|
 *   algorithm            AlgorithmIdentifier, | 
						|
 *   subjectPublicKey     BIT STRING | 
						|
 * } | 
						|
 * | 
						|
 * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension | 
						|
 * | 
						|
 * Extension ::= SEQUENCE { | 
						|
 *   extnID      OBJECT IDENTIFIER, | 
						|
 *   critical    BOOLEAN DEFAULT FALSE, | 
						|
 *   extnValue   OCTET STRING | 
						|
 * } | 
						|
 * | 
						|
 * The only key algorithm currently supported for PKI is RSA. | 
						|
 * | 
						|
 * RSASSA-PSS signatures are described in RFC 3447 and RFC 4055. | 
						|
 * | 
						|
 * PKCS#10 v1.7 describes certificate signing requests: | 
						|
 * | 
						|
 * CertificationRequestInfo: | 
						|
 * | 
						|
 * CertificationRequestInfo ::= SEQUENCE { | 
						|
 *   version       INTEGER { v1(0) } (v1,...), | 
						|
 *   subject       Name, | 
						|
 *   subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, | 
						|
 *   attributes    [0] Attributes{{ CRIAttributes }} | 
						|
 * } | 
						|
 * | 
						|
 * Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }} | 
						|
 * | 
						|
 * CRIAttributes  ATTRIBUTE  ::= { | 
						|
 *   ... -- add any locally defined attributes here -- } | 
						|
 * | 
						|
 * Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE { | 
						|
 *   type   ATTRIBUTE.&id({IOSet}), | 
						|
 *   values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type}) | 
						|
 * } | 
						|
 * | 
						|
 * CertificationRequest ::= SEQUENCE { | 
						|
 *   certificationRequestInfo CertificationRequestInfo, | 
						|
 *   signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, | 
						|
 *   signature          BIT STRING | 
						|
 * } | 
						|
 */ | 
						|
var forge = require('./forge'); | 
						|
require('./aes'); | 
						|
require('./asn1'); | 
						|
require('./des'); | 
						|
require('./md'); | 
						|
require('./mgf'); | 
						|
require('./oids'); | 
						|
require('./pem'); | 
						|
require('./pss'); | 
						|
require('./rsa'); | 
						|
require('./util'); | 
						|
 | 
						|
// shortcut for asn.1 API | 
						|
var asn1 = forge.asn1; | 
						|
 | 
						|
/* Public Key Infrastructure (PKI) implementation. */ | 
						|
var pki = module.exports = forge.pki = forge.pki || {}; | 
						|
var oids = pki.oids; | 
						|
 | 
						|
// short name OID mappings | 
						|
var _shortNames = {}; | 
						|
_shortNames['CN'] = oids['commonName']; | 
						|
_shortNames['commonName'] = 'CN'; | 
						|
_shortNames['C'] = oids['countryName']; | 
						|
_shortNames['countryName'] = 'C'; | 
						|
_shortNames['L'] = oids['localityName']; | 
						|
_shortNames['localityName'] = 'L'; | 
						|
_shortNames['ST'] = oids['stateOrProvinceName']; | 
						|
_shortNames['stateOrProvinceName'] = 'ST'; | 
						|
_shortNames['O'] = oids['organizationName']; | 
						|
_shortNames['organizationName'] = 'O'; | 
						|
_shortNames['OU'] = oids['organizationalUnitName']; | 
						|
_shortNames['organizationalUnitName'] = 'OU'; | 
						|
_shortNames['E'] = oids['emailAddress']; | 
						|
_shortNames['emailAddress'] = 'E'; | 
						|
 | 
						|
// validator for an SubjectPublicKeyInfo structure | 
						|
// Note: Currently only works with an RSA public key | 
						|
var publicKeyValidator = forge.pki.rsa.publicKeyValidator; | 
						|
 | 
						|
// validator for an X.509v3 certificate | 
						|
var x509CertificateValidator = { | 
						|
  name: 'Certificate', | 
						|
  tagClass: asn1.Class.UNIVERSAL, | 
						|
  type: asn1.Type.SEQUENCE, | 
						|
  constructed: true, | 
						|
  value: [{ | 
						|
    name: 'Certificate.TBSCertificate', | 
						|
    tagClass: asn1.Class.UNIVERSAL, | 
						|
    type: asn1.Type.SEQUENCE, | 
						|
    constructed: true, | 
						|
    captureAsn1: 'tbsCertificate', | 
						|
    value: [{ | 
						|
      name: 'Certificate.TBSCertificate.version', | 
						|
      tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
      type: 0, | 
						|
      constructed: true, | 
						|
      optional: true, | 
						|
      value: [{ | 
						|
        name: 'Certificate.TBSCertificate.version.integer', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.INTEGER, | 
						|
        constructed: false, | 
						|
        capture: 'certVersion' | 
						|
      }] | 
						|
    }, { | 
						|
      name: 'Certificate.TBSCertificate.serialNumber', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.INTEGER, | 
						|
      constructed: false, | 
						|
      capture: 'certSerialNumber' | 
						|
    }, { | 
						|
      name: 'Certificate.TBSCertificate.signature', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.SEQUENCE, | 
						|
      constructed: true, | 
						|
      value: [{ | 
						|
        name: 'Certificate.TBSCertificate.signature.algorithm', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.OID, | 
						|
        constructed: false, | 
						|
        capture: 'certinfoSignatureOid' | 
						|
      }, { | 
						|
        name: 'Certificate.TBSCertificate.signature.parameters', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        optional: true, | 
						|
        captureAsn1: 'certinfoSignatureParams' | 
						|
      }] | 
						|
    }, { | 
						|
      name: 'Certificate.TBSCertificate.issuer', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.SEQUENCE, | 
						|
      constructed: true, | 
						|
      captureAsn1: 'certIssuer' | 
						|
    }, { | 
						|
      name: 'Certificate.TBSCertificate.validity', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.SEQUENCE, | 
						|
      constructed: true, | 
						|
      // Note: UTC and generalized times may both appear so the capture | 
						|
      // names are based on their detected order, the names used below | 
						|
      // are only for the common case, which validity time really means | 
						|
      // "notBefore" and which means "notAfter" will be determined by order | 
						|
      value: [{ | 
						|
        // notBefore (Time) (UTC time case) | 
						|
        name: 'Certificate.TBSCertificate.validity.notBefore (utc)', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.UTCTIME, | 
						|
        constructed: false, | 
						|
        optional: true, | 
						|
        capture: 'certValidity1UTCTime' | 
						|
      }, { | 
						|
        // notBefore (Time) (generalized time case) | 
						|
        name: 'Certificate.TBSCertificate.validity.notBefore (generalized)', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.GENERALIZEDTIME, | 
						|
        constructed: false, | 
						|
        optional: true, | 
						|
        capture: 'certValidity2GeneralizedTime' | 
						|
      }, { | 
						|
        // notAfter (Time) (only UTC time is supported) | 
						|
        name: 'Certificate.TBSCertificate.validity.notAfter (utc)', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.UTCTIME, | 
						|
        constructed: false, | 
						|
        optional: true, | 
						|
        capture: 'certValidity3UTCTime' | 
						|
      }, { | 
						|
        // notAfter (Time) (only UTC time is supported) | 
						|
        name: 'Certificate.TBSCertificate.validity.notAfter (generalized)', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.GENERALIZEDTIME, | 
						|
        constructed: false, | 
						|
        optional: true, | 
						|
        capture: 'certValidity4GeneralizedTime' | 
						|
      }] | 
						|
    }, { | 
						|
      // Name (subject) (RDNSequence) | 
						|
      name: 'Certificate.TBSCertificate.subject', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.SEQUENCE, | 
						|
      constructed: true, | 
						|
      captureAsn1: 'certSubject' | 
						|
    }, | 
						|
    // SubjectPublicKeyInfo | 
						|
    publicKeyValidator, | 
						|
    { | 
						|
      // issuerUniqueID (optional) | 
						|
      name: 'Certificate.TBSCertificate.issuerUniqueID', | 
						|
      tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
      type: 1, | 
						|
      constructed: true, | 
						|
      optional: true, | 
						|
      value: [{ | 
						|
        name: 'Certificate.TBSCertificate.issuerUniqueID.id', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.BITSTRING, | 
						|
        constructed: false, | 
						|
        // TODO: support arbitrary bit length ids | 
						|
        captureBitStringValue: 'certIssuerUniqueId' | 
						|
      }] | 
						|
    }, { | 
						|
      // subjectUniqueID (optional) | 
						|
      name: 'Certificate.TBSCertificate.subjectUniqueID', | 
						|
      tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
      type: 2, | 
						|
      constructed: true, | 
						|
      optional: true, | 
						|
      value: [{ | 
						|
        name: 'Certificate.TBSCertificate.subjectUniqueID.id', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.BITSTRING, | 
						|
        constructed: false, | 
						|
        // TODO: support arbitrary bit length ids | 
						|
        captureBitStringValue: 'certSubjectUniqueId' | 
						|
      }] | 
						|
    }, { | 
						|
      // Extensions (optional) | 
						|
      name: 'Certificate.TBSCertificate.extensions', | 
						|
      tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
      type: 3, | 
						|
      constructed: true, | 
						|
      captureAsn1: 'certExtensions', | 
						|
      optional: true | 
						|
    }] | 
						|
  }, { | 
						|
    // AlgorithmIdentifier (signature algorithm) | 
						|
    name: 'Certificate.signatureAlgorithm', | 
						|
    tagClass: asn1.Class.UNIVERSAL, | 
						|
    type: asn1.Type.SEQUENCE, | 
						|
    constructed: true, | 
						|
    value: [{ | 
						|
      // algorithm | 
						|
      name: 'Certificate.signatureAlgorithm.algorithm', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.OID, | 
						|
      constructed: false, | 
						|
      capture: 'certSignatureOid' | 
						|
    }, { | 
						|
      name: 'Certificate.TBSCertificate.signature.parameters', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      optional: true, | 
						|
      captureAsn1: 'certSignatureParams' | 
						|
    }] | 
						|
  }, { | 
						|
    // SignatureValue | 
						|
    name: 'Certificate.signatureValue', | 
						|
    tagClass: asn1.Class.UNIVERSAL, | 
						|
    type: asn1.Type.BITSTRING, | 
						|
    constructed: false, | 
						|
    captureBitStringValue: 'certSignature' | 
						|
  }] | 
						|
}; | 
						|
 | 
						|
var rsassaPssParameterValidator = { | 
						|
  name: 'rsapss', | 
						|
  tagClass: asn1.Class.UNIVERSAL, | 
						|
  type: asn1.Type.SEQUENCE, | 
						|
  constructed: true, | 
						|
  value: [{ | 
						|
    name: 'rsapss.hashAlgorithm', | 
						|
    tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
    type: 0, | 
						|
    constructed: true, | 
						|
    value: [{ | 
						|
      name: 'rsapss.hashAlgorithm.AlgorithmIdentifier', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Class.SEQUENCE, | 
						|
      constructed: true, | 
						|
      optional: true, | 
						|
      value: [{ | 
						|
        name: 'rsapss.hashAlgorithm.AlgorithmIdentifier.algorithm', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.OID, | 
						|
        constructed: false, | 
						|
        capture: 'hashOid' | 
						|
        /* parameter block omitted, for SHA1 NULL anyhow. */ | 
						|
      }] | 
						|
    }] | 
						|
  }, { | 
						|
    name: 'rsapss.maskGenAlgorithm', | 
						|
    tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
    type: 1, | 
						|
    constructed: true, | 
						|
    value: [{ | 
						|
      name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Class.SEQUENCE, | 
						|
      constructed: true, | 
						|
      optional: true, | 
						|
      value: [{ | 
						|
        name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.algorithm', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.OID, | 
						|
        constructed: false, | 
						|
        capture: 'maskGenOid' | 
						|
      }, { | 
						|
        name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.params', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.SEQUENCE, | 
						|
        constructed: true, | 
						|
        value: [{ | 
						|
          name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.params.algorithm', | 
						|
          tagClass: asn1.Class.UNIVERSAL, | 
						|
          type: asn1.Type.OID, | 
						|
          constructed: false, | 
						|
          capture: 'maskGenHashOid' | 
						|
          /* parameter block omitted, for SHA1 NULL anyhow. */ | 
						|
        }] | 
						|
      }] | 
						|
    }] | 
						|
  }, { | 
						|
    name: 'rsapss.saltLength', | 
						|
    tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
    type: 2, | 
						|
    optional: true, | 
						|
    value: [{ | 
						|
      name: 'rsapss.saltLength.saltLength', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Class.INTEGER, | 
						|
      constructed: false, | 
						|
      capture: 'saltLength' | 
						|
    }] | 
						|
  }, { | 
						|
    name: 'rsapss.trailerField', | 
						|
    tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
    type: 3, | 
						|
    optional: true, | 
						|
    value: [{ | 
						|
      name: 'rsapss.trailer.trailer', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Class.INTEGER, | 
						|
      constructed: false, | 
						|
      capture: 'trailer' | 
						|
    }] | 
						|
  }] | 
						|
}; | 
						|
 | 
						|
// validator for a CertificationRequestInfo structure | 
						|
var certificationRequestInfoValidator = { | 
						|
  name: 'CertificationRequestInfo', | 
						|
  tagClass: asn1.Class.UNIVERSAL, | 
						|
  type: asn1.Type.SEQUENCE, | 
						|
  constructed: true, | 
						|
  captureAsn1: 'certificationRequestInfo', | 
						|
  value: [{ | 
						|
    name: 'CertificationRequestInfo.integer', | 
						|
    tagClass: asn1.Class.UNIVERSAL, | 
						|
    type: asn1.Type.INTEGER, | 
						|
    constructed: false, | 
						|
    capture: 'certificationRequestInfoVersion' | 
						|
  }, { | 
						|
    // Name (subject) (RDNSequence) | 
						|
    name: 'CertificationRequestInfo.subject', | 
						|
    tagClass: asn1.Class.UNIVERSAL, | 
						|
    type: asn1.Type.SEQUENCE, | 
						|
    constructed: true, | 
						|
    captureAsn1: 'certificationRequestInfoSubject' | 
						|
  }, | 
						|
  // SubjectPublicKeyInfo | 
						|
  publicKeyValidator, | 
						|
  { | 
						|
    name: 'CertificationRequestInfo.attributes', | 
						|
    tagClass: asn1.Class.CONTEXT_SPECIFIC, | 
						|
    type: 0, | 
						|
    constructed: true, | 
						|
    optional: true, | 
						|
    capture: 'certificationRequestInfoAttributes', | 
						|
    value: [{ | 
						|
      name: 'CertificationRequestInfo.attributes', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.SEQUENCE, | 
						|
      constructed: true, | 
						|
      value: [{ | 
						|
        name: 'CertificationRequestInfo.attributes.type', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.OID, | 
						|
        constructed: false | 
						|
      }, { | 
						|
        name: 'CertificationRequestInfo.attributes.value', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.SET, | 
						|
        constructed: true | 
						|
      }] | 
						|
    }] | 
						|
  }] | 
						|
}; | 
						|
 | 
						|
// validator for a CertificationRequest structure | 
						|
var certificationRequestValidator = { | 
						|
  name: 'CertificationRequest', | 
						|
  tagClass: asn1.Class.UNIVERSAL, | 
						|
  type: asn1.Type.SEQUENCE, | 
						|
  constructed: true, | 
						|
  captureAsn1: 'csr', | 
						|
  value: [ | 
						|
    certificationRequestInfoValidator, { | 
						|
      // AlgorithmIdentifier (signature algorithm) | 
						|
      name: 'CertificationRequest.signatureAlgorithm', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.SEQUENCE, | 
						|
      constructed: true, | 
						|
      value: [{ | 
						|
        // algorithm | 
						|
        name: 'CertificationRequest.signatureAlgorithm.algorithm', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        type: asn1.Type.OID, | 
						|
        constructed: false, | 
						|
        capture: 'csrSignatureOid' | 
						|
      }, { | 
						|
        name: 'CertificationRequest.signatureAlgorithm.parameters', | 
						|
        tagClass: asn1.Class.UNIVERSAL, | 
						|
        optional: true, | 
						|
        captureAsn1: 'csrSignatureParams' | 
						|
      }] | 
						|
    }, { | 
						|
      // signature | 
						|
      name: 'CertificationRequest.signature', | 
						|
      tagClass: asn1.Class.UNIVERSAL, | 
						|
      type: asn1.Type.BITSTRING, | 
						|
      constructed: false, | 
						|
      captureBitStringValue: 'csrSignature' | 
						|
    } | 
						|
  ] | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an RDNSequence of ASN.1 DER-encoded RelativeDistinguishedName | 
						|
 * sets into an array with objects that have type and value properties. | 
						|
 * | 
						|
 * @param rdn the RDNSequence to convert. | 
						|
 * @param md a message digest to append type and value to if provided. | 
						|
 */ | 
						|
pki.RDNAttributesAsArray = function(rdn, md) { | 
						|
  var rval = []; | 
						|
 | 
						|
  // each value in 'rdn' in is a SET of RelativeDistinguishedName | 
						|
  var set, attr, obj; | 
						|
  for(var si = 0; si < rdn.value.length; ++si) { | 
						|
    // get the RelativeDistinguishedName set | 
						|
    set = rdn.value[si]; | 
						|
 | 
						|
    // each value in the SET is an AttributeTypeAndValue sequence | 
						|
    // containing first a type (an OID) and second a value (defined by | 
						|
    // the OID) | 
						|
    for(var i = 0; i < set.value.length; ++i) { | 
						|
      obj = {}; | 
						|
      attr = set.value[i]; | 
						|
      obj.type = asn1.derToOid(attr.value[0].value); | 
						|
      obj.value = attr.value[1].value; | 
						|
      obj.valueTagClass = attr.value[1].type; | 
						|
      // if the OID is known, get its name and short name | 
						|
      if(obj.type in oids) { | 
						|
        obj.name = oids[obj.type]; | 
						|
        if(obj.name in _shortNames) { | 
						|
          obj.shortName = _shortNames[obj.name]; | 
						|
        } | 
						|
      } | 
						|
      if(md) { | 
						|
        md.update(obj.type); | 
						|
        md.update(obj.value); | 
						|
      } | 
						|
      rval.push(obj); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return rval; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts ASN.1 CRIAttributes into an array with objects that have type and | 
						|
 * value properties. | 
						|
 * | 
						|
 * @param attributes the CRIAttributes to convert. | 
						|
 */ | 
						|
pki.CRIAttributesAsArray = function(attributes) { | 
						|
  var rval = []; | 
						|
 | 
						|
  // each value in 'attributes' in is a SEQUENCE with an OID and a SET | 
						|
  for(var si = 0; si < attributes.length; ++si) { | 
						|
    // get the attribute sequence | 
						|
    var seq = attributes[si]; | 
						|
 | 
						|
    // each value in the SEQUENCE containing first a type (an OID) and | 
						|
    // second a set of values (defined by the OID) | 
						|
    var type = asn1.derToOid(seq.value[0].value); | 
						|
    var values = seq.value[1].value; | 
						|
    for(var vi = 0; vi < values.length; ++vi) { | 
						|
      var obj = {}; | 
						|
      obj.type = type; | 
						|
      obj.value = values[vi].value; | 
						|
      obj.valueTagClass = values[vi].type; | 
						|
      // if the OID is known, get its name and short name | 
						|
      if(obj.type in oids) { | 
						|
        obj.name = oids[obj.type]; | 
						|
        if(obj.name in _shortNames) { | 
						|
          obj.shortName = _shortNames[obj.name]; | 
						|
        } | 
						|
      } | 
						|
      // parse extensions | 
						|
      if(obj.type === oids.extensionRequest) { | 
						|
        obj.extensions = []; | 
						|
        for(var ei = 0; ei < obj.value.length; ++ei) { | 
						|
          obj.extensions.push(pki.certificateExtensionFromAsn1(obj.value[ei])); | 
						|
        } | 
						|
      } | 
						|
      rval.push(obj); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return rval; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Gets an issuer or subject attribute from its name, type, or short name. | 
						|
 * | 
						|
 * @param obj the issuer or subject object. | 
						|
 * @param options a short name string or an object with: | 
						|
 *          shortName the short name for the attribute. | 
						|
 *          name the name for the attribute. | 
						|
 *          type the type for the attribute. | 
						|
 * | 
						|
 * @return the attribute. | 
						|
 */ | 
						|
function _getAttribute(obj, options) { | 
						|
  if(typeof options === 'string') { | 
						|
    options = {shortName: options}; | 
						|
  } | 
						|
 | 
						|
  var rval = null; | 
						|
  var attr; | 
						|
  for(var i = 0; rval === null && i < obj.attributes.length; ++i) { | 
						|
    attr = obj.attributes[i]; | 
						|
    if(options.type && options.type === attr.type) { | 
						|
      rval = attr; | 
						|
    } else if(options.name && options.name === attr.name) { | 
						|
      rval = attr; | 
						|
    } else if(options.shortName && options.shortName === attr.shortName) { | 
						|
      rval = attr; | 
						|
    } | 
						|
  } | 
						|
  return rval; | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Converts signature parameters from ASN.1 structure. | 
						|
 * | 
						|
 * Currently only RSASSA-PSS supported.  The PKCS#1 v1.5 signature scheme had | 
						|
 * no parameters. | 
						|
 * | 
						|
 * RSASSA-PSS-params  ::=  SEQUENCE  { | 
						|
 *   hashAlgorithm      [0] HashAlgorithm DEFAULT | 
						|
 *                             sha1Identifier, | 
						|
 *   maskGenAlgorithm   [1] MaskGenAlgorithm DEFAULT | 
						|
 *                             mgf1SHA1Identifier, | 
						|
 *   saltLength         [2] INTEGER DEFAULT 20, | 
						|
 *   trailerField       [3] INTEGER DEFAULT 1 | 
						|
 * } | 
						|
 * | 
						|
 * HashAlgorithm  ::=  AlgorithmIdentifier | 
						|
 * | 
						|
 * MaskGenAlgorithm  ::=  AlgorithmIdentifier | 
						|
 * | 
						|
 * AlgorithmIdentifer ::= SEQUENCE { | 
						|
 *   algorithm OBJECT IDENTIFIER, | 
						|
 *   parameters ANY DEFINED BY algorithm OPTIONAL | 
						|
 * } | 
						|
 * | 
						|
 * @param oid The OID specifying the signature algorithm | 
						|
 * @param obj The ASN.1 structure holding the parameters | 
						|
 * @param fillDefaults Whether to use return default values where omitted | 
						|
 * @return signature parameter object | 
						|
 */ | 
						|
var _readSignatureParameters = function(oid, obj, fillDefaults) { | 
						|
  var params = {}; | 
						|
 | 
						|
  if(oid !== oids['RSASSA-PSS']) { | 
						|
    return params; | 
						|
  } | 
						|
 | 
						|
  if(fillDefaults) { | 
						|
    params = { | 
						|
      hash: { | 
						|
        algorithmOid: oids['sha1'] | 
						|
      }, | 
						|
      mgf: { | 
						|
        algorithmOid: oids['mgf1'], | 
						|
        hash: { | 
						|
          algorithmOid: oids['sha1'] | 
						|
        } | 
						|
      }, | 
						|
      saltLength: 20 | 
						|
    }; | 
						|
  } | 
						|
 | 
						|
  var capture = {}; | 
						|
  var errors = []; | 
						|
  if(!asn1.validate(obj, rsassaPssParameterValidator, capture, errors)) { | 
						|
    var error = new Error('Cannot read RSASSA-PSS parameter block.'); | 
						|
    error.errors = errors; | 
						|
    throw error; | 
						|
  } | 
						|
 | 
						|
  if(capture.hashOid !== undefined) { | 
						|
    params.hash = params.hash || {}; | 
						|
    params.hash.algorithmOid = asn1.derToOid(capture.hashOid); | 
						|
  } | 
						|
 | 
						|
  if(capture.maskGenOid !== undefined) { | 
						|
    params.mgf = params.mgf || {}; | 
						|
    params.mgf.algorithmOid = asn1.derToOid(capture.maskGenOid); | 
						|
    params.mgf.hash = params.mgf.hash || {}; | 
						|
    params.mgf.hash.algorithmOid = asn1.derToOid(capture.maskGenHashOid); | 
						|
  } | 
						|
 | 
						|
  if(capture.saltLength !== undefined) { | 
						|
    params.saltLength = capture.saltLength.charCodeAt(0); | 
						|
  } | 
						|
 | 
						|
  return params; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an X.509 certificate from PEM format. | 
						|
 * | 
						|
 * Note: If the certificate is to be verified then compute hash should | 
						|
 * be set to true. This will scan the TBSCertificate part of the ASN.1 | 
						|
 * object while it is converted so it doesn't need to be converted back | 
						|
 * to ASN.1-DER-encoding later. | 
						|
 * | 
						|
 * @param pem the PEM-formatted certificate. | 
						|
 * @param computeHash true to compute the hash for verification. | 
						|
 * @param strict true to be strict when checking ASN.1 value lengths, false to | 
						|
 *          allow truncated values (default: true). | 
						|
 * | 
						|
 * @return the certificate. | 
						|
 */ | 
						|
pki.certificateFromPem = function(pem, computeHash, strict) { | 
						|
  var msg = forge.pem.decode(pem)[0]; | 
						|
 | 
						|
  if(msg.type !== 'CERTIFICATE' && | 
						|
    msg.type !== 'X509 CERTIFICATE' && | 
						|
    msg.type !== 'TRUSTED CERTIFICATE') { | 
						|
    var error = new Error( | 
						|
      'Could not convert certificate from PEM; PEM header type ' + | 
						|
      'is not "CERTIFICATE", "X509 CERTIFICATE", or "TRUSTED CERTIFICATE".'); | 
						|
    error.headerType = msg.type; | 
						|
    throw error; | 
						|
  } | 
						|
  if(msg.procType && msg.procType.type === 'ENCRYPTED') { | 
						|
    throw new Error( | 
						|
      'Could not convert certificate from PEM; PEM is encrypted.'); | 
						|
  } | 
						|
 | 
						|
  // convert DER to ASN.1 object | 
						|
  var obj = asn1.fromDer(msg.body, strict); | 
						|
 | 
						|
  return pki.certificateFromAsn1(obj, computeHash); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an X.509 certificate to PEM format. | 
						|
 * | 
						|
 * @param cert the certificate. | 
						|
 * @param maxline the maximum characters per line, defaults to 64. | 
						|
 * | 
						|
 * @return the PEM-formatted certificate. | 
						|
 */ | 
						|
pki.certificateToPem = function(cert, maxline) { | 
						|
  // convert to ASN.1, then DER, then PEM-encode | 
						|
  var msg = { | 
						|
    type: 'CERTIFICATE', | 
						|
    body: asn1.toDer(pki.certificateToAsn1(cert)).getBytes() | 
						|
  }; | 
						|
  return forge.pem.encode(msg, {maxline: maxline}); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an RSA public key from PEM format. | 
						|
 * | 
						|
 * @param pem the PEM-formatted public key. | 
						|
 * | 
						|
 * @return the public key. | 
						|
 */ | 
						|
pki.publicKeyFromPem = function(pem) { | 
						|
  var msg = forge.pem.decode(pem)[0]; | 
						|
 | 
						|
  if(msg.type !== 'PUBLIC KEY' && msg.type !== 'RSA PUBLIC KEY') { | 
						|
    var error = new Error('Could not convert public key from PEM; PEM header ' + | 
						|
      'type is not "PUBLIC KEY" or "RSA PUBLIC KEY".'); | 
						|
    error.headerType = msg.type; | 
						|
    throw error; | 
						|
  } | 
						|
  if(msg.procType && msg.procType.type === 'ENCRYPTED') { | 
						|
    throw new Error('Could not convert public key from PEM; PEM is encrypted.'); | 
						|
  } | 
						|
 | 
						|
  // convert DER to ASN.1 object | 
						|
  var obj = asn1.fromDer(msg.body); | 
						|
 | 
						|
  return pki.publicKeyFromAsn1(obj); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an RSA public key to PEM format (using a SubjectPublicKeyInfo). | 
						|
 * | 
						|
 * @param key the public key. | 
						|
 * @param maxline the maximum characters per line, defaults to 64. | 
						|
 * | 
						|
 * @return the PEM-formatted public key. | 
						|
 */ | 
						|
pki.publicKeyToPem = function(key, maxline) { | 
						|
  // convert to ASN.1, then DER, then PEM-encode | 
						|
  var msg = { | 
						|
    type: 'PUBLIC KEY', | 
						|
    body: asn1.toDer(pki.publicKeyToAsn1(key)).getBytes() | 
						|
  }; | 
						|
  return forge.pem.encode(msg, {maxline: maxline}); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an RSA public key to PEM format (using an RSAPublicKey). | 
						|
 * | 
						|
 * @param key the public key. | 
						|
 * @param maxline the maximum characters per line, defaults to 64. | 
						|
 * | 
						|
 * @return the PEM-formatted public key. | 
						|
 */ | 
						|
pki.publicKeyToRSAPublicKeyPem = function(key, maxline) { | 
						|
  // convert to ASN.1, then DER, then PEM-encode | 
						|
  var msg = { | 
						|
    type: 'RSA PUBLIC KEY', | 
						|
    body: asn1.toDer(pki.publicKeyToRSAPublicKey(key)).getBytes() | 
						|
  }; | 
						|
  return forge.pem.encode(msg, {maxline: maxline}); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Gets a fingerprint for the given public key. | 
						|
 * | 
						|
 * @param options the options to use. | 
						|
 *          [md] the message digest object to use (defaults to forge.md.sha1). | 
						|
 *          [type] the type of fingerprint, such as 'RSAPublicKey', | 
						|
 *            'SubjectPublicKeyInfo' (defaults to 'RSAPublicKey'). | 
						|
 *          [encoding] an alternative output encoding, such as 'hex' | 
						|
 *            (defaults to none, outputs a byte buffer). | 
						|
 *          [delimiter] the delimiter to use between bytes for 'hex' encoded | 
						|
 *            output, eg: ':' (defaults to none). | 
						|
 * | 
						|
 * @return the fingerprint as a byte buffer or other encoding based on options. | 
						|
 */ | 
						|
pki.getPublicKeyFingerprint = function(key, options) { | 
						|
  options = options || {}; | 
						|
  var md = options.md || forge.md.sha1.create(); | 
						|
  var type = options.type || 'RSAPublicKey'; | 
						|
 | 
						|
  var bytes; | 
						|
  switch(type) { | 
						|
    case 'RSAPublicKey': | 
						|
      bytes = asn1.toDer(pki.publicKeyToRSAPublicKey(key)).getBytes(); | 
						|
      break; | 
						|
    case 'SubjectPublicKeyInfo': | 
						|
      bytes = asn1.toDer(pki.publicKeyToAsn1(key)).getBytes(); | 
						|
      break; | 
						|
    default: | 
						|
      throw new Error('Unknown fingerprint type "' + options.type + '".'); | 
						|
  } | 
						|
 | 
						|
  // hash public key bytes | 
						|
  md.start(); | 
						|
  md.update(bytes); | 
						|
  var digest = md.digest(); | 
						|
  if(options.encoding === 'hex') { | 
						|
    var hex = digest.toHex(); | 
						|
    if(options.delimiter) { | 
						|
      return hex.match(/.{2}/g).join(options.delimiter); | 
						|
    } | 
						|
    return hex; | 
						|
  } else if(options.encoding === 'binary') { | 
						|
    return digest.getBytes(); | 
						|
  } else if(options.encoding) { | 
						|
    throw new Error('Unknown encoding "' + options.encoding + '".'); | 
						|
  } | 
						|
  return digest; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts a PKCS#10 certification request (CSR) from PEM format. | 
						|
 * | 
						|
 * Note: If the certification request is to be verified then compute hash | 
						|
 * should be set to true. This will scan the CertificationRequestInfo part of | 
						|
 * the ASN.1 object while it is converted so it doesn't need to be converted | 
						|
 * back to ASN.1-DER-encoding later. | 
						|
 * | 
						|
 * @param pem the PEM-formatted certificate. | 
						|
 * @param computeHash true to compute the hash for verification. | 
						|
 * @param strict true to be strict when checking ASN.1 value lengths, false to | 
						|
 *          allow truncated values (default: true). | 
						|
 * | 
						|
 * @return the certification request (CSR). | 
						|
 */ | 
						|
pki.certificationRequestFromPem = function(pem, computeHash, strict) { | 
						|
  var msg = forge.pem.decode(pem)[0]; | 
						|
 | 
						|
  if(msg.type !== 'CERTIFICATE REQUEST') { | 
						|
    var error = new Error('Could not convert certification request from PEM; ' + | 
						|
      'PEM header type is not "CERTIFICATE REQUEST".'); | 
						|
    error.headerType = msg.type; | 
						|
    throw error; | 
						|
  } | 
						|
  if(msg.procType && msg.procType.type === 'ENCRYPTED') { | 
						|
    throw new Error('Could not convert certification request from PEM; ' + | 
						|
      'PEM is encrypted.'); | 
						|
  } | 
						|
 | 
						|
  // convert DER to ASN.1 object | 
						|
  var obj = asn1.fromDer(msg.body, strict); | 
						|
 | 
						|
  return pki.certificationRequestFromAsn1(obj, computeHash); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts a PKCS#10 certification request (CSR) to PEM format. | 
						|
 * | 
						|
 * @param csr the certification request. | 
						|
 * @param maxline the maximum characters per line, defaults to 64. | 
						|
 * | 
						|
 * @return the PEM-formatted certification request. | 
						|
 */ | 
						|
pki.certificationRequestToPem = function(csr, maxline) { | 
						|
  // convert to ASN.1, then DER, then PEM-encode | 
						|
  var msg = { | 
						|
    type: 'CERTIFICATE REQUEST', | 
						|
    body: asn1.toDer(pki.certificationRequestToAsn1(csr)).getBytes() | 
						|
  }; | 
						|
  return forge.pem.encode(msg, {maxline: maxline}); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Creates an empty X.509v3 RSA certificate. | 
						|
 * | 
						|
 * @return the certificate. | 
						|
 */ | 
						|
pki.createCertificate = function() { | 
						|
  var cert = {}; | 
						|
  cert.version = 0x02; | 
						|
  cert.serialNumber = '00'; | 
						|
  cert.signatureOid = null; | 
						|
  cert.signature = null; | 
						|
  cert.siginfo = {}; | 
						|
  cert.siginfo.algorithmOid = null; | 
						|
  cert.validity = {}; | 
						|
  cert.validity.notBefore = new Date(); | 
						|
  cert.validity.notAfter = new Date(); | 
						|
 | 
						|
  cert.issuer = {}; | 
						|
  cert.issuer.getField = function(sn) { | 
						|
    return _getAttribute(cert.issuer, sn); | 
						|
  }; | 
						|
  cert.issuer.addField = function(attr) { | 
						|
    _fillMissingFields([attr]); | 
						|
    cert.issuer.attributes.push(attr); | 
						|
  }; | 
						|
  cert.issuer.attributes = []; | 
						|
  cert.issuer.hash = null; | 
						|
 | 
						|
  cert.subject = {}; | 
						|
  cert.subject.getField = function(sn) { | 
						|
    return _getAttribute(cert.subject, sn); | 
						|
  }; | 
						|
  cert.subject.addField = function(attr) { | 
						|
    _fillMissingFields([attr]); | 
						|
    cert.subject.attributes.push(attr); | 
						|
  }; | 
						|
  cert.subject.attributes = []; | 
						|
  cert.subject.hash = null; | 
						|
 | 
						|
  cert.extensions = []; | 
						|
  cert.publicKey = null; | 
						|
  cert.md = null; | 
						|
 | 
						|
  /** | 
						|
   * Sets the subject of this certificate. | 
						|
   * | 
						|
   * @param attrs the array of subject attributes to use. | 
						|
   * @param uniqueId an optional a unique ID to use. | 
						|
   */ | 
						|
  cert.setSubject = function(attrs, uniqueId) { | 
						|
    // set new attributes, clear hash | 
						|
    _fillMissingFields(attrs); | 
						|
    cert.subject.attributes = attrs; | 
						|
    delete cert.subject.uniqueId; | 
						|
    if(uniqueId) { | 
						|
      // TODO: support arbitrary bit length ids | 
						|
      cert.subject.uniqueId = uniqueId; | 
						|
    } | 
						|
    cert.subject.hash = null; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Sets the issuer of this certificate. | 
						|
   * | 
						|
   * @param attrs the array of issuer attributes to use. | 
						|
   * @param uniqueId an optional a unique ID to use. | 
						|
   */ | 
						|
  cert.setIssuer = function(attrs, uniqueId) { | 
						|
    // set new attributes, clear hash | 
						|
    _fillMissingFields(attrs); | 
						|
    cert.issuer.attributes = attrs; | 
						|
    delete cert.issuer.uniqueId; | 
						|
    if(uniqueId) { | 
						|
      // TODO: support arbitrary bit length ids | 
						|
      cert.issuer.uniqueId = uniqueId; | 
						|
    } | 
						|
    cert.issuer.hash = null; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Sets the extensions of this certificate. | 
						|
   * | 
						|
   * @param exts the array of extensions to use. | 
						|
   */ | 
						|
  cert.setExtensions = function(exts) { | 
						|
    for(var i = 0; i < exts.length; ++i) { | 
						|
      _fillMissingExtensionFields(exts[i], {cert: cert}); | 
						|
    } | 
						|
    // set new extensions | 
						|
    cert.extensions = exts; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Gets an extension by its name or id. | 
						|
   * | 
						|
   * @param options the name to use or an object with: | 
						|
   *          name the name to use. | 
						|
   *          id the id to use. | 
						|
   * | 
						|
   * @return the extension or null if not found. | 
						|
   */ | 
						|
  cert.getExtension = function(options) { | 
						|
    if(typeof options === 'string') { | 
						|
      options = {name: options}; | 
						|
    } | 
						|
 | 
						|
    var rval = null; | 
						|
    var ext; | 
						|
    for(var i = 0; rval === null && i < cert.extensions.length; ++i) { | 
						|
      ext = cert.extensions[i]; | 
						|
      if(options.id && ext.id === options.id) { | 
						|
        rval = ext; | 
						|
      } else if(options.name && ext.name === options.name) { | 
						|
        rval = ext; | 
						|
      } | 
						|
    } | 
						|
    return rval; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Signs this certificate using the given private key. | 
						|
   * | 
						|
   * @param key the private key to sign with. | 
						|
   * @param md the message digest object to use (defaults to forge.md.sha1). | 
						|
   */ | 
						|
  cert.sign = function(key, md) { | 
						|
    // TODO: get signature OID from private key | 
						|
    cert.md = md || forge.md.sha1.create(); | 
						|
    var algorithmOid = oids[cert.md.algorithm + 'WithRSAEncryption']; | 
						|
    if(!algorithmOid) { | 
						|
      var error = new Error('Could not compute certificate digest. ' + | 
						|
        'Unknown message digest algorithm OID.'); | 
						|
      error.algorithm = cert.md.algorithm; | 
						|
      throw error; | 
						|
    } | 
						|
    cert.signatureOid = cert.siginfo.algorithmOid = algorithmOid; | 
						|
 | 
						|
    // get TBSCertificate, convert to DER | 
						|
    cert.tbsCertificate = pki.getTBSCertificate(cert); | 
						|
    var bytes = asn1.toDer(cert.tbsCertificate); | 
						|
 | 
						|
    // digest and sign | 
						|
    cert.md.update(bytes.getBytes()); | 
						|
    cert.signature = key.sign(cert.md); | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Attempts verify the signature on the passed certificate using this | 
						|
   * certificate's public key. | 
						|
   * | 
						|
   * @param child the certificate to verify. | 
						|
   * | 
						|
   * @return true if verified, false if not. | 
						|
   */ | 
						|
  cert.verify = function(child) { | 
						|
    var rval = false; | 
						|
 | 
						|
    if(!cert.issued(child)) { | 
						|
      var issuer = child.issuer; | 
						|
      var subject = cert.subject; | 
						|
      var error = new Error( | 
						|
        'The parent certificate did not issue the given child ' + | 
						|
        'certificate; the child certificate\'s issuer does not match the ' + | 
						|
        'parent\'s subject.'); | 
						|
      error.expectedIssuer = issuer.attributes; | 
						|
      error.actualIssuer = subject.attributes; | 
						|
      throw error; | 
						|
    } | 
						|
 | 
						|
    var md = child.md; | 
						|
    if(md === null) { | 
						|
      // check signature OID for supported signature types | 
						|
      if(child.signatureOid in oids) { | 
						|
        var oid = oids[child.signatureOid]; | 
						|
        switch(oid) { | 
						|
          case 'sha1WithRSAEncryption': | 
						|
            md = forge.md.sha1.create(); | 
						|
            break; | 
						|
          case 'md5WithRSAEncryption': | 
						|
            md = forge.md.md5.create(); | 
						|
            break; | 
						|
          case 'sha256WithRSAEncryption': | 
						|
            md = forge.md.sha256.create(); | 
						|
            break; | 
						|
          case 'sha384WithRSAEncryption': | 
						|
            md = forge.md.sha384.create(); | 
						|
            break; | 
						|
          case 'sha512WithRSAEncryption': | 
						|
            md = forge.md.sha512.create(); | 
						|
            break; | 
						|
          case 'RSASSA-PSS': | 
						|
            md = forge.md.sha256.create(); | 
						|
            break; | 
						|
        } | 
						|
      } | 
						|
      if(md === null) { | 
						|
        var error = new Error('Could not compute certificate digest. ' + | 
						|
          'Unknown signature OID.'); | 
						|
        error.signatureOid = child.signatureOid; | 
						|
        throw error; | 
						|
      } | 
						|
 | 
						|
      // produce DER formatted TBSCertificate and digest it | 
						|
      var tbsCertificate = child.tbsCertificate || pki.getTBSCertificate(child); | 
						|
      var bytes = asn1.toDer(tbsCertificate); | 
						|
      md.update(bytes.getBytes()); | 
						|
    } | 
						|
 | 
						|
    if(md !== null) { | 
						|
      var scheme; | 
						|
 | 
						|
      switch(child.signatureOid) { | 
						|
        case oids.sha1WithRSAEncryption: | 
						|
          scheme = undefined; /* use PKCS#1 v1.5 padding scheme */ | 
						|
          break; | 
						|
        case oids['RSASSA-PSS']: | 
						|
          var hash, mgf; | 
						|
 | 
						|
          /* initialize mgf */ | 
						|
          hash = oids[child.signatureParameters.mgf.hash.algorithmOid]; | 
						|
          if(hash === undefined || forge.md[hash] === undefined) { | 
						|
            var error = new Error('Unsupported MGF hash function.'); | 
						|
            error.oid = child.signatureParameters.mgf.hash.algorithmOid; | 
						|
            error.name = hash; | 
						|
            throw error; | 
						|
          } | 
						|
 | 
						|
          mgf = oids[child.signatureParameters.mgf.algorithmOid]; | 
						|
          if(mgf === undefined || forge.mgf[mgf] === undefined) { | 
						|
            var error = new Error('Unsupported MGF function.'); | 
						|
            error.oid = child.signatureParameters.mgf.algorithmOid; | 
						|
            error.name = mgf; | 
						|
            throw error; | 
						|
          } | 
						|
 | 
						|
          mgf = forge.mgf[mgf].create(forge.md[hash].create()); | 
						|
 | 
						|
          /* initialize hash function */ | 
						|
          hash = oids[child.signatureParameters.hash.algorithmOid]; | 
						|
          if(hash === undefined || forge.md[hash] === undefined) { | 
						|
            throw { | 
						|
              message: 'Unsupported RSASSA-PSS hash function.', | 
						|
              oid: child.signatureParameters.hash.algorithmOid, | 
						|
              name: hash | 
						|
            }; | 
						|
          } | 
						|
 | 
						|
          scheme = forge.pss.create(forge.md[hash].create(), mgf, | 
						|
            child.signatureParameters.saltLength); | 
						|
          break; | 
						|
      } | 
						|
 | 
						|
      // verify signature on cert using public key | 
						|
      rval = cert.publicKey.verify( | 
						|
        md.digest().getBytes(), child.signature, scheme); | 
						|
    } | 
						|
 | 
						|
    return rval; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Returns true if this certificate's issuer matches the passed | 
						|
   * certificate's subject. Note that no signature check is performed. | 
						|
   * | 
						|
   * @param parent the certificate to check. | 
						|
   * | 
						|
   * @return true if this certificate's issuer matches the passed certificate's | 
						|
   *         subject. | 
						|
   */ | 
						|
  cert.isIssuer = function(parent) { | 
						|
    var rval = false; | 
						|
 | 
						|
    var i = cert.issuer; | 
						|
    var s = parent.subject; | 
						|
 | 
						|
    // compare hashes if present | 
						|
    if(i.hash && s.hash) { | 
						|
      rval = (i.hash === s.hash); | 
						|
    } else if(i.attributes.length === s.attributes.length) { | 
						|
      // all attributes are the same so issuer matches subject | 
						|
      rval = true; | 
						|
      var iattr, sattr; | 
						|
      for(var n = 0; rval && n < i.attributes.length; ++n) { | 
						|
        iattr = i.attributes[n]; | 
						|
        sattr = s.attributes[n]; | 
						|
        if(iattr.type !== sattr.type || iattr.value !== sattr.value) { | 
						|
          // attribute mismatch | 
						|
          rval = false; | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    return rval; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Returns true if this certificate's subject matches the issuer of the | 
						|
   * given certificate). Note that not signature check is performed. | 
						|
   * | 
						|
   * @param child the certificate to check. | 
						|
   * | 
						|
   * @return true if this certificate's subject matches the passed | 
						|
   *         certificate's issuer. | 
						|
   */ | 
						|
  cert.issued = function(child) { | 
						|
    return child.isIssuer(cert); | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Generates the subjectKeyIdentifier for this certificate as byte buffer. | 
						|
   * | 
						|
   * @return the subjectKeyIdentifier for this certificate as byte buffer. | 
						|
   */ | 
						|
  cert.generateSubjectKeyIdentifier = function() { | 
						|
    /* See: 4.2.1.2 section of the the RFC3280, keyIdentifier is either: | 
						|
 | 
						|
      (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the | 
						|
        value of the BIT STRING subjectPublicKey (excluding the tag, | 
						|
        length, and number of unused bits). | 
						|
 | 
						|
      (2) The keyIdentifier is composed of a four bit type field with | 
						|
        the value 0100 followed by the least significant 60 bits of the | 
						|
        SHA-1 hash of the value of the BIT STRING subjectPublicKey | 
						|
        (excluding the tag, length, and number of unused bit string bits). | 
						|
    */ | 
						|
 | 
						|
    // skipping the tag, length, and number of unused bits is the same | 
						|
    // as just using the RSAPublicKey (for RSA keys, which are the | 
						|
    // only ones supported) | 
						|
    return pki.getPublicKeyFingerprint(cert.publicKey, {type: 'RSAPublicKey'}); | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Verifies the subjectKeyIdentifier extension value for this certificate | 
						|
   * against its public key. If no extension is found, false will be | 
						|
   * returned. | 
						|
   * | 
						|
   * @return true if verified, false if not. | 
						|
   */ | 
						|
  cert.verifySubjectKeyIdentifier = function() { | 
						|
    var oid = oids['subjectKeyIdentifier']; | 
						|
    for(var i = 0; i < cert.extensions.length; ++i) { | 
						|
      var ext = cert.extensions[i]; | 
						|
      if(ext.id === oid) { | 
						|
        var ski = cert.generateSubjectKeyIdentifier().getBytes(); | 
						|
        return (forge.util.hexToBytes(ext.subjectKeyIdentifier) === ski); | 
						|
      } | 
						|
    } | 
						|
    return false; | 
						|
  }; | 
						|
 | 
						|
  return cert; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an X.509v3 RSA certificate from an ASN.1 object. | 
						|
 * | 
						|
 * Note: If the certificate is to be verified then compute hash should | 
						|
 * be set to true. There is currently no implementation for converting | 
						|
 * a certificate back to ASN.1 so the TBSCertificate part of the ASN.1 | 
						|
 * object needs to be scanned before the cert object is created. | 
						|
 * | 
						|
 * @param obj the asn1 representation of an X.509v3 RSA certificate. | 
						|
 * @param computeHash true to compute the hash for verification. | 
						|
 * | 
						|
 * @return the certificate. | 
						|
 */ | 
						|
pki.certificateFromAsn1 = function(obj, computeHash) { | 
						|
  // validate certificate and capture data | 
						|
  var capture = {}; | 
						|
  var errors = []; | 
						|
  if(!asn1.validate(obj, x509CertificateValidator, capture, errors)) { | 
						|
    var error = new Error('Cannot read X.509 certificate. ' + | 
						|
      'ASN.1 object is not an X509v3 Certificate.'); | 
						|
    error.errors = errors; | 
						|
    throw error; | 
						|
  } | 
						|
 | 
						|
  // get oid | 
						|
  var oid = asn1.derToOid(capture.publicKeyOid); | 
						|
  if(oid !== pki.oids.rsaEncryption) { | 
						|
    throw new Error('Cannot read public key. OID is not RSA.'); | 
						|
  } | 
						|
 | 
						|
  // create certificate | 
						|
  var cert = pki.createCertificate(); | 
						|
  cert.version = capture.certVersion ? | 
						|
    capture.certVersion.charCodeAt(0) : 0; | 
						|
  var serial = forge.util.createBuffer(capture.certSerialNumber); | 
						|
  cert.serialNumber = serial.toHex(); | 
						|
  cert.signatureOid = forge.asn1.derToOid(capture.certSignatureOid); | 
						|
  cert.signatureParameters = _readSignatureParameters( | 
						|
    cert.signatureOid, capture.certSignatureParams, true); | 
						|
  cert.siginfo.algorithmOid = forge.asn1.derToOid(capture.certinfoSignatureOid); | 
						|
  cert.siginfo.parameters = _readSignatureParameters(cert.siginfo.algorithmOid, | 
						|
    capture.certinfoSignatureParams, false); | 
						|
  cert.signature = capture.certSignature; | 
						|
 | 
						|
  var validity = []; | 
						|
  if(capture.certValidity1UTCTime !== undefined) { | 
						|
    validity.push(asn1.utcTimeToDate(capture.certValidity1UTCTime)); | 
						|
  } | 
						|
  if(capture.certValidity2GeneralizedTime !== undefined) { | 
						|
    validity.push(asn1.generalizedTimeToDate( | 
						|
      capture.certValidity2GeneralizedTime)); | 
						|
  } | 
						|
  if(capture.certValidity3UTCTime !== undefined) { | 
						|
    validity.push(asn1.utcTimeToDate(capture.certValidity3UTCTime)); | 
						|
  } | 
						|
  if(capture.certValidity4GeneralizedTime !== undefined) { | 
						|
    validity.push(asn1.generalizedTimeToDate( | 
						|
      capture.certValidity4GeneralizedTime)); | 
						|
  } | 
						|
  if(validity.length > 2) { | 
						|
    throw new Error('Cannot read notBefore/notAfter validity times; more ' + | 
						|
      'than two times were provided in the certificate.'); | 
						|
  } | 
						|
  if(validity.length < 2) { | 
						|
    throw new Error('Cannot read notBefore/notAfter validity times; they ' + | 
						|
      'were not provided as either UTCTime or GeneralizedTime.'); | 
						|
  } | 
						|
  cert.validity.notBefore = validity[0]; | 
						|
  cert.validity.notAfter = validity[1]; | 
						|
 | 
						|
  // keep TBSCertificate to preserve signature when exporting | 
						|
  cert.tbsCertificate = capture.tbsCertificate; | 
						|
 | 
						|
  if(computeHash) { | 
						|
    // check signature OID for supported signature types | 
						|
    cert.md = null; | 
						|
    if(cert.signatureOid in oids) { | 
						|
      var oid = oids[cert.signatureOid]; | 
						|
      switch(oid) { | 
						|
        case 'sha1WithRSAEncryption': | 
						|
          cert.md = forge.md.sha1.create(); | 
						|
          break; | 
						|
        case 'md5WithRSAEncryption': | 
						|
          cert.md = forge.md.md5.create(); | 
						|
          break; | 
						|
        case 'sha256WithRSAEncryption': | 
						|
          cert.md = forge.md.sha256.create(); | 
						|
          break; | 
						|
        case 'sha384WithRSAEncryption': | 
						|
          cert.md = forge.md.sha384.create(); | 
						|
          break; | 
						|
        case 'sha512WithRSAEncryption': | 
						|
          cert.md = forge.md.sha512.create(); | 
						|
          break; | 
						|
        case 'RSASSA-PSS': | 
						|
          cert.md = forge.md.sha256.create(); | 
						|
          break; | 
						|
      } | 
						|
    } | 
						|
    if(cert.md === null) { | 
						|
      var error = new Error('Could not compute certificate digest. ' + | 
						|
        'Unknown signature OID.'); | 
						|
      error.signatureOid = cert.signatureOid; | 
						|
      throw error; | 
						|
    } | 
						|
 | 
						|
    // produce DER formatted TBSCertificate and digest it | 
						|
    var bytes = asn1.toDer(cert.tbsCertificate); | 
						|
    cert.md.update(bytes.getBytes()); | 
						|
  } | 
						|
 | 
						|
  // handle issuer, build issuer message digest | 
						|
  var imd = forge.md.sha1.create(); | 
						|
  cert.issuer.getField = function(sn) { | 
						|
    return _getAttribute(cert.issuer, sn); | 
						|
  }; | 
						|
  cert.issuer.addField = function(attr) { | 
						|
    _fillMissingFields([attr]); | 
						|
    cert.issuer.attributes.push(attr); | 
						|
  }; | 
						|
  cert.issuer.attributes = pki.RDNAttributesAsArray(capture.certIssuer, imd); | 
						|
  if(capture.certIssuerUniqueId) { | 
						|
    cert.issuer.uniqueId = capture.certIssuerUniqueId; | 
						|
  } | 
						|
  cert.issuer.hash = imd.digest().toHex(); | 
						|
 | 
						|
  // handle subject, build subject message digest | 
						|
  var smd = forge.md.sha1.create(); | 
						|
  cert.subject.getField = function(sn) { | 
						|
    return _getAttribute(cert.subject, sn); | 
						|
  }; | 
						|
  cert.subject.addField = function(attr) { | 
						|
    _fillMissingFields([attr]); | 
						|
    cert.subject.attributes.push(attr); | 
						|
  }; | 
						|
  cert.subject.attributes = pki.RDNAttributesAsArray(capture.certSubject, smd); | 
						|
  if(capture.certSubjectUniqueId) { | 
						|
    cert.subject.uniqueId = capture.certSubjectUniqueId; | 
						|
  } | 
						|
  cert.subject.hash = smd.digest().toHex(); | 
						|
 | 
						|
  // handle extensions | 
						|
  if(capture.certExtensions) { | 
						|
    cert.extensions = pki.certificateExtensionsFromAsn1(capture.certExtensions); | 
						|
  } else { | 
						|
    cert.extensions = []; | 
						|
  } | 
						|
 | 
						|
  // convert RSA public key from ASN.1 | 
						|
  cert.publicKey = pki.publicKeyFromAsn1(capture.subjectPublicKeyInfo); | 
						|
 | 
						|
  return cert; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an ASN.1 extensions object (with extension sequences as its | 
						|
 * values) into an array of extension objects with types and values. | 
						|
 * | 
						|
 * Supported extensions: | 
						|
 * | 
						|
 * id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 } | 
						|
 * KeyUsage ::= BIT STRING { | 
						|
 *   digitalSignature        (0), | 
						|
 *   nonRepudiation          (1), | 
						|
 *   keyEncipherment         (2), | 
						|
 *   dataEncipherment        (3), | 
						|
 *   keyAgreement            (4), | 
						|
 *   keyCertSign             (5), | 
						|
 *   cRLSign                 (6), | 
						|
 *   encipherOnly            (7), | 
						|
 *   decipherOnly            (8) | 
						|
 * } | 
						|
 * | 
						|
 * id-ce-basicConstraints OBJECT IDENTIFIER ::=  { id-ce 19 } | 
						|
 * BasicConstraints ::= SEQUENCE { | 
						|
 *   cA                      BOOLEAN DEFAULT FALSE, | 
						|
 *   pathLenConstraint       INTEGER (0..MAX) OPTIONAL | 
						|
 * } | 
						|
 * | 
						|
 * subjectAltName EXTENSION ::= { | 
						|
 *   SYNTAX GeneralNames | 
						|
 *   IDENTIFIED BY id-ce-subjectAltName | 
						|
 * } | 
						|
 * | 
						|
 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName | 
						|
 * | 
						|
 * GeneralName ::= CHOICE { | 
						|
 *   otherName      [0] INSTANCE OF OTHER-NAME, | 
						|
 *   rfc822Name     [1] IA5String, | 
						|
 *   dNSName        [2] IA5String, | 
						|
 *   x400Address    [3] ORAddress, | 
						|
 *   directoryName  [4] Name, | 
						|
 *   ediPartyName   [5] EDIPartyName, | 
						|
 *   uniformResourceIdentifier [6] IA5String, | 
						|
 *   IPAddress      [7] OCTET STRING, | 
						|
 *   registeredID   [8] OBJECT IDENTIFIER | 
						|
 * } | 
						|
 * | 
						|
 * OTHER-NAME ::= TYPE-IDENTIFIER | 
						|
 * | 
						|
 * EDIPartyName ::= SEQUENCE { | 
						|
 *   nameAssigner [0] DirectoryString {ub-name} OPTIONAL, | 
						|
 *   partyName    [1] DirectoryString {ub-name} | 
						|
 * } | 
						|
 * | 
						|
 * @param exts the extensions ASN.1 with extension sequences to parse. | 
						|
 * | 
						|
 * @return the array. | 
						|
 */ | 
						|
pki.certificateExtensionsFromAsn1 = function(exts) { | 
						|
  var rval = []; | 
						|
  for(var i = 0; i < exts.value.length; ++i) { | 
						|
    // get extension sequence | 
						|
    var extseq = exts.value[i]; | 
						|
    for(var ei = 0; ei < extseq.value.length; ++ei) { | 
						|
      rval.push(pki.certificateExtensionFromAsn1(extseq.value[ei])); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return rval; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Parses a single certificate extension from ASN.1. | 
						|
 * | 
						|
 * @param ext the extension in ASN.1 format. | 
						|
 * | 
						|
 * @return the parsed extension as an object. | 
						|
 */ | 
						|
pki.certificateExtensionFromAsn1 = function(ext) { | 
						|
  // an extension has: | 
						|
  // [0] extnID      OBJECT IDENTIFIER | 
						|
  // [1] critical    BOOLEAN DEFAULT FALSE | 
						|
  // [2] extnValue   OCTET STRING | 
						|
  var e = {}; | 
						|
  e.id = asn1.derToOid(ext.value[0].value); | 
						|
  e.critical = false; | 
						|
  if(ext.value[1].type === asn1.Type.BOOLEAN) { | 
						|
    e.critical = (ext.value[1].value.charCodeAt(0) !== 0x00); | 
						|
    e.value = ext.value[2].value; | 
						|
  } else { | 
						|
    e.value = ext.value[1].value; | 
						|
  } | 
						|
  // if the oid is known, get its name | 
						|
  if(e.id in oids) { | 
						|
    e.name = oids[e.id]; | 
						|
 | 
						|
    // handle key usage | 
						|
    if(e.name === 'keyUsage') { | 
						|
      // get value as BIT STRING | 
						|
      var ev = asn1.fromDer(e.value); | 
						|
      var b2 = 0x00; | 
						|
      var b3 = 0x00; | 
						|
      if(ev.value.length > 1) { | 
						|
        // skip first byte, just indicates unused bits which | 
						|
        // will be padded with 0s anyway | 
						|
        // get bytes with flag bits | 
						|
        b2 = ev.value.charCodeAt(1); | 
						|
        b3 = ev.value.length > 2 ? ev.value.charCodeAt(2) : 0; | 
						|
      } | 
						|
      // set flags | 
						|
      e.digitalSignature = (b2 & 0x80) === 0x80; | 
						|
      e.nonRepudiation = (b2 & 0x40) === 0x40; | 
						|
      e.keyEncipherment = (b2 & 0x20) === 0x20; | 
						|
      e.dataEncipherment = (b2 & 0x10) === 0x10; | 
						|
      e.keyAgreement = (b2 & 0x08) === 0x08; | 
						|
      e.keyCertSign = (b2 & 0x04) === 0x04; | 
						|
      e.cRLSign = (b2 & 0x02) === 0x02; | 
						|
      e.encipherOnly = (b2 & 0x01) === 0x01; | 
						|
      e.decipherOnly = (b3 & 0x80) === 0x80; | 
						|
    } else if(e.name === 'basicConstraints') { | 
						|
      // handle basic constraints | 
						|
      // get value as SEQUENCE | 
						|
      var ev = asn1.fromDer(e.value); | 
						|
      // get cA BOOLEAN flag (defaults to false) | 
						|
      if(ev.value.length > 0 && ev.value[0].type === asn1.Type.BOOLEAN) { | 
						|
        e.cA = (ev.value[0].value.charCodeAt(0) !== 0x00); | 
						|
      } else { | 
						|
        e.cA = false; | 
						|
      } | 
						|
      // get path length constraint | 
						|
      var value = null; | 
						|
      if(ev.value.length > 0 && ev.value[0].type === asn1.Type.INTEGER) { | 
						|
        value = ev.value[0].value; | 
						|
      } else if(ev.value.length > 1) { | 
						|
        value = ev.value[1].value; | 
						|
      } | 
						|
      if(value !== null) { | 
						|
        e.pathLenConstraint = asn1.derToInteger(value); | 
						|
      } | 
						|
    } else if(e.name === 'extKeyUsage') { | 
						|
      // handle extKeyUsage | 
						|
      // value is a SEQUENCE of OIDs | 
						|
      var ev = asn1.fromDer(e.value); | 
						|
      for(var vi = 0; vi < ev.value.length; ++vi) { | 
						|
        var oid = asn1.derToOid(ev.value[vi].value); | 
						|
        if(oid in oids) { | 
						|
          e[oids[oid]] = true; | 
						|
        } else { | 
						|
          e[oid] = true; | 
						|
        } | 
						|
      } | 
						|
    } else if(e.name === 'nsCertType') { | 
						|
      // handle nsCertType | 
						|
      // get value as BIT STRING | 
						|
      var ev = asn1.fromDer(e.value); | 
						|
      var b2 = 0x00; | 
						|
      if(ev.value.length > 1) { | 
						|
        // skip first byte, just indicates unused bits which | 
						|
        // will be padded with 0s anyway | 
						|
        // get bytes with flag bits | 
						|
        b2 = ev.value.charCodeAt(1); | 
						|
      } | 
						|
      // set flags | 
						|
      e.client = (b2 & 0x80) === 0x80; | 
						|
      e.server = (b2 & 0x40) === 0x40; | 
						|
      e.email = (b2 & 0x20) === 0x20; | 
						|
      e.objsign = (b2 & 0x10) === 0x10; | 
						|
      e.reserved = (b2 & 0x08) === 0x08; | 
						|
      e.sslCA = (b2 & 0x04) === 0x04; | 
						|
      e.emailCA = (b2 & 0x02) === 0x02; | 
						|
      e.objCA = (b2 & 0x01) === 0x01; | 
						|
    } else if( | 
						|
      e.name === 'subjectAltName' || | 
						|
      e.name === 'issuerAltName') { | 
						|
      // handle subjectAltName/issuerAltName | 
						|
      e.altNames = []; | 
						|
 | 
						|
      // ev is a SYNTAX SEQUENCE | 
						|
      var gn; | 
						|
      var ev = asn1.fromDer(e.value); | 
						|
      for(var n = 0; n < ev.value.length; ++n) { | 
						|
        // get GeneralName | 
						|
        gn = ev.value[n]; | 
						|
 | 
						|
        var altName = { | 
						|
          type: gn.type, | 
						|
          value: gn.value | 
						|
        }; | 
						|
        e.altNames.push(altName); | 
						|
 | 
						|
        // Note: Support for types 1,2,6,7,8 | 
						|
        switch(gn.type) { | 
						|
          // rfc822Name | 
						|
          case 1: | 
						|
          // dNSName | 
						|
          case 2: | 
						|
          // uniformResourceIdentifier (URI) | 
						|
          case 6: | 
						|
            break; | 
						|
          // IPAddress | 
						|
          case 7: | 
						|
            // convert to IPv4/IPv6 string representation | 
						|
            altName.ip = forge.util.bytesToIP(gn.value); | 
						|
            break; | 
						|
          // registeredID | 
						|
          case 8: | 
						|
            altName.oid = asn1.derToOid(gn.value); | 
						|
            break; | 
						|
          default: | 
						|
            // unsupported | 
						|
        } | 
						|
      } | 
						|
    } else if(e.name === 'subjectKeyIdentifier') { | 
						|
      // value is an OCTETSTRING w/the hash of the key-type specific | 
						|
      // public key structure (eg: RSAPublicKey) | 
						|
      var ev = asn1.fromDer(e.value); | 
						|
      e.subjectKeyIdentifier = forge.util.bytesToHex(ev.value); | 
						|
    } | 
						|
  } | 
						|
  return e; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts a PKCS#10 certification request (CSR) from an ASN.1 object. | 
						|
 * | 
						|
 * Note: If the certification request is to be verified then compute hash | 
						|
 * should be set to true. There is currently no implementation for converting | 
						|
 * a certificate back to ASN.1 so the CertificationRequestInfo part of the | 
						|
 * ASN.1 object needs to be scanned before the csr object is created. | 
						|
 * | 
						|
 * @param obj the asn1 representation of a PKCS#10 certification request (CSR). | 
						|
 * @param computeHash true to compute the hash for verification. | 
						|
 * | 
						|
 * @return the certification request (CSR). | 
						|
 */ | 
						|
pki.certificationRequestFromAsn1 = function(obj, computeHash) { | 
						|
  // validate certification request and capture data | 
						|
  var capture = {}; | 
						|
  var errors = []; | 
						|
  if(!asn1.validate(obj, certificationRequestValidator, capture, errors)) { | 
						|
    var error = new Error('Cannot read PKCS#10 certificate request. ' + | 
						|
      'ASN.1 object is not a PKCS#10 CertificationRequest.'); | 
						|
    error.errors = errors; | 
						|
    throw error; | 
						|
  } | 
						|
 | 
						|
  // get oid | 
						|
  var oid = asn1.derToOid(capture.publicKeyOid); | 
						|
  if(oid !== pki.oids.rsaEncryption) { | 
						|
    throw new Error('Cannot read public key. OID is not RSA.'); | 
						|
  } | 
						|
 | 
						|
  // create certification request | 
						|
  var csr = pki.createCertificationRequest(); | 
						|
  csr.version = capture.csrVersion ? capture.csrVersion.charCodeAt(0) : 0; | 
						|
  csr.signatureOid = forge.asn1.derToOid(capture.csrSignatureOid); | 
						|
  csr.signatureParameters = _readSignatureParameters( | 
						|
    csr.signatureOid, capture.csrSignatureParams, true); | 
						|
  csr.siginfo.algorithmOid = forge.asn1.derToOid(capture.csrSignatureOid); | 
						|
  csr.siginfo.parameters = _readSignatureParameters( | 
						|
    csr.siginfo.algorithmOid, capture.csrSignatureParams, false); | 
						|
  csr.signature = capture.csrSignature; | 
						|
 | 
						|
  // keep CertificationRequestInfo to preserve signature when exporting | 
						|
  csr.certificationRequestInfo = capture.certificationRequestInfo; | 
						|
 | 
						|
  if(computeHash) { | 
						|
    // check signature OID for supported signature types | 
						|
    csr.md = null; | 
						|
    if(csr.signatureOid in oids) { | 
						|
      var oid = oids[csr.signatureOid]; | 
						|
      switch(oid) { | 
						|
        case 'sha1WithRSAEncryption': | 
						|
          csr.md = forge.md.sha1.create(); | 
						|
          break; | 
						|
        case 'md5WithRSAEncryption': | 
						|
          csr.md = forge.md.md5.create(); | 
						|
          break; | 
						|
        case 'sha256WithRSAEncryption': | 
						|
          csr.md = forge.md.sha256.create(); | 
						|
          break; | 
						|
        case 'sha384WithRSAEncryption': | 
						|
          csr.md = forge.md.sha384.create(); | 
						|
          break; | 
						|
        case 'sha512WithRSAEncryption': | 
						|
          csr.md = forge.md.sha512.create(); | 
						|
          break; | 
						|
        case 'RSASSA-PSS': | 
						|
          csr.md = forge.md.sha256.create(); | 
						|
          break; | 
						|
      } | 
						|
    } | 
						|
    if(csr.md === null) { | 
						|
      var error = new Error('Could not compute certification request digest. ' + | 
						|
        'Unknown signature OID.'); | 
						|
      error.signatureOid = csr.signatureOid; | 
						|
      throw error; | 
						|
    } | 
						|
 | 
						|
    // produce DER formatted CertificationRequestInfo and digest it | 
						|
    var bytes = asn1.toDer(csr.certificationRequestInfo); | 
						|
    csr.md.update(bytes.getBytes()); | 
						|
  } | 
						|
 | 
						|
  // handle subject, build subject message digest | 
						|
  var smd = forge.md.sha1.create(); | 
						|
  csr.subject.getField = function(sn) { | 
						|
    return _getAttribute(csr.subject, sn); | 
						|
  }; | 
						|
  csr.subject.addField = function(attr) { | 
						|
    _fillMissingFields([attr]); | 
						|
    csr.subject.attributes.push(attr); | 
						|
  }; | 
						|
  csr.subject.attributes = pki.RDNAttributesAsArray( | 
						|
    capture.certificationRequestInfoSubject, smd); | 
						|
  csr.subject.hash = smd.digest().toHex(); | 
						|
 | 
						|
  // convert RSA public key from ASN.1 | 
						|
  csr.publicKey = pki.publicKeyFromAsn1(capture.subjectPublicKeyInfo); | 
						|
 | 
						|
  // convert attributes from ASN.1 | 
						|
  csr.getAttribute = function(sn) { | 
						|
    return _getAttribute(csr, sn); | 
						|
  }; | 
						|
  csr.addAttribute = function(attr) { | 
						|
    _fillMissingFields([attr]); | 
						|
    csr.attributes.push(attr); | 
						|
  }; | 
						|
  csr.attributes = pki.CRIAttributesAsArray( | 
						|
    capture.certificationRequestInfoAttributes || []); | 
						|
 | 
						|
  return csr; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Creates an empty certification request (a CSR or certificate signing | 
						|
 * request). Once created, its public key and attributes can be set and then | 
						|
 * it can be signed. | 
						|
 * | 
						|
 * @return the empty certification request. | 
						|
 */ | 
						|
pki.createCertificationRequest = function() { | 
						|
  var csr = {}; | 
						|
  csr.version = 0x00; | 
						|
  csr.signatureOid = null; | 
						|
  csr.signature = null; | 
						|
  csr.siginfo = {}; | 
						|
  csr.siginfo.algorithmOid = null; | 
						|
 | 
						|
  csr.subject = {}; | 
						|
  csr.subject.getField = function(sn) { | 
						|
    return _getAttribute(csr.subject, sn); | 
						|
  }; | 
						|
  csr.subject.addField = function(attr) { | 
						|
    _fillMissingFields([attr]); | 
						|
    csr.subject.attributes.push(attr); | 
						|
  }; | 
						|
  csr.subject.attributes = []; | 
						|
  csr.subject.hash = null; | 
						|
 | 
						|
  csr.publicKey = null; | 
						|
  csr.attributes = []; | 
						|
  csr.getAttribute = function(sn) { | 
						|
    return _getAttribute(csr, sn); | 
						|
  }; | 
						|
  csr.addAttribute = function(attr) { | 
						|
    _fillMissingFields([attr]); | 
						|
    csr.attributes.push(attr); | 
						|
  }; | 
						|
  csr.md = null; | 
						|
 | 
						|
  /** | 
						|
   * Sets the subject of this certification request. | 
						|
   * | 
						|
   * @param attrs the array of subject attributes to use. | 
						|
   */ | 
						|
  csr.setSubject = function(attrs) { | 
						|
    // set new attributes | 
						|
    _fillMissingFields(attrs); | 
						|
    csr.subject.attributes = attrs; | 
						|
    csr.subject.hash = null; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Sets the attributes of this certification request. | 
						|
   * | 
						|
   * @param attrs the array of attributes to use. | 
						|
   */ | 
						|
  csr.setAttributes = function(attrs) { | 
						|
    // set new attributes | 
						|
    _fillMissingFields(attrs); | 
						|
    csr.attributes = attrs; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Signs this certification request using the given private key. | 
						|
   * | 
						|
   * @param key the private key to sign with. | 
						|
   * @param md the message digest object to use (defaults to forge.md.sha1). | 
						|
   */ | 
						|
  csr.sign = function(key, md) { | 
						|
    // TODO: get signature OID from private key | 
						|
    csr.md = md || forge.md.sha1.create(); | 
						|
    var algorithmOid = oids[csr.md.algorithm + 'WithRSAEncryption']; | 
						|
    if(!algorithmOid) { | 
						|
      var error = new Error('Could not compute certification request digest. ' + | 
						|
        'Unknown message digest algorithm OID.'); | 
						|
      error.algorithm = csr.md.algorithm; | 
						|
      throw error; | 
						|
    } | 
						|
    csr.signatureOid = csr.siginfo.algorithmOid = algorithmOid; | 
						|
 | 
						|
    // get CertificationRequestInfo, convert to DER | 
						|
    csr.certificationRequestInfo = pki.getCertificationRequestInfo(csr); | 
						|
    var bytes = asn1.toDer(csr.certificationRequestInfo); | 
						|
 | 
						|
    // digest and sign | 
						|
    csr.md.update(bytes.getBytes()); | 
						|
    csr.signature = key.sign(csr.md); | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Attempts verify the signature on the passed certification request using | 
						|
   * its public key. | 
						|
   * | 
						|
   * A CSR that has been exported to a file in PEM format can be verified using | 
						|
   * OpenSSL using this command: | 
						|
   * | 
						|
   * openssl req -in <the-csr-pem-file> -verify -noout -text | 
						|
   * | 
						|
   * @return true if verified, false if not. | 
						|
   */ | 
						|
  csr.verify = function() { | 
						|
    var rval = false; | 
						|
 | 
						|
    var md = csr.md; | 
						|
    if(md === null) { | 
						|
      // check signature OID for supported signature types | 
						|
      if(csr.signatureOid in oids) { | 
						|
        // TODO: create DRY `OID to md` function | 
						|
        var oid = oids[csr.signatureOid]; | 
						|
        switch(oid) { | 
						|
          case 'sha1WithRSAEncryption': | 
						|
            md = forge.md.sha1.create(); | 
						|
            break; | 
						|
          case 'md5WithRSAEncryption': | 
						|
            md = forge.md.md5.create(); | 
						|
            break; | 
						|
          case 'sha256WithRSAEncryption': | 
						|
            md = forge.md.sha256.create(); | 
						|
            break; | 
						|
          case 'sha384WithRSAEncryption': | 
						|
            md = forge.md.sha384.create(); | 
						|
            break; | 
						|
          case 'sha512WithRSAEncryption': | 
						|
            md = forge.md.sha512.create(); | 
						|
            break; | 
						|
          case 'RSASSA-PSS': | 
						|
            md = forge.md.sha256.create(); | 
						|
            break; | 
						|
        } | 
						|
      } | 
						|
      if(md === null) { | 
						|
        var error = new Error( | 
						|
          'Could not compute certification request digest. ' + | 
						|
          'Unknown signature OID.'); | 
						|
        error.signatureOid = csr.signatureOid; | 
						|
        throw error; | 
						|
      } | 
						|
 | 
						|
      // produce DER formatted CertificationRequestInfo and digest it | 
						|
      var cri = csr.certificationRequestInfo || | 
						|
        pki.getCertificationRequestInfo(csr); | 
						|
      var bytes = asn1.toDer(cri); | 
						|
      md.update(bytes.getBytes()); | 
						|
    } | 
						|
 | 
						|
    if(md !== null) { | 
						|
      var scheme; | 
						|
 | 
						|
      switch(csr.signatureOid) { | 
						|
        case oids.sha1WithRSAEncryption: | 
						|
          /* use PKCS#1 v1.5 padding scheme */ | 
						|
          break; | 
						|
        case oids['RSASSA-PSS']: | 
						|
          var hash, mgf; | 
						|
 | 
						|
          /* initialize mgf */ | 
						|
          hash = oids[csr.signatureParameters.mgf.hash.algorithmOid]; | 
						|
          if(hash === undefined || forge.md[hash] === undefined) { | 
						|
            var error = new Error('Unsupported MGF hash function.'); | 
						|
            error.oid = csr.signatureParameters.mgf.hash.algorithmOid; | 
						|
            error.name = hash; | 
						|
            throw error; | 
						|
          } | 
						|
 | 
						|
          mgf = oids[csr.signatureParameters.mgf.algorithmOid]; | 
						|
          if(mgf === undefined || forge.mgf[mgf] === undefined) { | 
						|
            var error = new Error('Unsupported MGF function.'); | 
						|
            error.oid = csr.signatureParameters.mgf.algorithmOid; | 
						|
            error.name = mgf; | 
						|
            throw error; | 
						|
          } | 
						|
 | 
						|
          mgf = forge.mgf[mgf].create(forge.md[hash].create()); | 
						|
 | 
						|
          /* initialize hash function */ | 
						|
          hash = oids[csr.signatureParameters.hash.algorithmOid]; | 
						|
          if(hash === undefined || forge.md[hash] === undefined) { | 
						|
            var error = new Error('Unsupported RSASSA-PSS hash function.'); | 
						|
            error.oid = csr.signatureParameters.hash.algorithmOid; | 
						|
            error.name = hash; | 
						|
            throw error; | 
						|
          } | 
						|
 | 
						|
          scheme = forge.pss.create(forge.md[hash].create(), mgf, | 
						|
            csr.signatureParameters.saltLength); | 
						|
          break; | 
						|
      } | 
						|
 | 
						|
      // verify signature on csr using its public key | 
						|
      rval = csr.publicKey.verify( | 
						|
        md.digest().getBytes(), csr.signature, scheme); | 
						|
    } | 
						|
 | 
						|
    return rval; | 
						|
  }; | 
						|
 | 
						|
  return csr; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an X.509 subject or issuer to an ASN.1 RDNSequence. | 
						|
 * | 
						|
 * @param obj the subject or issuer (distinguished name). | 
						|
 * | 
						|
 * @return the ASN.1 RDNSequence. | 
						|
 */ | 
						|
function _dnToAsn1(obj) { | 
						|
  // create an empty RDNSequence | 
						|
  var rval = asn1.create( | 
						|
    asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
 | 
						|
  // iterate over attributes | 
						|
  var attr, set; | 
						|
  var attrs = obj.attributes; | 
						|
  for(var i = 0; i < attrs.length; ++i) { | 
						|
    attr = attrs[i]; | 
						|
    var value = attr.value; | 
						|
 | 
						|
    // reuse tag class for attribute value if available | 
						|
    var valueTagClass = asn1.Type.PRINTABLESTRING; | 
						|
    if('valueTagClass' in attr) { | 
						|
      valueTagClass = attr.valueTagClass; | 
						|
 | 
						|
      if(valueTagClass === asn1.Type.UTF8) { | 
						|
        value = forge.util.encodeUtf8(value); | 
						|
      } | 
						|
      // FIXME: handle more encodings | 
						|
    } | 
						|
 | 
						|
    // create a RelativeDistinguishedName set | 
						|
    // each value in the set is an AttributeTypeAndValue first | 
						|
    // containing the type (an OID) and second the value | 
						|
    set = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [ | 
						|
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
        // AttributeType | 
						|
        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
          asn1.oidToDer(attr.type).getBytes()), | 
						|
        // AttributeValue | 
						|
        asn1.create(asn1.Class.UNIVERSAL, valueTagClass, false, value) | 
						|
      ]) | 
						|
    ]); | 
						|
    rval.value.push(set); | 
						|
  } | 
						|
 | 
						|
  return rval; | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Gets all printable attributes (typically of an issuer or subject) in a | 
						|
 * simplified JSON format for display. | 
						|
 * | 
						|
 * @param attrs the attributes. | 
						|
 * | 
						|
 * @return the JSON for display. | 
						|
 */ | 
						|
function _getAttributesAsJson(attrs) { | 
						|
  var rval = {}; | 
						|
  for(var i = 0; i < attrs.length; ++i) { | 
						|
    var attr = attrs[i]; | 
						|
    if(attr.shortName && ( | 
						|
      attr.valueTagClass === asn1.Type.UTF8 || | 
						|
      attr.valueTagClass === asn1.Type.PRINTABLESTRING || | 
						|
      attr.valueTagClass === asn1.Type.IA5STRING)) { | 
						|
      var value = attr.value; | 
						|
      if(attr.valueTagClass === asn1.Type.UTF8) { | 
						|
        value = forge.util.encodeUtf8(attr.value); | 
						|
      } | 
						|
      if(!(attr.shortName in rval)) { | 
						|
        rval[attr.shortName] = value; | 
						|
      } else if(forge.util.isArray(rval[attr.shortName])) { | 
						|
        rval[attr.shortName].push(value); | 
						|
      } else { | 
						|
        rval[attr.shortName] = [rval[attr.shortName], value]; | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
  return rval; | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Fills in missing fields in attributes. | 
						|
 * | 
						|
 * @param attrs the attributes to fill missing fields in. | 
						|
 */ | 
						|
function _fillMissingFields(attrs) { | 
						|
  var attr; | 
						|
  for(var i = 0; i < attrs.length; ++i) { | 
						|
    attr = attrs[i]; | 
						|
 | 
						|
    // populate missing name | 
						|
    if(typeof attr.name === 'undefined') { | 
						|
      if(attr.type && attr.type in pki.oids) { | 
						|
        attr.name = pki.oids[attr.type]; | 
						|
      } else if(attr.shortName && attr.shortName in _shortNames) { | 
						|
        attr.name = pki.oids[_shortNames[attr.shortName]]; | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // populate missing type (OID) | 
						|
    if(typeof attr.type === 'undefined') { | 
						|
      if(attr.name && attr.name in pki.oids) { | 
						|
        attr.type = pki.oids[attr.name]; | 
						|
      } else { | 
						|
        var error = new Error('Attribute type not specified.'); | 
						|
        error.attribute = attr; | 
						|
        throw error; | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // populate missing shortname | 
						|
    if(typeof attr.shortName === 'undefined') { | 
						|
      if(attr.name && attr.name in _shortNames) { | 
						|
        attr.shortName = _shortNames[attr.name]; | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // convert extensions to value | 
						|
    if(attr.type === oids.extensionRequest) { | 
						|
      attr.valueConstructed = true; | 
						|
      attr.valueTagClass = asn1.Type.SEQUENCE; | 
						|
      if(!attr.value && attr.extensions) { | 
						|
        attr.value = []; | 
						|
        for(var ei = 0; ei < attr.extensions.length; ++ei) { | 
						|
          attr.value.push(pki.certificateExtensionToAsn1( | 
						|
            _fillMissingExtensionFields(attr.extensions[ei]))); | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    if(typeof attr.value === 'undefined') { | 
						|
      var error = new Error('Attribute value not specified.'); | 
						|
      error.attribute = attr; | 
						|
      throw error; | 
						|
    } | 
						|
  } | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Fills in missing fields in certificate extensions. | 
						|
 * | 
						|
 * @param e the extension. | 
						|
 * @param [options] the options to use. | 
						|
 *          [cert] the certificate the extensions are for. | 
						|
 * | 
						|
 * @return the extension. | 
						|
 */ | 
						|
function _fillMissingExtensionFields(e, options) { | 
						|
  options = options || {}; | 
						|
 | 
						|
  // populate missing name | 
						|
  if(typeof e.name === 'undefined') { | 
						|
    if(e.id && e.id in pki.oids) { | 
						|
      e.name = pki.oids[e.id]; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  // populate missing id | 
						|
  if(typeof e.id === 'undefined') { | 
						|
    if(e.name && e.name in pki.oids) { | 
						|
      e.id = pki.oids[e.name]; | 
						|
    } else { | 
						|
      var error = new Error('Extension ID not specified.'); | 
						|
      error.extension = e; | 
						|
      throw error; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  if(typeof e.value !== 'undefined') { | 
						|
    return e; | 
						|
  } | 
						|
 | 
						|
  // handle missing value: | 
						|
 | 
						|
  // value is a BIT STRING | 
						|
  if(e.name === 'keyUsage') { | 
						|
    // build flags | 
						|
    var unused = 0; | 
						|
    var b2 = 0x00; | 
						|
    var b3 = 0x00; | 
						|
    if(e.digitalSignature) { | 
						|
      b2 |= 0x80; | 
						|
      unused = 7; | 
						|
    } | 
						|
    if(e.nonRepudiation) { | 
						|
      b2 |= 0x40; | 
						|
      unused = 6; | 
						|
    } | 
						|
    if(e.keyEncipherment) { | 
						|
      b2 |= 0x20; | 
						|
      unused = 5; | 
						|
    } | 
						|
    if(e.dataEncipherment) { | 
						|
      b2 |= 0x10; | 
						|
      unused = 4; | 
						|
    } | 
						|
    if(e.keyAgreement) { | 
						|
      b2 |= 0x08; | 
						|
      unused = 3; | 
						|
    } | 
						|
    if(e.keyCertSign) { | 
						|
      b2 |= 0x04; | 
						|
      unused = 2; | 
						|
    } | 
						|
    if(e.cRLSign) { | 
						|
      b2 |= 0x02; | 
						|
      unused = 1; | 
						|
    } | 
						|
    if(e.encipherOnly) { | 
						|
      b2 |= 0x01; | 
						|
      unused = 0; | 
						|
    } | 
						|
    if(e.decipherOnly) { | 
						|
      b3 |= 0x80; | 
						|
      unused = 7; | 
						|
    } | 
						|
 | 
						|
    // create bit string | 
						|
    var value = String.fromCharCode(unused); | 
						|
    if(b3 !== 0) { | 
						|
      value += String.fromCharCode(b2) + String.fromCharCode(b3); | 
						|
    } else if(b2 !== 0) { | 
						|
      value += String.fromCharCode(b2); | 
						|
    } | 
						|
    e.value = asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, value); | 
						|
  } else if(e.name === 'basicConstraints') { | 
						|
    // basicConstraints is a SEQUENCE | 
						|
    e.value = asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
    // cA BOOLEAN flag defaults to false | 
						|
    if(e.cA) { | 
						|
      e.value.value.push(asn1.create( | 
						|
        asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false, | 
						|
        String.fromCharCode(0xFF))); | 
						|
    } | 
						|
    if('pathLenConstraint' in e) { | 
						|
      e.value.value.push(asn1.create( | 
						|
        asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, | 
						|
        asn1.integerToDer(e.pathLenConstraint).getBytes())); | 
						|
    } | 
						|
  } else if(e.name === 'extKeyUsage') { | 
						|
    // extKeyUsage is a SEQUENCE of OIDs | 
						|
    e.value = asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
    var seq = e.value.value; | 
						|
    for(var key in e) { | 
						|
      if(e[key] !== true) { | 
						|
        continue; | 
						|
      } | 
						|
      // key is name in OID map | 
						|
      if(key in oids) { | 
						|
        seq.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, | 
						|
          false, asn1.oidToDer(oids[key]).getBytes())); | 
						|
      } else if(key.indexOf('.') !== -1) { | 
						|
        // assume key is an OID | 
						|
        seq.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, | 
						|
          false, asn1.oidToDer(key).getBytes())); | 
						|
      } | 
						|
    } | 
						|
  } else if(e.name === 'nsCertType') { | 
						|
    // nsCertType is a BIT STRING | 
						|
    // build flags | 
						|
    var unused = 0; | 
						|
    var b2 = 0x00; | 
						|
 | 
						|
    if(e.client) { | 
						|
      b2 |= 0x80; | 
						|
      unused = 7; | 
						|
    } | 
						|
    if(e.server) { | 
						|
      b2 |= 0x40; | 
						|
      unused = 6; | 
						|
    } | 
						|
    if(e.email) { | 
						|
      b2 |= 0x20; | 
						|
      unused = 5; | 
						|
    } | 
						|
    if(e.objsign) { | 
						|
      b2 |= 0x10; | 
						|
      unused = 4; | 
						|
    } | 
						|
    if(e.reserved) { | 
						|
      b2 |= 0x08; | 
						|
      unused = 3; | 
						|
    } | 
						|
    if(e.sslCA) { | 
						|
      b2 |= 0x04; | 
						|
      unused = 2; | 
						|
    } | 
						|
    if(e.emailCA) { | 
						|
      b2 |= 0x02; | 
						|
      unused = 1; | 
						|
    } | 
						|
    if(e.objCA) { | 
						|
      b2 |= 0x01; | 
						|
      unused = 0; | 
						|
    } | 
						|
 | 
						|
    // create bit string | 
						|
    var value = String.fromCharCode(unused); | 
						|
    if(b2 !== 0) { | 
						|
      value += String.fromCharCode(b2); | 
						|
    } | 
						|
    e.value = asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, value); | 
						|
  } else if(e.name === 'subjectAltName' || e.name === 'issuerAltName') { | 
						|
    // SYNTAX SEQUENCE | 
						|
    e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
 | 
						|
    var altName; | 
						|
    for(var n = 0; n < e.altNames.length; ++n) { | 
						|
      altName = e.altNames[n]; | 
						|
      var value = altName.value; | 
						|
      // handle IP | 
						|
      if(altName.type === 7 && altName.ip) { | 
						|
        value = forge.util.bytesFromIP(altName.ip); | 
						|
        if(value === null) { | 
						|
          var error = new Error( | 
						|
            'Extension "ip" value is not a valid IPv4 or IPv6 address.'); | 
						|
          error.extension = e; | 
						|
          throw error; | 
						|
        } | 
						|
      } else if(altName.type === 8) { | 
						|
        // handle OID | 
						|
        if(altName.oid) { | 
						|
          value = asn1.oidToDer(asn1.oidToDer(altName.oid)); | 
						|
        } else { | 
						|
          // deprecated ... convert value to OID | 
						|
          value = asn1.oidToDer(value); | 
						|
        } | 
						|
      } | 
						|
      e.value.value.push(asn1.create( | 
						|
        asn1.Class.CONTEXT_SPECIFIC, altName.type, false, | 
						|
        value)); | 
						|
    } | 
						|
  } else if(e.name === 'nsComment' && options.cert) { | 
						|
    // sanity check value is ASCII (req'd) and not too big | 
						|
    if(!(/^[\x00-\x7F]*$/.test(e.comment)) || | 
						|
      (e.comment.length < 1) || (e.comment.length > 128)) { | 
						|
      throw new Error('Invalid "nsComment" content.'); | 
						|
    } | 
						|
    // IA5STRING opaque comment | 
						|
    e.value = asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.IA5STRING, false, e.comment); | 
						|
  } else if(e.name === 'subjectKeyIdentifier' && options.cert) { | 
						|
    var ski = options.cert.generateSubjectKeyIdentifier(); | 
						|
    e.subjectKeyIdentifier = ski.toHex(); | 
						|
    // OCTETSTRING w/digest | 
						|
    e.value = asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, ski.getBytes()); | 
						|
  } else if(e.name === 'authorityKeyIdentifier' && options.cert) { | 
						|
    // SYNTAX SEQUENCE | 
						|
    e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
    var seq = e.value.value; | 
						|
 | 
						|
    if(e.keyIdentifier) { | 
						|
      var keyIdentifier = (e.keyIdentifier === true ? | 
						|
        options.cert.generateSubjectKeyIdentifier().getBytes() : | 
						|
        e.keyIdentifier); | 
						|
      seq.push( | 
						|
        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, false, keyIdentifier)); | 
						|
    } | 
						|
 | 
						|
    if(e.authorityCertIssuer) { | 
						|
      var authorityCertIssuer = [ | 
						|
        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 4, true, [ | 
						|
          _dnToAsn1(e.authorityCertIssuer === true ? | 
						|
            options.cert.issuer : e.authorityCertIssuer) | 
						|
        ]) | 
						|
      ]; | 
						|
      seq.push( | 
						|
        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, authorityCertIssuer)); | 
						|
    } | 
						|
 | 
						|
    if(e.serialNumber) { | 
						|
      var serialNumber = forge.util.hexToBytes(e.serialNumber === true ? | 
						|
        options.cert.serialNumber : e.serialNumber); | 
						|
      seq.push( | 
						|
        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, false, serialNumber)); | 
						|
    } | 
						|
  } else if(e.name === 'cRLDistributionPoints') { | 
						|
    e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
    var seq = e.value.value; | 
						|
 | 
						|
    // Create sub SEQUENCE of DistributionPointName | 
						|
    var subSeq = asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
 | 
						|
    // Create fullName CHOICE | 
						|
    var fullNameGeneralNames = asn1.create( | 
						|
      asn1.Class.CONTEXT_SPECIFIC, 0, true, []); | 
						|
    var altName; | 
						|
    for(var n = 0; n < e.altNames.length; ++n) { | 
						|
      altName = e.altNames[n]; | 
						|
      var value = altName.value; | 
						|
      // handle IP | 
						|
      if(altName.type === 7 && altName.ip) { | 
						|
        value = forge.util.bytesFromIP(altName.ip); | 
						|
        if(value === null) { | 
						|
          var error = new Error( | 
						|
            'Extension "ip" value is not a valid IPv4 or IPv6 address.'); | 
						|
          error.extension = e; | 
						|
          throw error; | 
						|
        } | 
						|
      } else if(altName.type === 8) { | 
						|
        // handle OID | 
						|
        if(altName.oid) { | 
						|
          value = asn1.oidToDer(asn1.oidToDer(altName.oid)); | 
						|
        } else { | 
						|
          // deprecated ... convert value to OID | 
						|
          value = asn1.oidToDer(value); | 
						|
        } | 
						|
      } | 
						|
      fullNameGeneralNames.value.push(asn1.create( | 
						|
        asn1.Class.CONTEXT_SPECIFIC, altName.type, false, | 
						|
        value)); | 
						|
    } | 
						|
 | 
						|
    // Add to the parent SEQUENCE | 
						|
    subSeq.value.push(asn1.create( | 
						|
      asn1.Class.CONTEXT_SPECIFIC, 0, true, [fullNameGeneralNames])); | 
						|
    seq.push(subSeq); | 
						|
  } | 
						|
 | 
						|
  // ensure value has been defined by now | 
						|
  if(typeof e.value === 'undefined') { | 
						|
    var error = new Error('Extension value not specified.'); | 
						|
    error.extension = e; | 
						|
    throw error; | 
						|
  } | 
						|
 | 
						|
  return e; | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Convert signature parameters object to ASN.1 | 
						|
 * | 
						|
 * @param {String} oid Signature algorithm OID | 
						|
 * @param params The signature parametrs object | 
						|
 * @return ASN.1 object representing signature parameters | 
						|
 */ | 
						|
function _signatureParametersToAsn1(oid, params) { | 
						|
  switch(oid) { | 
						|
    case oids['RSASSA-PSS']: | 
						|
      var parts = []; | 
						|
 | 
						|
      if(params.hash.algorithmOid !== undefined) { | 
						|
        parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ | 
						|
          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
              asn1.oidToDer(params.hash.algorithmOid).getBytes()), | 
						|
            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '') | 
						|
          ]) | 
						|
        ])); | 
						|
      } | 
						|
 | 
						|
      if(params.mgf.algorithmOid !== undefined) { | 
						|
        parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [ | 
						|
          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
              asn1.oidToDer(params.mgf.algorithmOid).getBytes()), | 
						|
            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
              asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
                asn1.oidToDer(params.mgf.hash.algorithmOid).getBytes()), | 
						|
              asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '') | 
						|
            ]) | 
						|
          ]) | 
						|
        ])); | 
						|
      } | 
						|
 | 
						|
      if(params.saltLength !== undefined) { | 
						|
        parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [ | 
						|
          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, | 
						|
            asn1.integerToDer(params.saltLength).getBytes()) | 
						|
        ])); | 
						|
      } | 
						|
 | 
						|
      return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, parts); | 
						|
 | 
						|
    default: | 
						|
      return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, ''); | 
						|
  } | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Converts a certification request's attributes to an ASN.1 set of | 
						|
 * CRIAttributes. | 
						|
 * | 
						|
 * @param csr certification request. | 
						|
 * | 
						|
 * @return the ASN.1 set of CRIAttributes. | 
						|
 */ | 
						|
function _CRIAttributesToAsn1(csr) { | 
						|
  // create an empty context-specific container | 
						|
  var rval = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, []); | 
						|
 | 
						|
  // no attributes, return empty container | 
						|
  if(csr.attributes.length === 0) { | 
						|
    return rval; | 
						|
  } | 
						|
 | 
						|
  // each attribute has a sequence with a type and a set of values | 
						|
  var attrs = csr.attributes; | 
						|
  for(var i = 0; i < attrs.length; ++i) { | 
						|
    var attr = attrs[i]; | 
						|
    var value = attr.value; | 
						|
 | 
						|
    // reuse tag class for attribute value if available | 
						|
    var valueTagClass = asn1.Type.UTF8; | 
						|
    if('valueTagClass' in attr) { | 
						|
      valueTagClass = attr.valueTagClass; | 
						|
    } | 
						|
    if(valueTagClass === asn1.Type.UTF8) { | 
						|
      value = forge.util.encodeUtf8(value); | 
						|
    } | 
						|
    var valueConstructed = false; | 
						|
    if('valueConstructed' in attr) { | 
						|
      valueConstructed = attr.valueConstructed; | 
						|
    } | 
						|
    // FIXME: handle more encodings | 
						|
 | 
						|
    // create a RelativeDistinguishedName set | 
						|
    // each value in the set is an AttributeTypeAndValue first | 
						|
    // containing the type (an OID) and second the value | 
						|
    var seq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
      // AttributeType | 
						|
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
        asn1.oidToDer(attr.type).getBytes()), | 
						|
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [ | 
						|
        // AttributeValue | 
						|
        asn1.create( | 
						|
          asn1.Class.UNIVERSAL, valueTagClass, valueConstructed, value) | 
						|
      ]) | 
						|
    ]); | 
						|
    rval.value.push(seq); | 
						|
  } | 
						|
 | 
						|
  return rval; | 
						|
} | 
						|
 | 
						|
var jan_1_1950 = new Date('1950-01-01T00:00:00Z'); | 
						|
var jan_1_2050 = new Date('2050-01-01T00:00:00Z'); | 
						|
 | 
						|
/** | 
						|
 * Converts a Date object to ASN.1 | 
						|
 * Handles the different format before and after 1st January 2050 | 
						|
 * | 
						|
 * @param date date object. | 
						|
 * | 
						|
 * @return the ASN.1 object representing the date. | 
						|
 */ | 
						|
function _dateToAsn1(date) { | 
						|
  if(date >= jan_1_1950 && date < jan_1_2050) { | 
						|
    return asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false, | 
						|
      asn1.dateToUtcTime(date)); | 
						|
  } else { | 
						|
    return asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false, | 
						|
      asn1.dateToGeneralizedTime(date)); | 
						|
  } | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Gets the ASN.1 TBSCertificate part of an X.509v3 certificate. | 
						|
 * | 
						|
 * @param cert the certificate. | 
						|
 * | 
						|
 * @return the asn1 TBSCertificate. | 
						|
 */ | 
						|
pki.getTBSCertificate = function(cert) { | 
						|
  // TBSCertificate | 
						|
  var notBefore = _dateToAsn1(cert.validity.notBefore); | 
						|
  var notAfter = _dateToAsn1(cert.validity.notAfter); | 
						|
  var tbs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
    // version | 
						|
    asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ | 
						|
      // integer | 
						|
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, | 
						|
        asn1.integerToDer(cert.version).getBytes()) | 
						|
    ]), | 
						|
    // serialNumber | 
						|
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, | 
						|
      forge.util.hexToBytes(cert.serialNumber)), | 
						|
    // signature | 
						|
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
      // algorithm | 
						|
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
        asn1.oidToDer(cert.siginfo.algorithmOid).getBytes()), | 
						|
      // parameters | 
						|
      _signatureParametersToAsn1( | 
						|
        cert.siginfo.algorithmOid, cert.siginfo.parameters) | 
						|
    ]), | 
						|
    // issuer | 
						|
    _dnToAsn1(cert.issuer), | 
						|
    // validity | 
						|
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
      notBefore, | 
						|
      notAfter | 
						|
    ]), | 
						|
    // subject | 
						|
    _dnToAsn1(cert.subject), | 
						|
    // SubjectPublicKeyInfo | 
						|
    pki.publicKeyToAsn1(cert.publicKey) | 
						|
  ]); | 
						|
 | 
						|
  if(cert.issuer.uniqueId) { | 
						|
    // issuerUniqueID (optional) | 
						|
    tbs.value.push( | 
						|
      asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [ | 
						|
        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, | 
						|
          // TODO: support arbitrary bit length ids | 
						|
          String.fromCharCode(0x00) + | 
						|
          cert.issuer.uniqueId | 
						|
        ) | 
						|
      ]) | 
						|
    ); | 
						|
  } | 
						|
  if(cert.subject.uniqueId) { | 
						|
    // subjectUniqueID (optional) | 
						|
    tbs.value.push( | 
						|
      asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [ | 
						|
        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, | 
						|
          // TODO: support arbitrary bit length ids | 
						|
          String.fromCharCode(0x00) + | 
						|
          cert.subject.uniqueId | 
						|
        ) | 
						|
      ]) | 
						|
    ); | 
						|
  } | 
						|
 | 
						|
  if(cert.extensions.length > 0) { | 
						|
    // extensions (optional) | 
						|
    tbs.value.push(pki.certificateExtensionsToAsn1(cert.extensions)); | 
						|
  } | 
						|
 | 
						|
  return tbs; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Gets the ASN.1 CertificationRequestInfo part of a | 
						|
 * PKCS#10 CertificationRequest. | 
						|
 * | 
						|
 * @param csr the certification request. | 
						|
 * | 
						|
 * @return the asn1 CertificationRequestInfo. | 
						|
 */ | 
						|
pki.getCertificationRequestInfo = function(csr) { | 
						|
  // CertificationRequestInfo | 
						|
  var cri = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
    // version | 
						|
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, | 
						|
      asn1.integerToDer(csr.version).getBytes()), | 
						|
    // subject | 
						|
    _dnToAsn1(csr.subject), | 
						|
    // SubjectPublicKeyInfo | 
						|
    pki.publicKeyToAsn1(csr.publicKey), | 
						|
    // attributes | 
						|
    _CRIAttributesToAsn1(csr) | 
						|
  ]); | 
						|
 | 
						|
  return cri; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts a DistinguishedName (subject or issuer) to an ASN.1 object. | 
						|
 * | 
						|
 * @param dn the DistinguishedName. | 
						|
 * | 
						|
 * @return the asn1 representation of a DistinguishedName. | 
						|
 */ | 
						|
pki.distinguishedNameToAsn1 = function(dn) { | 
						|
  return _dnToAsn1(dn); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts an X.509v3 RSA certificate to an ASN.1 object. | 
						|
 * | 
						|
 * @param cert the certificate. | 
						|
 * | 
						|
 * @return the asn1 representation of an X.509v3 RSA certificate. | 
						|
 */ | 
						|
pki.certificateToAsn1 = function(cert) { | 
						|
  // prefer cached TBSCertificate over generating one | 
						|
  var tbsCertificate = cert.tbsCertificate || pki.getTBSCertificate(cert); | 
						|
 | 
						|
  // Certificate | 
						|
  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
    // TBSCertificate | 
						|
    tbsCertificate, | 
						|
    // AlgorithmIdentifier (signature algorithm) | 
						|
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
      // algorithm | 
						|
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
        asn1.oidToDer(cert.signatureOid).getBytes()), | 
						|
      // parameters | 
						|
      _signatureParametersToAsn1(cert.signatureOid, cert.signatureParameters) | 
						|
    ]), | 
						|
    // SignatureValue | 
						|
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, | 
						|
      String.fromCharCode(0x00) + cert.signature) | 
						|
  ]); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts X.509v3 certificate extensions to ASN.1. | 
						|
 * | 
						|
 * @param exts the extensions to convert. | 
						|
 * | 
						|
 * @return the extensions in ASN.1 format. | 
						|
 */ | 
						|
pki.certificateExtensionsToAsn1 = function(exts) { | 
						|
  // create top-level extension container | 
						|
  var rval = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 3, true, []); | 
						|
 | 
						|
  // create extension sequence (stores a sequence for each extension) | 
						|
  var seq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
  rval.value.push(seq); | 
						|
 | 
						|
  for(var i = 0; i < exts.length; ++i) { | 
						|
    seq.value.push(pki.certificateExtensionToAsn1(exts[i])); | 
						|
  } | 
						|
 | 
						|
  return rval; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts a single certificate extension to ASN.1. | 
						|
 * | 
						|
 * @param ext the extension to convert. | 
						|
 * | 
						|
 * @return the extension in ASN.1 format. | 
						|
 */ | 
						|
pki.certificateExtensionToAsn1 = function(ext) { | 
						|
  // create a sequence for each extension | 
						|
  var extseq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); | 
						|
 | 
						|
  // extnID (OID) | 
						|
  extseq.value.push(asn1.create( | 
						|
    asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
    asn1.oidToDer(ext.id).getBytes())); | 
						|
 | 
						|
  // critical defaults to false | 
						|
  if(ext.critical) { | 
						|
    // critical BOOLEAN DEFAULT FALSE | 
						|
    extseq.value.push(asn1.create( | 
						|
      asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false, | 
						|
      String.fromCharCode(0xFF))); | 
						|
  } | 
						|
 | 
						|
  var value = ext.value; | 
						|
  if(typeof ext.value !== 'string') { | 
						|
    // value is asn.1 | 
						|
    value = asn1.toDer(value).getBytes(); | 
						|
  } | 
						|
 | 
						|
  // extnValue (OCTET STRING) | 
						|
  extseq.value.push(asn1.create( | 
						|
    asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, value)); | 
						|
 | 
						|
  return extseq; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Converts a PKCS#10 certification request to an ASN.1 object. | 
						|
 * | 
						|
 * @param csr the certification request. | 
						|
 * | 
						|
 * @return the asn1 representation of a certification request. | 
						|
 */ | 
						|
pki.certificationRequestToAsn1 = function(csr) { | 
						|
  // prefer cached CertificationRequestInfo over generating one | 
						|
  var cri = csr.certificationRequestInfo || | 
						|
    pki.getCertificationRequestInfo(csr); | 
						|
 | 
						|
  // Certificate | 
						|
  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
    // CertificationRequestInfo | 
						|
    cri, | 
						|
    // AlgorithmIdentifier (signature algorithm) | 
						|
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | 
						|
      // algorithm | 
						|
      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, | 
						|
        asn1.oidToDer(csr.signatureOid).getBytes()), | 
						|
      // parameters | 
						|
      _signatureParametersToAsn1(csr.signatureOid, csr.signatureParameters) | 
						|
    ]), | 
						|
    // signature | 
						|
    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, | 
						|
      String.fromCharCode(0x00) + csr.signature) | 
						|
  ]); | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Creates a CA store. | 
						|
 * | 
						|
 * @param certs an optional array of certificate objects or PEM-formatted | 
						|
 *          certificate strings to add to the CA store. | 
						|
 * | 
						|
 * @return the CA store. | 
						|
 */ | 
						|
pki.createCaStore = function(certs) { | 
						|
  // create CA store | 
						|
  var caStore = { | 
						|
    // stored certificates | 
						|
    certs: {} | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Gets the certificate that issued the passed certificate or its | 
						|
   * 'parent'. | 
						|
   * | 
						|
   * @param cert the certificate to get the parent for. | 
						|
   * | 
						|
   * @return the parent certificate or null if none was found. | 
						|
   */ | 
						|
  caStore.getIssuer = function(cert) { | 
						|
    var rval = getBySubject(cert.issuer); | 
						|
 | 
						|
    // see if there are multiple matches | 
						|
    /*if(forge.util.isArray(rval)) { | 
						|
      // TODO: resolve multiple matches by checking | 
						|
      // authorityKey/subjectKey/issuerUniqueID/other identifiers, etc. | 
						|
      // FIXME: or alternatively do authority key mapping | 
						|
      // if possible (X.509v1 certs can't work?) | 
						|
      throw new Error('Resolving multiple issuer matches not implemented yet.'); | 
						|
    }*/ | 
						|
 | 
						|
    return rval; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Adds a trusted certificate to the store. | 
						|
   * | 
						|
   * @param cert the certificate to add as a trusted certificate (either a | 
						|
   *          pki.certificate object or a PEM-formatted certificate). | 
						|
   */ | 
						|
  caStore.addCertificate = function(cert) { | 
						|
    // convert from pem if necessary | 
						|
    if(typeof cert === 'string') { | 
						|
      cert = forge.pki.certificateFromPem(cert); | 
						|
    } | 
						|
 | 
						|
    ensureSubjectHasHash(cert.subject); | 
						|
 | 
						|
    if(!caStore.hasCertificate(cert)) { // avoid duplicate certificates in store | 
						|
      if(cert.subject.hash in caStore.certs) { | 
						|
        // subject hash already exists, append to array | 
						|
        var tmp = caStore.certs[cert.subject.hash]; | 
						|
        if(!forge.util.isArray(tmp)) { | 
						|
          tmp = [tmp]; | 
						|
        } | 
						|
        tmp.push(cert); | 
						|
        caStore.certs[cert.subject.hash] = tmp; | 
						|
      } else { | 
						|
        caStore.certs[cert.subject.hash] = cert; | 
						|
      } | 
						|
    } | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Checks to see if the given certificate is in the store. | 
						|
   * | 
						|
   * @param cert the certificate to check (either a pki.certificate or a | 
						|
   *          PEM-formatted certificate). | 
						|
   * | 
						|
   * @return true if the certificate is in the store, false if not. | 
						|
   */ | 
						|
  caStore.hasCertificate = function(cert) { | 
						|
    // convert from pem if necessary | 
						|
    if(typeof cert === 'string') { | 
						|
      cert = forge.pki.certificateFromPem(cert); | 
						|
    } | 
						|
 | 
						|
    var match = getBySubject(cert.subject); | 
						|
    if(!match) { | 
						|
      return false; | 
						|
    } | 
						|
    if(!forge.util.isArray(match)) { | 
						|
      match = [match]; | 
						|
    } | 
						|
    // compare DER-encoding of certificates | 
						|
    var der1 = asn1.toDer(pki.certificateToAsn1(cert)).getBytes(); | 
						|
    for(var i = 0; i < match.length; ++i) { | 
						|
      var der2 = asn1.toDer(pki.certificateToAsn1(match[i])).getBytes(); | 
						|
      if(der1 === der2) { | 
						|
        return true; | 
						|
      } | 
						|
    } | 
						|
    return false; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Lists all of the certificates kept in the store. | 
						|
   * | 
						|
   * @return an array of all of the pki.certificate objects in the store. | 
						|
   */ | 
						|
  caStore.listAllCertificates = function() { | 
						|
    var certList = []; | 
						|
 | 
						|
    for(var hash in caStore.certs) { | 
						|
      if(caStore.certs.hasOwnProperty(hash)) { | 
						|
        var value = caStore.certs[hash]; | 
						|
        if(!forge.util.isArray(value)) { | 
						|
          certList.push(value); | 
						|
        } else { | 
						|
          for(var i = 0; i < value.length; ++i) { | 
						|
            certList.push(value[i]); | 
						|
          } | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    return certList; | 
						|
  }; | 
						|
 | 
						|
  /** | 
						|
   * Removes a certificate from the store. | 
						|
   * | 
						|
   * @param cert the certificate to remove (either a pki.certificate or a | 
						|
   *          PEM-formatted certificate). | 
						|
   * | 
						|
   * @return the certificate that was removed or null if the certificate | 
						|
   *           wasn't in store. | 
						|
   */ | 
						|
  caStore.removeCertificate = function(cert) { | 
						|
    var result; | 
						|
 | 
						|
    // convert from pem if necessary | 
						|
    if(typeof cert === 'string') { | 
						|
      cert = forge.pki.certificateFromPem(cert); | 
						|
    } | 
						|
    ensureSubjectHasHash(cert.subject); | 
						|
    if(!caStore.hasCertificate(cert)) { | 
						|
      return null; | 
						|
    } | 
						|
 | 
						|
    var match = getBySubject(cert.subject); | 
						|
 | 
						|
    if(!forge.util.isArray(match)) { | 
						|
      result = caStore.certs[cert.subject.hash]; | 
						|
      delete caStore.certs[cert.subject.hash]; | 
						|
      return result; | 
						|
    } | 
						|
 | 
						|
    // compare DER-encoding of certificates | 
						|
    var der1 = asn1.toDer(pki.certificateToAsn1(cert)).getBytes(); | 
						|
    for(var i = 0; i < match.length; ++i) { | 
						|
      var der2 = asn1.toDer(pki.certificateToAsn1(match[i])).getBytes(); | 
						|
      if(der1 === der2) { | 
						|
        result = match[i]; | 
						|
        match.splice(i, 1); | 
						|
      } | 
						|
    } | 
						|
    if(match.length === 0) { | 
						|
      delete caStore.certs[cert.subject.hash]; | 
						|
    } | 
						|
 | 
						|
    return result; | 
						|
  }; | 
						|
 | 
						|
  function getBySubject(subject) { | 
						|
    ensureSubjectHasHash(subject); | 
						|
    return caStore.certs[subject.hash] || null; | 
						|
  } | 
						|
 | 
						|
  function ensureSubjectHasHash(subject) { | 
						|
    // produce subject hash if it doesn't exist | 
						|
    if(!subject.hash) { | 
						|
      var md = forge.md.sha1.create(); | 
						|
      subject.attributes = pki.RDNAttributesAsArray(_dnToAsn1(subject), md); | 
						|
      subject.hash = md.digest().toHex(); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  // auto-add passed in certs | 
						|
  if(certs) { | 
						|
    // parse PEM-formatted certificates as necessary | 
						|
    for(var i = 0; i < certs.length; ++i) { | 
						|
      var cert = certs[i]; | 
						|
      caStore.addCertificate(cert); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return caStore; | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Certificate verification errors, based on TLS. | 
						|
 */ | 
						|
pki.certificateError = { | 
						|
  bad_certificate: 'forge.pki.BadCertificate', | 
						|
  unsupported_certificate: 'forge.pki.UnsupportedCertificate', | 
						|
  certificate_revoked: 'forge.pki.CertificateRevoked', | 
						|
  certificate_expired: 'forge.pki.CertificateExpired', | 
						|
  certificate_unknown: 'forge.pki.CertificateUnknown', | 
						|
  unknown_ca: 'forge.pki.UnknownCertificateAuthority' | 
						|
}; | 
						|
 | 
						|
/** | 
						|
 * Verifies a certificate chain against the given Certificate Authority store | 
						|
 * with an optional custom verify callback. | 
						|
 * | 
						|
 * @param caStore a certificate store to verify against. | 
						|
 * @param chain the certificate chain to verify, with the root or highest | 
						|
 *          authority at the end (an array of certificates). | 
						|
 * @param options a callback to be called for every certificate in the chain or | 
						|
 *                  an object with: | 
						|
 *                  verify a callback to be called for every certificate in the | 
						|
 *                    chain | 
						|
 *                  validityCheckDate the date against which the certificate | 
						|
 *                    validity period should be checked. Pass null to not check | 
						|
 *                    the validity period. By default, the current date is used. | 
						|
 * | 
						|
 * The verify callback has the following signature: | 
						|
 * | 
						|
 * verified - Set to true if certificate was verified, otherwise the | 
						|
 *   pki.certificateError for why the certificate failed. | 
						|
 * depth - The current index in the chain, where 0 is the end point's cert. | 
						|
 * certs - The certificate chain, *NOTE* an empty chain indicates an anonymous | 
						|
 *   end point. | 
						|
 * | 
						|
 * The function returns true on success and on failure either the appropriate | 
						|
 * pki.certificateError or an object with 'error' set to the appropriate | 
						|
 * pki.certificateError and 'message' set to a custom error message. | 
						|
 * | 
						|
 * @return true if successful, error thrown if not. | 
						|
 */ | 
						|
pki.verifyCertificateChain = function(caStore, chain, options) { | 
						|
  /* From: RFC3280 - Internet X.509 Public Key Infrastructure Certificate | 
						|
    Section 6: Certification Path Validation | 
						|
    See inline parentheticals related to this particular implementation. | 
						|
 | 
						|
    The primary goal of path validation is to verify the binding between | 
						|
    a subject distinguished name or a subject alternative name and subject | 
						|
    public key, as represented in the end entity certificate, based on the | 
						|
    public key of the trust anchor. This requires obtaining a sequence of | 
						|
    certificates that support that binding. That sequence should be provided | 
						|
    in the passed 'chain'. The trust anchor should be in the given CA | 
						|
    store. The 'end entity' certificate is the certificate provided by the | 
						|
    end point (typically a server) and is the first in the chain. | 
						|
 | 
						|
    To meet this goal, the path validation process verifies, among other | 
						|
    things, that a prospective certification path (a sequence of n | 
						|
    certificates or a 'chain') satisfies the following conditions: | 
						|
 | 
						|
    (a) for all x in {1, ..., n-1}, the subject of certificate x is | 
						|
          the issuer of certificate x+1; | 
						|
 | 
						|
    (b) certificate 1 is issued by the trust anchor; | 
						|
 | 
						|
    (c) certificate n is the certificate to be validated; and | 
						|
 | 
						|
    (d) for all x in {1, ..., n}, the certificate was valid at the | 
						|
          time in question. | 
						|
 | 
						|
    Note that here 'n' is index 0 in the chain and 1 is the last certificate | 
						|
    in the chain and it must be signed by a certificate in the connection's | 
						|
    CA store. | 
						|
 | 
						|
    The path validation process also determines the set of certificate | 
						|
    policies that are valid for this path, based on the certificate policies | 
						|
    extension, policy mapping extension, policy constraints extension, and | 
						|
    inhibit any-policy extension. | 
						|
 | 
						|
    Note: Policy mapping extension not supported (Not Required). | 
						|
 | 
						|
    Note: If the certificate has an unsupported critical extension, then it | 
						|
    must be rejected. | 
						|
 | 
						|
    Note: A certificate is self-issued if the DNs that appear in the subject | 
						|
    and issuer fields are identical and are not empty. | 
						|
 | 
						|
    The path validation algorithm assumes the following seven inputs are | 
						|
    provided to the path processing logic. What this specific implementation | 
						|
    will use is provided parenthetically: | 
						|
 | 
						|
    (a) a prospective certification path of length n (the 'chain') | 
						|
    (b) the current date/time: ('now'). | 
						|
    (c) user-initial-policy-set: A set of certificate policy identifiers | 
						|
          naming the policies that are acceptable to the certificate user. | 
						|
          The user-initial-policy-set contains the special value any-policy | 
						|
          if the user is not concerned about certificate policy | 
						|
          (Not implemented. Any policy is accepted). | 
						|
    (d) trust anchor information, describing a CA that serves as a trust | 
						|
          anchor for the certification path. The trust anchor information | 
						|
          includes: | 
						|
 | 
						|
      (1)  the trusted issuer name, | 
						|
      (2)  the trusted public key algorithm, | 
						|
      (3)  the trusted public key, and | 
						|
      (4)  optionally, the trusted public key parameters associated | 
						|
             with the public key. | 
						|
 | 
						|
      (Trust anchors are provided via certificates in the CA store). | 
						|
 | 
						|
      The trust anchor information may be provided to the path processing | 
						|
      procedure in the form of a self-signed certificate. The trusted anchor | 
						|
      information is trusted because it was delivered to the path processing | 
						|
      procedure by some trustworthy out-of-band procedure. If the trusted | 
						|
      public key algorithm requires parameters, then the parameters are | 
						|
      provided along with the trusted public key (No parameters used in this | 
						|
      implementation). | 
						|
 | 
						|
    (e) initial-policy-mapping-inhibit, which indicates if policy mapping is | 
						|
          allowed in the certification path. | 
						|
          (Not implemented, no policy checking) | 
						|
 | 
						|
    (f) initial-explicit-policy, which indicates if the path must be valid | 
						|
          for at least one of the certificate policies in the user-initial- | 
						|
          policy-set. | 
						|
          (Not implemented, no policy checking) | 
						|
 | 
						|
    (g) initial-any-policy-inhibit, which indicates whether the | 
						|
          anyPolicy OID should be processed if it is included in a | 
						|
          certificate. | 
						|
          (Not implemented, so any policy is valid provided that it is | 
						|
          not marked as critical) */ | 
						|
 | 
						|
  /* Basic Path Processing: | 
						|
 | 
						|
    For each certificate in the 'chain', the following is checked: | 
						|
 | 
						|
    1. The certificate validity period includes the current time. | 
						|
    2. The certificate was signed by its parent (where the parent is either | 
						|
       the next in the chain or from the CA store). Allow processing to | 
						|
       continue to the next step if no parent is found but the certificate is | 
						|
       in the CA store. | 
						|
    3. TODO: The certificate has not been revoked. | 
						|
    4. The certificate issuer name matches the parent's subject name. | 
						|
    5. TODO: If the certificate is self-issued and not the final certificate | 
						|
       in the chain, skip this step, otherwise verify that the subject name | 
						|
       is within one of the permitted subtrees of X.500 distinguished names | 
						|
       and that each of the alternative names in the subjectAltName extension | 
						|
       (critical or non-critical) is within one of the permitted subtrees for | 
						|
       that name type. | 
						|
    6. TODO: If the certificate is self-issued and not the final certificate | 
						|
       in the chain, skip this step, otherwise verify that the subject name | 
						|
       is not within one of the excluded subtrees for X.500 distinguished | 
						|
       names and none of the subjectAltName extension names are excluded for | 
						|
       that name type. | 
						|
    7. The other steps in the algorithm for basic path processing involve | 
						|
       handling the policy extension which is not presently supported in this | 
						|
       implementation. Instead, if a critical policy extension is found, the | 
						|
       certificate is rejected as not supported. | 
						|
    8. If the certificate is not the first or if its the only certificate in | 
						|
       the chain (having no parent from the CA store or is self-signed) and it | 
						|
       has a critical key usage extension, verify that the keyCertSign bit is | 
						|
       set. If the key usage extension exists, verify that the basic | 
						|
       constraints extension exists. If the basic constraints extension exists, | 
						|
       verify that the cA flag is set. If pathLenConstraint is set, ensure that | 
						|
       the number of certificates that precede in the chain (come earlier | 
						|
       in the chain as implemented below), excluding the very first in the | 
						|
       chain (typically the end-entity one), isn't greater than the | 
						|
       pathLenConstraint. This constraint limits the number of intermediate | 
						|
       CAs that may appear below a CA before only end-entity certificates | 
						|
       may be issued. */ | 
						|
 | 
						|
  // if a verify callback is passed as the third parameter, package it within | 
						|
  // the options object. This is to support a legacy function signature that | 
						|
  // expected the verify callback as the third parameter. | 
						|
  if(typeof options === 'function') { | 
						|
    options = {verify: options}; | 
						|
  } | 
						|
  options = options || {}; | 
						|
 | 
						|
  // copy cert chain references to another array to protect against changes | 
						|
  // in verify callback | 
						|
  chain = chain.slice(0); | 
						|
  var certs = chain.slice(0); | 
						|
 | 
						|
  var validityCheckDate = options.validityCheckDate; | 
						|
  // if no validityCheckDate is specified, default to the current date. Make | 
						|
  // sure to maintain the value null because it indicates that the validity | 
						|
  // period should not be checked. | 
						|
  if(typeof validityCheckDate === 'undefined') { | 
						|
    validityCheckDate = new Date(); | 
						|
  } | 
						|
 | 
						|
  // verify each cert in the chain using its parent, where the parent | 
						|
  // is either the next in the chain or from the CA store | 
						|
  var first = true; | 
						|
  var error = null; | 
						|
  var depth = 0; | 
						|
  do { | 
						|
    var cert = chain.shift(); | 
						|
    var parent = null; | 
						|
    var selfSigned = false; | 
						|
 | 
						|
    if(validityCheckDate) { | 
						|
      // 1. check valid time | 
						|
      if(validityCheckDate < cert.validity.notBefore || | 
						|
         validityCheckDate > cert.validity.notAfter) { | 
						|
        error = { | 
						|
          message: 'Certificate is not valid yet or has expired.', | 
						|
          error: pki.certificateError.certificate_expired, | 
						|
          notBefore: cert.validity.notBefore, | 
						|
          notAfter: cert.validity.notAfter, | 
						|
          // TODO: we might want to reconsider renaming 'now' to | 
						|
          // 'validityCheckDate' should this API be changed in the future. | 
						|
          now: validityCheckDate | 
						|
        }; | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // 2. verify with parent from chain or CA store | 
						|
    if(error === null) { | 
						|
      parent = chain[0] || caStore.getIssuer(cert); | 
						|
      if(parent === null) { | 
						|
        // check for self-signed cert | 
						|
        if(cert.isIssuer(cert)) { | 
						|
          selfSigned = true; | 
						|
          parent = cert; | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      if(parent) { | 
						|
        // FIXME: current CA store implementation might have multiple | 
						|
        // certificates where the issuer can't be determined from the | 
						|
        // certificate (happens rarely with, eg: old certificates) so normalize | 
						|
        // by always putting parents into an array | 
						|
        // TODO: there's may be an extreme degenerate case currently uncovered | 
						|
        // where an old intermediate certificate seems to have a matching parent | 
						|
        // but none of the parents actually verify ... but the intermediate | 
						|
        // is in the CA and it should pass this check; needs investigation | 
						|
        var parents = parent; | 
						|
        if(!forge.util.isArray(parents)) { | 
						|
          parents = [parents]; | 
						|
        } | 
						|
 | 
						|
        // try to verify with each possible parent (typically only one) | 
						|
        var verified = false; | 
						|
        while(!verified && parents.length > 0) { | 
						|
          parent = parents.shift(); | 
						|
          try { | 
						|
            verified = parent.verify(cert); | 
						|
          } catch(ex) { | 
						|
            // failure to verify, don't care why, try next one | 
						|
          } | 
						|
        } | 
						|
 | 
						|
        if(!verified) { | 
						|
          error = { | 
						|
            message: 'Certificate signature is invalid.', | 
						|
            error: pki.certificateError.bad_certificate | 
						|
          }; | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      if(error === null && (!parent || selfSigned) && | 
						|
        !caStore.hasCertificate(cert)) { | 
						|
        // no parent issuer and certificate itself is not trusted | 
						|
        error = { | 
						|
          message: 'Certificate is not trusted.', | 
						|
          error: pki.certificateError.unknown_ca | 
						|
        }; | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // TODO: 3. check revoked | 
						|
 | 
						|
    // 4. check for matching issuer/subject | 
						|
    if(error === null && parent && !cert.isIssuer(parent)) { | 
						|
      // parent is not issuer | 
						|
      error = { | 
						|
        message: 'Certificate issuer is invalid.', | 
						|
        error: pki.certificateError.bad_certificate | 
						|
      }; | 
						|
    } | 
						|
 | 
						|
    // 5. TODO: check names with permitted names tree | 
						|
 | 
						|
    // 6. TODO: check names against excluded names tree | 
						|
 | 
						|
    // 7. check for unsupported critical extensions | 
						|
    if(error === null) { | 
						|
      // supported extensions | 
						|
      var se = { | 
						|
        keyUsage: true, | 
						|
        basicConstraints: true | 
						|
      }; | 
						|
      for(var i = 0; error === null && i < cert.extensions.length; ++i) { | 
						|
        var ext = cert.extensions[i]; | 
						|
        if(ext.critical && !(ext.name in se)) { | 
						|
          error = { | 
						|
            message: | 
						|
              'Certificate has an unsupported critical extension.', | 
						|
            error: pki.certificateError.unsupported_certificate | 
						|
          }; | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // 8. check for CA if cert is not first or is the only certificate | 
						|
    // remaining in chain with no parent or is self-signed | 
						|
    if(error === null && | 
						|
      (!first || (chain.length === 0 && (!parent || selfSigned)))) { | 
						|
      // first check keyUsage extension and then basic constraints | 
						|
      var bcExt = cert.getExtension('basicConstraints'); | 
						|
      var keyUsageExt = cert.getExtension('keyUsage'); | 
						|
      if(keyUsageExt !== null) { | 
						|
        // keyCertSign must be true and there must be a basic | 
						|
        // constraints extension | 
						|
        if(!keyUsageExt.keyCertSign || bcExt === null) { | 
						|
          // bad certificate | 
						|
          error = { | 
						|
            message: | 
						|
              'Certificate keyUsage or basicConstraints conflict ' + | 
						|
              'or indicate that the certificate is not a CA. ' + | 
						|
              'If the certificate is the only one in the chain or ' + | 
						|
              'isn\'t the first then the certificate must be a ' + | 
						|
              'valid CA.', | 
						|
            error: pki.certificateError.bad_certificate | 
						|
          }; | 
						|
        } | 
						|
      } | 
						|
      // basic constraints cA flag must be set | 
						|
      if(error === null && bcExt !== null && !bcExt.cA) { | 
						|
        // bad certificate | 
						|
        error = { | 
						|
          message: | 
						|
            'Certificate basicConstraints indicates the certificate ' + | 
						|
            'is not a CA.', | 
						|
          error: pki.certificateError.bad_certificate | 
						|
        }; | 
						|
      } | 
						|
      // if error is not null and keyUsage is available, then we know it | 
						|
      // has keyCertSign and there is a basic constraints extension too, | 
						|
      // which means we can check pathLenConstraint (if it exists) | 
						|
      if(error === null && keyUsageExt !== null && | 
						|
        'pathLenConstraint' in bcExt) { | 
						|
        // pathLen is the maximum # of intermediate CA certs that can be | 
						|
        // found between the current certificate and the end-entity (depth 0) | 
						|
        // certificate; this number does not include the end-entity (depth 0, | 
						|
        // last in the chain) even if it happens to be a CA certificate itself | 
						|
        var pathLen = depth - 1; | 
						|
        if(pathLen > bcExt.pathLenConstraint) { | 
						|
          // pathLenConstraint violated, bad certificate | 
						|
          error = { | 
						|
            message: | 
						|
              'Certificate basicConstraints pathLenConstraint violated.', | 
						|
            error: pki.certificateError.bad_certificate | 
						|
          }; | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    // call application callback | 
						|
    var vfd = (error === null) ? true : error.error; | 
						|
    var ret = options.verify ? options.verify(vfd, depth, certs) : vfd; | 
						|
    if(ret === true) { | 
						|
      // clear any set error | 
						|
      error = null; | 
						|
    } else { | 
						|
      // if passed basic tests, set default message and alert | 
						|
      if(vfd === true) { | 
						|
        error = { | 
						|
          message: 'The application rejected the certificate.', | 
						|
          error: pki.certificateError.bad_certificate | 
						|
        }; | 
						|
      } | 
						|
 | 
						|
      // check for custom error info | 
						|
      if(ret || ret === 0) { | 
						|
        // set custom message and error | 
						|
        if(typeof ret === 'object' && !forge.util.isArray(ret)) { | 
						|
          if(ret.message) { | 
						|
            error.message = ret.message; | 
						|
          } | 
						|
          if(ret.error) { | 
						|
            error.error = ret.error; | 
						|
          } | 
						|
        } else if(typeof ret === 'string') { | 
						|
          // set custom error | 
						|
          error.error = ret; | 
						|
        } | 
						|
      } | 
						|
 | 
						|
      // throw error | 
						|
      throw error; | 
						|
    } | 
						|
 | 
						|
    // no longer first cert in chain | 
						|
    first = false; | 
						|
    ++depth; | 
						|
  } while(chain.length > 0); | 
						|
 | 
						|
  return true; | 
						|
};
 | 
						|
 |