123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- /*
- * 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
- */
- /* jshint node:true */
- 'use strict';
- var validator = require('./input-validator');
- var codec = require('scytl-codec');
- var forge = require('node-forge');
- module.exports = Pkcs12KeyStore;
- /**
- * @class Pkcs12KeyStore
- * @classdesc Encapsulates a PKCS #12 key store. To instantiate this object, use
- * the method {@link KeyStoreService.newPkcs12KeyStore}.
- * @hideconstructor
- * @param {Uint8Array|string}
- * keyStore The provided PKCS #12 key store as a DER encoded ASN.1
- * structure <b>OR</b> such a structure Base64 encoded.
- * @param {string}
- * password The password to load the PKCS #12 key store.
- * @throws {Error}
- * If the input data validation fails or the underlying key store
- * could not be loaded.
- */
- function Pkcs12KeyStore(keyStore, password) {
- var pkcs12Der;
- if (typeof keyStore !== 'string') {
- pkcs12Der = keyStore;
- } else {
- validator.checkIsNonEmptyString(
- keyStore, 'PKCS12 key store, Base64 encoded, to load');
- pkcs12Der = codec.base64Decode(keyStore);
- }
- validator.checkIsInstanceOf(
- pkcs12Der, Uint8Array, 'Uint8Array',
- 'PKCS12 key store, DER encoded, to load');
- validator.checkIsType(
- password, 'string', 'Password to load PKCS12 key store');
- var forgePkcs12Asn1_;
- var forgePkcs12_;
- try {
- forgePkcs12Asn1_ = forge.asn1.fromDer(codec.binaryEncode(pkcs12Der), false);
- forgePkcs12_ =
- forge.pkcs12.pkcs12FromAsn1(forgePkcs12Asn1_, false, password);
- } catch (error) {
- throw new Error('Could not load PKCS12 key store; ' + error);
- }
- /**
- * Retrieves a private key stored inside the PKCS #12 key store, given the
- * key's storage alias name and password
- *
- * @function getPrivateKey
- * @memberof Pkcs12KeyStore
- * @param {string}
- * alias The storage alias name of the private key to retrieve.
- * @param {string}
- * password The password used to store the private key.
- * @returns {string} The private key, in PEM format.
- * @throws {Error}
- * If the input data validation fails or the private key could
- * not be retrieved.
- */
- this.getPrivateKey = function(alias, password) {
- validator.checkIsNonEmptyString(
- alias,
- 'Storage alias name of private key to retrieve from PKCS12 key store');
- checkPassword(
- password, forgePkcs12Asn1_,
- 'Password to retrieve private key from PKCS12 key store');
- try {
- var privateKeyPem;
- var privateKeyFound = false;
- for (var i = 0; i < forgePkcs12_.safeContents.length; i++) {
- var safeContents = forgePkcs12_.safeContents[i];
- for (var j = 0; j < safeContents.safeBags.length; j++) {
- var safeBag = safeContents.safeBags[j];
- if (safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag &&
- safeBag.attributes.friendlyName[0] === alias) {
- privateKeyPem = forge.pki.privateKeyToPem(safeBag.key);
- privateKeyFound = true;
- break;
- }
- }
- if (privateKeyFound) {
- break;
- }
- }
- if (!privateKeyPem) {
- throw new Error(
- 'Could not find any private key with alias \'' + alias +
- '\' in PKCS12 key store.');
- }
- return privateKeyPem;
- } catch (error) {
- throw new Error(
- 'Could not retrieve private key with alias \'' + alias +
- '\' from PKCS12 key store; ' + error);
- }
- };
- /**
- * Retrieves a certificate stored inside the PKCS #12 key store, given the
- * storage alias name of the certificate or that of its associated private
- * key entry.
- *
- * @function getCertificate
- * @memberof Pkcs12KeyStore
- * @param {string}
- * alias The storage alias name of the certificate or that of its
- * associated private key entry.
- * @returns {string} The certificate, in PEM format.
- * @throws {Error}
- * If the input data validation fails or the certificate could
- * not be retrieved.
- */
- this.getCertificate = function(alias) {
- validator.checkIsNonEmptyString(
- alias,
- 'Storage alias name of certificate or that of its associated private key entry, to retrieve from PKCS12 key store');
- try {
- var certificatePem;
- var certificateFound = false;
- for (var i = 0; i < forgePkcs12_.safeContents.length; i++) {
- var safeContents = forgePkcs12_.safeContents[i];
- for (var j = 0; j < safeContents.safeBags.length; j++) {
- var safeBag = safeContents.safeBags[j];
- if (safeBag.type === forge.pki.oids.certBag &&
- safeBag.attributes && safeBag.attributes.friendlyName &&
- safeBag.attributes.friendlyName[0] === alias) {
- certificatePem = forge.pki.certificateToPem(safeBag.cert);
- certificateFound = true;
- break;
- }
- }
- if (certificateFound) {
- break;
- }
- }
- if (!certificatePem) {
- throw new Error(
- 'Could not find any certificate with alias \'' + alias +
- '\' in PKCS12 key store.');
- }
- return certificatePem;
- } catch (error) {
- throw new Error(
- 'Could not retrieve certificate with alias \'' + alias +
- '\' from PKCS12 key store; ' + error);
- }
- };
- /**
- * Retrieves a certificate stored inside the PKCS #12 key store, given the
- * certificate's subject common name.
- *
- * @function getCertificateBySubject
- * @memberof Pkcs12KeyStore
- * @param {string}
- * subjectCn The subject common name of the certificate.
- * @returns {string} The certificate, in PEM format.
- * @throws {Error}
- * If the input data validation fails or the certificate could
- * not be retrieved.
- */
- this.getCertificateBySubject = function(subjectCN) {
- validator.checkIsNonEmptyString(
- subjectCN,
- 'Subject common name of certificate to retrieve from PKCS12 key store');
- try {
- var certificatePem;
- var certificateFound = false;
- for (var i = 0; i < forgePkcs12_.safeContents.length; i++) {
- var safeContents = forgePkcs12_.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) {
- certificatePem = forge.pki.certificateToPem(safeBag.cert);
- certificateFound = true;
- break;
- }
- }
- if (certificateFound) {
- break;
- }
- }
- if (!certificatePem) {
- throw new Error(
- 'Could not find any certificate with subject common name \'' +
- subjectCN + '\' in PKCS12 key store.');
- }
- return certificatePem;
- } catch (error) {
- throw Error(
- 'Could not retrieve certificate with subject common name \'' +
- subjectCN + '\' from PKCS12 key store; ' + error);
- }
- };
- /**
- * Retrieves a certificate chain stored inside the PKCS #12 key store, given
- * the storage alias name of the chain's associated private key entry.
- *
- * @function getCertificateChain
- * @memberof Pkcs12KeyStore
- * @param {string}
- * alias The storage alias name of the chain's associated private
- * key entry.
- * @returns {string[]} The certificate chain, as an array of strings in PEM
- * format.
- * @throws {Error}
- * If the input data validation fails or the certificate chain
- * could not be retrieved.
- */
- this.getCertificateChain = function(alias) {
- validator.checkIsNonEmptyString(
- alias,
- 'Storage alias name of private key associated with certificate chain to retrieve from PKCS12 key store');
- try {
- // Loop through certificate safe bags of PKCS12.
- var certificatePemChain = [];
- var savedBags = {};
- var nextBagId;
- for (var i = 0; i < forgePkcs12_.safeContents.length; i++) {
- var safeContents = forgePkcs12_.safeContents[i];
- for (var j = 0; j < safeContents.safeBags.length; j++) {
- var safeBag = safeContents.safeBags[j];
- if (safeBag.type === forge.pki.oids.certBag) {
- var nextBag;
- // If no bags have been found yet and this is the safe bag
- // associated with the alias (i.e. the leaf safe bag).
- // Or if this happens to be the next bag in the chain.
- if ((!nextBagId && safeBag.attributes &&
- safeBag.attributes.friendlyName &&
- safeBag.attributes.friendlyName[0] === alias) ||
- (nextBagId && nextBagId === safeBag.cert.subject.hash)) {
- nextBag = safeBag;
- }
- // If this is a bag higher up in the chain, and so will need to be
- // added later.
- else {
- savedBags[safeBag.cert.subject.hash] = safeBag;
- }
- // Add this bag and any saved bags to the chain, and in the proper
- // order.
- while (nextBag) {
- var certificatePem = forge.pki.certificateToPem(nextBag.cert);
- certificatePemChain.push(certificatePem);
- // If this bag is the root certificate, then the process is
- // finished.
- if (nextBag.cert.subject.hash === nextBag.cert.issuer.hash) {
- return certificatePemChain;
- }
- // Save the issuer of this bag as the ID of the next bag to add to
- // the chain.
- nextBagId = nextBag.cert.issuer.hash;
- // Add the saved bag corresponding to the ID of the next bag to
- // the chain.
- nextBag = savedBags[nextBagId];
- }
- }
- }
- }
- // Check whether any certificate safe bags were found.
- if (certificatePemChain.length === 0) {
- throw new Error(
- 'Could not find any certificate chains with storage alias name \'' +
- alias + '\' in PKCS12 key store');
- }
- return certificatePemChain;
- } catch (error) {
- throw new Error(
- 'Could not retrieve certificate chain with storage alias name \'' +
- alias + '\' from PKCS12 key store; ' + error);
- }
- };
- function checkPassword(password, pkcs12Asn1, label) {
- validator.checkIsType(password, 'string', label);
- try {
- forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, password);
- } catch (error) {
- throw new Error(label + ' is not valid');
- }
- }
- }
|