| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 | /* * Copyright 2018 Scytl Secure Electronic Voting SA * * All rights reserved * * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package */cryptolib.modules.stores = cryptolib.modules.stores || {};/** * Defines a pkcs12 store. */cryptolib.modules.stores.pkcs12 = function(box) {  'use strict';  box.stores = box.stores || {};  box.stores.pkcs12 = {};  var converters;  var exceptions;  cryptolib('commons', function(box) {    converters = new box.commons.utils.Converters();    exceptions = box.commons.exceptions;  });  /**   * Defines a PKCS12.   *   * @param pkcs12DerB64   *            PKCS12, as string in Base64 encoded DER format.   */  box.stores.pkcs12.Pkcs12 = function(pkcs12DerB64) {    this.pkcs12Der = converters.base64Decode(pkcs12DerB64);    this.pkcs12Asn1 = forge.asn1.fromDer(this.pkcs12Der, false);  };  box.stores.pkcs12.Pkcs12.prototype = {    /**     * Retrieves the private key of the PKCS12.     *     * @param passwordB64     *            Password as string in Base64 encoded format.     * @return Private key of PKCS12, as string in PEM format.     */    getPrivateKey: function(passwordB64) {      var aliases = Array.prototype.slice.call(arguments, 1);      function isAliasInList(alias) {        var returnValue = false;        if (aliases.indexOf(alias) >= 0) {          returnValue = true;        }        return returnValue;      }      var privateKeyAlias = '';      try {        var password = converters.base64Decode(passwordB64);        var pkcs12 =            forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);        // Retrieve private key safe bag from PKCS12.        var privateKeys = {};        for (var i = 0; i < pkcs12.safeContents.length; i++) {          var safeContents = pkcs12.safeContents[i];          for (var j = 0; j < safeContents.safeBags.length; j++) {            var safeBag = safeContents.safeBags[j];            if (safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag) {              privateKeyAlias = safeBag.attributes.friendlyName[0];              if (aliases.length === 0 || isAliasInList(privateKeyAlias)) {                privateKeys[privateKeyAlias] =                    forge.pki.privateKeyToPem(safeBag.key);              }            }          }        }        // Check whether any private key safe bags were found.        if (Object.getOwnPropertyNames(privateKeys).length === 0) {          throw new exceptions.CryptoLibException(              'Could not find any private key safe bags in PKCS12.');        }        return privateKeys;      } catch (error) {        var errorMsg = 'Could not retrieve private key from PKCS12.';        if (privateKeyAlias.length > 0) {          errorMsg = 'Could not retrieve private key with alias ' +              privateKeyAlias + ' from PKCS12.';        }        throw new exceptions.CryptoLibException(errorMsg);      }    },    /**     * Retrieves the private key chain from the PKCS12.     *     * @param passwordB64     *            Password as string in Base64 encoded format.     * @return Private key chain of PKCS12, as PrivateKeyChain object.     */    getPrivateKeyChain: function(passwordB64) {      try {        var password = converters.base64Decode(passwordB64);        var pkcs12 =            forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);        // Retrieve private key safe bags from PKCS12.        var privateKeyChain = new box.stores.pkcs12.PrivateKeyChain();        for (var i = 0; i < pkcs12.safeContents.length; i++) {          var safeContents = pkcs12.safeContents[i];          for (var j = 0; j < safeContents.safeBags.length; j++) {            var safeBag = safeContents.safeBags[j];            if (safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag) {              var alias = safeBag.attributes.friendlyName[0];              var privateKeyPem = forge.pki.privateKeyToPem(safeBag.key);              privateKeyChain.add(alias, privateKeyPem);            }          }        }        // Check whether any private key safe bags were found.        if (privateKeyChain.length === 0) {          throw new exceptions.CryptoLibException(              'Could not find any private key safe bags in PKCS12.');        }        return privateKeyChain;      } catch (error) {        throw new exceptions.CryptoLibException(            'Could not retrieve private key chain from PKCS12.', error);      }    },    /**     * Retrieves the certificate chain of the PKCS12.     *     * @param passwordB64     *            Password as string in Base64 encoded format.     * @return Certificate chain of PKCS12, as array of strings in PEM     *         format.     */    getCertificateChain: function(passwordB64) {      try {        var password = converters.base64Decode(passwordB64);        var pkcs12 =            forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);        // Retrieve certificate safe bags from PKCS12.        var certificatePemChain = [];        for (var i = 0; i < pkcs12.safeContents.length; i++) {          var safeContents = pkcs12.safeContents[i];          for (var j = 0; j < safeContents.safeBags.length; j++) {            var safeBag = safeContents.safeBags[j];            if (safeBag.type === forge.pki.oids.certBag) {              var certificatePem = forge.pki.certificateToPem(safeBag.cert);              certificatePemChain.push(certificatePem);            }          }        }        // Check whether any certificate safe bags were found.        if (certificatePemChain.length === 0) {          throw new exceptions.CryptoLibException(              'Could not find any certificate safe bags in PKCS12.');        }        return certificatePemChain;      } catch (error) {        throw new exceptions.CryptoLibException(            'Could not retrieve certificate chain from PKCS12.', error);      }    },    /**     * Retrieves the certificate chain of the PKCS12 filtered by alias.     *     * @param passwordB64     *            Password as string in Base64 encoded format.     * @param alias     *            Alias to filter     * @return Certificate chain of PKCS12, as array of strings in PEM     *         format.     */    getCertificateChainByAlias: function(passwordB64, alias) {      try {        var password = converters.base64Decode(passwordB64);        var pkcs12 =            forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);        var discardedBags = {};        var nextBagId = null;        // Retrieve certificate safe bags from PKCS12.        var certificatePemChain = [];        for (var i = 0; i < pkcs12.safeContents.length; i++) {          var safeContents = pkcs12.safeContents[i];          for (var j = 0; j < safeContents.safeBags.length; j++) {            var safeBag = safeContents.safeBags[j];            if (safeBag.type === forge.pki.oids.certBag) {              var bagFound = null;              // if not initializated the chain, find the one with alias              // or if is the one that goes next into the list take it              if ((!nextBagId && safeBag.attributes &&                  safeBag.attributes.friendlyName &&                  safeBag.attributes.friendlyName[0] === alias) ||                  (nextBagId && nextBagId === safeBag.cert.subject.hash)) {                bagFound = safeBag;              }              // otherwise, if not initializated and it's not the alias, save it              // for later use              else {                discardedBags[safeBag.cert.subject.hash] = safeBag;              }              // add the bag and check if we have the next one in the list we              // had              while (bagFound) {                // transform the current to PEM and save it                var certificatePem = forge.pki.certificateToPem(bagFound.cert);                certificatePemChain.push(certificatePem);                // we arrived to the root so we are done!                if (bagFound.cert.subject.hash === bagFound.cert.issuer.hash) {                  return certificatePemChain;                }                // save which is the next bag we need                nextBagId = bagFound.cert.issuer.hash;                // go for the next in the list of discarded ones                bagFound = discardedBags[nextBagId];              }            }          }        }        // Check whether any certificate safe bags were found.        if (certificatePemChain.length === 0) {          throw new exceptions.CryptoLibException(              'Could not find any certificates for that alias.');        }        return certificatePemChain;      } catch (error) {        throw new exceptions.CryptoLibException(            'Could not retrieve certificate chain from PKCS12.', error);      }    },    /**     * Retrieves an issuer certificate from the certificate chain of the     * PKCS12, given the issuer common name.     *     * @param passwordB64     *            Password as string in Base64 encoded format.     * @param issuerCn     *            Issuer common name of certificate to retrieve, as string.     * @return Certificate with given issuer common name, as string in PEM     *         format.     */    getCertificateByIssuer: function(passwordB64, issuerCn) {      try {        var password = converters.base64Decode(passwordB64);        var pkcs12 =            forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);        // Retrieve certificate from PKCS12.        var certificate = null;        var certificateFound = false;        for (var i = 0; i < pkcs12.safeContents.length; i++) {          var safeContents = pkcs12.safeContents[i];          for (var j = 0; j < safeContents.safeBags.length; j++) {            var safeBag = safeContents.safeBags[j];            if (safeBag.type === forge.pki.oids.certBag &&                safeBag.cert.issuer.getField('CN').value === issuerCn) {                certificate = safeBag.cert;                certificateFound = true;                break;            }          }          if (certificateFound) {            break;          }        }        // Check whether certificate was found.        if (certificate === null) {          throw new exceptions.CryptoLibException(              'Could not find certificate in PKCS12 with issuer common name ' +              issuerCn);        }        return forge.pki.certificateToPem(certificate);      } catch (error) {        throw new exceptions.CryptoLibException(            'Could not retrieve certificate with issuer common name ' +                issuerCn + ' from PKCS12.',            error);      }    },    /**     * Retrieves the subject certificate from the certificate chain of the     * PKCS12, given the subject common name.     *     * @param passwordB64     *            Password as string in Base64 encoded format.     * @param subjectCn     *            Subject common name of certificate to retrieve, as string.     * @return Certificate with given issuer common name, as string in PEM     *         format.     */    getCertificateBySubject: function(passwordB64, subjectCn) {      try {        var password = converters.base64Decode(passwordB64);        var pkcs12 =            forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);        // Retrieve subject certificate from PKCS12.        var certificate = null;        var certificateFound = false;        for (var i = 0; i < pkcs12.safeContents.length; i++) {          var safeContents = pkcs12.safeContents[i];          for (var j = 0; j < safeContents.safeBags.length; j++) {            var safeBag = safeContents.safeBags[j];            if (safeBag.type === forge.pki.oids.certBag &&                safeBag.cert.subject.getField('CN').value === subjectCn) {                certificate = safeBag.cert;                certificateFound = true;                break;            }          }          if (certificateFound) {            break;          }        }        // Check whether certificate was found.        if (certificate === null) {          throw new exceptions.CryptoLibException(              'Could not find certificate in PKCS12 with subject common name ' +              subjectCn);        }        return forge.pki.certificateToPem(certificate);      } catch (error) {        throw new exceptions.CryptoLibException(            'Could not retrieve certificate with subject common name ' +                subjectCn + ' from PKCS12.',            error);      }    },    /**     * Retrieves the PKCS12 as a Base64 encoded DER string.     *     * @return PKCS12, as Base64 encoded DER string.     */    getBase64Encoded: function() {      return converters.base64Encode(this.pkcs12Der);    }  };  /**   * Container class for a chain of private keys to be inserted in a key   * store.   */  box.stores.pkcs12.PrivateKeyChain = function() {    this.map = {};  };  box.stores.pkcs12.PrivateKeyChain.prototype = {    /**     * Adds a private key to the chain.     *     * @param alias     *            Alias of private key, as string.     * @param privateKeyPem     *            Private key to add, as string in PEM format.     */    add: function(alias, privateKeyPem) {      this.map[alias] = privateKeyPem;    },    /**     * Retrieves a private key from the chain.     *     * @return Private key chain, as array of strings in PEM format.     */    get: function(alias) {      return this.map[alias];    }  };};
 |