123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958 |
- /*
- * 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
- */
- /** @namespace certificates */
- cryptolib.modules.certificates = function(box) {
- 'use strict';
- if (box.certificates) {
- return;
- }
- box.certificates = {};
- /**
- * Provides an API that can be used to perform operations with certificates.
- *
- * @exports certificates/service
- */
- box.certificates.service = {};
- var converters;
- var exceptions;
- cryptolib('commons', function(box) {
- converters = new box.commons.utils.Converters();
- exceptions = box.commons.exceptions;
- });
- /**
- * Loads a certificate in pem format.
- *
- * @function
- * @param {string}
- * certificatePem certificate as string in PEM format.
- * @returns {certificates.box.certificates.CryptoX509Certificate} the
- * certificate.
- */
- box.certificates.service.load = function(certificatePem) {
- return new box.certificates.CryptoX509Certificate(certificatePem);
- };
- /**
- * Provides methods to access X509 certificate data.
- *
- * @class
- * @param certificatePem
- * certificate as string in PEM format.
- * @memberof certificates
- */
- box.certificates.CryptoX509Certificate = function(certificatePem) {
- /** @property {object} certificate the certificate. * */
- this.certificate = forge.pki.certificateFromPem(certificatePem, true);
- };
- box.certificates.CryptoX509Certificate.prototype = {
- /**
- * Retrieves the public key of the certificate.
- *
- * @function
- * @returns public key, as string in PEM format.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getPublicKey: function() {
- var publicKey = this.certificate.publicKey;
- if (publicKey !== null) {
- return forge.pki.publicKeyToPem(publicKey);
- } else {
- throw new exceptions.CryptoLibException(
- 'Could not find public key in certificate.');
- }
- },
- /**
- * Retrieves the start time of the certificate's validity.
- *
- * @function
- * @returns start time of certificate validity, as Date object.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getNotBefore: function() {
- var notBeforeField = this.certificate.validity.notBefore;
- if (notBeforeField !== null) {
- return notBeforeField;
- } else {
- throw new exceptions.CryptoLibException(
- 'Could not find validity start time in certificate.');
- }
- },
- /**
- * Retrieves the end time of the certificate's validity.
- *
- * @function
- * @returns end time of certificate validity, as Date object.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getNotAfter: function() {
- var notAfterField = this.certificate.validity.notAfter;
- if (notAfterField !== null) {
- return notAfterField;
- } else {
- throw new exceptions.CryptoLibException(
- 'Could not find validity end time in certificate.');
- }
- },
- /**
- * Retrieves the serial number of the certificate.
- *
- * @function
- * @returns serial number of certificate, as hexadecimal string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getSerialNumber: function() {
- var serialNumber = this.certificate.serialNumber;
- if (serialNumber !== null) {
- return serialNumber;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the issuer common name of the certificate.
- *
- * @function
- * @returns issuer common name of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getIssuerCN: function() {
- var issuerCNField =
- this.certificate.issuer.getField({name: 'commonName'});
- if (issuerCNField !== null) {
- return issuerCNField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the issuer organizational unit of the certificate.
- *
- * @function
- * @returns issuer organizational unit of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getIssuerOrgUnit: function() {
- var orgUnitField = this.certificate.issuer.getField({shortName: 'OU'});
- if (orgUnitField !== null) {
- return orgUnitField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the issuer organization name of the certificate.
- *
- * @function
- * @returns Issuer organization name of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getIssuerOrg: function() {
- var orgNameField =
- this.certificate.issuer.getField({name: 'organizationName'});
- if (orgNameField !== null) {
- return orgNameField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the issuer locality name of the certificate.
- *
- * @function
- * @returns Locality name of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getIssuerLocality: function() {
- var localityField =
- this.certificate.issuer.getField({name: 'localityName'});
- if (localityField !== null) {
- return localityField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the issuer country name of the certificate.
- *
- * @function
- * @returns Issuer country name of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getIssuerCountry: function() {
- var countryField =
- this.certificate.issuer.getField({name: 'countryName'});
- if (countryField !== null) {
- return countryField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the subject common name of the certificate.
- *
- * @function
- * @returns Subject common name of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getSubjectCN: function() {
- var subjectCNField =
- this.certificate.subject.getField({name: 'commonName'});
- if (subjectCNField !== null) {
- return subjectCNField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the subject organizational unit of the certificate.
- *
- * @function
- * @returns Subject organizational unit of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getSubjectOrgUnit: function() {
- var orgUnitField = this.certificate.subject.getField({shortName: 'OU'});
- if (orgUnitField !== null) {
- return orgUnitField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the subject organization name of the certificate.
- *
- * @function
- * @return Subject organization name of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getSubjectOrg: function() {
- var organizationNameField =
- this.certificate.subject.getField({name: 'organizationName'});
- if (organizationNameField !== null) {
- return organizationNameField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the subject locality name of the certificate.
- *
- * @function
- * @returns Subject locality name of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getSubjectLocality: function() {
- var localityField =
- this.certificate.subject.getField({name: 'localityName'});
- if (localityField !== null) {
- return localityField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the subject country name of the certificate.
- *
- * @function
- * @returns Subject country name of certificate, as string.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getSubjectCountry: function() {
- var countryField =
- this.certificate.subject.getField({name: 'countryName'});
- if (countryField !== null) {
- return countryField.value;
- } else {
- return '';
- }
- },
- /**
- * Retrieves the key usage extension of the certificate.
- *
- * @function
- * @return KeyUsageExtension key usage extension, as KeyUsageExtension
- * object.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getKeyUsageExtension: function() {
- var keyUsageExt = this.certificate.getExtension({name: 'keyUsage'});
- if (keyUsageExt !== null) {
- var keyUsageMap = {};
- keyUsageMap.digitalSignature = keyUsageExt.digitalSignature;
- keyUsageMap.nonRepudiation = keyUsageExt.nonRepudiation;
- keyUsageMap.keyEncipherment = keyUsageExt.keyEncipherment;
- keyUsageMap.dataEncipherment = keyUsageExt.dataEncipherment;
- keyUsageMap.keyAgreement = keyUsageExt.keyAgreement;
- keyUsageMap.keyCertSign = keyUsageExt.keyCertSign;
- keyUsageMap.crlSign = keyUsageExt.cRLSign;
- keyUsageMap.encipherOnly = keyUsageExt.encipherOnly;
- keyUsageMap.decipherOnly = keyUsageExt.decipherOnly;
- return new box.certificates.KeyUsageExtension(keyUsageMap);
- } else {
- return null;
- }
- },
- /**
- * Retrieves the basic constraints of the certificate.
- *
- * @function
- * @returns map containing the basic constraints.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getBasicConstraints: function() {
- var basicConstraints =
- this.certificate.getExtension({name: 'basicConstraints'});
- if (basicConstraints !== null) {
- var basicConstraintsMap = {};
- basicConstraintsMap.ca = basicConstraints.cA;
- return basicConstraintsMap;
- } else {
- return null;
- }
- },
- /**
- * Retrieves the digital signature of the certificate.
- *
- * @function
- * @returns digital signature of certificate, as string in Base64
- * encoded format.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- getSignature: function() {
- var signature = this.certificate.signature;
- if (signature !== null) {
- var signatureB64 =
- converters.base64Encode(signature, box.RSA_LINE_LENGTH);
- if (signatureB64 !== null) {
- return signatureB64;
- } else {
- throw new exceptions.CryptoLibException(
- 'Base64 encoding of signature is null.');
- }
- } else {
- throw new exceptions.CryptoLibException('Signature is null.');
- }
- },
- /**
- * Verifies the digital signature of the certificate provided as input,
- * using this certificate's public key.
- *
- * @function
- * @param certificatePem
- * certificate whose signature is to be verified, as string
- * in PEM format.
- * @returns boolean indicating whether signature was verified.
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- verify: function(certificatePem) {
- var certificate = forge.pki.certificateFromPem(certificatePem);
- if (certificate !== null) {
- return this.certificate.verify(certificate);
- } else {
- throw new exceptions.CryptoLibException('Certificate is null.');
- }
- },
- /**
- * Retrieves the certificate, in PEM format.
- *
- * @function
- * @returns certificate, as string in PEM format
- * @memberof certificates.box.certificates.CryptoX509Certificate
- */
- toPem: function() {
- if (this.certificate !== null) {
- return forge.pki.certificateToPem(
- this.certificate, box.RSA_LINE_LENGTH);
- } else {
- throw new exceptions.CryptoLibException('Certificate is null.');
- }
- }
- };
- /**
- * Container class for the key usage extension flags of a digital
- * certificate.
- *
- * @class
- * @param keyUsageMap
- * map containing name-value pairs of key usage extension flags.
- * @memberof certificates
- */
- box.certificates.KeyUsageExtension = function(keyUsageMap) {
- this.keyUsageMap = keyUsageMap;
- };
- box.certificates.KeyUsageExtension.prototype = {
- /**
- * Retrieves the digital signature flag of the key usage extension.
- *
- * @function
- * @returns boolean indicating whether digital signature flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- digitalSignature: function() {
- return this.keyUsageMap.digitalSignature;
- },
- /**
- * Retrieves the non-repudiation flag of the key usage extension.
- *
- * @function
- * @return boolean indicating whether non-repudiation flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- nonRepudiation: function() {
- return this.keyUsageMap.nonRepudiation;
- },
- /**
- * Retrieves the key encipherment flag of the key usage extension.
- *
- * @function
- * @returns boolean indicating whether key encipherment flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- keyEncipherment: function() {
- return this.keyUsageMap.keyEncipherment;
- },
- /**
- * Retrieves the data encipherment flag of the key usage extension.
- *
- * @function
- * @returns boolean indicating whether data encipherment flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- dataEncipherment: function() {
- return this.keyUsageMap.dataEncipherment;
- },
- /**
- * Retrieves the key agreement flag of the key usage extension.
- *
- * @function
- * @returns boolean indicating whether key agreement flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- keyAgreement: function() {
- return this.keyUsageMap.keyAgreement;
- },
- /**
- * Retrieves the key certificate sign flag of the key usage extension.
- *
- * @function
- * @returns Boolean indicating whether key certificate sign flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- keyCertSign: function() {
- return this.keyUsageMap.keyCertSign;
- },
- /**
- * Retrieves the CRL sign flag of the key usage extension.
- *
- * @function
- * @returns boolean indicating whether CRL sign flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- crlSign: function() {
- return this.keyUsageMap.crlSign;
- },
- /**
- * Retrieves the encipher only flag of the key usage extension.
- *
- * @function
- * @returns boolean indicating whether encipher only flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- encipherOnly: function() {
- return this.keyUsageMap.encipherOnly;
- },
- /**
- * Retrieves the decipher only flag of the key usage extension.
- *
- * @function
- * @returns boolean indicating whether decipher only flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- decipherOnly: function() {
- return this.keyUsageMap.decipherOnly;
- },
- /**
- * Retrieves the CA flag of the key usage extension.
- *
- * @function
- * @return boolean indicating whether CA flag is set.
- * @memberof certificates.box.certificates.KeyUsageExtension
- */
- ca: function() {
- return this.keyUsageMap.ca;
- }
- };
- /**
- * Class that validates the content of a CryptoX509Certificate.
- *
- * @class
- * @param validationData
- * @param {string}
- * validationData.subject
- * @param {string}
- * validationData.subject.commonName
- * @param {string}
- * validationData.subject.organization
- * @param {string}
- * validationData.subject.organizationUnit
- * @param {string}
- * validationData.subject.country
- * @param {string}
- * validationData.issuer
- * @param {string}
- * validationData.issuer.commonName
- * @param {string}
- * validationData.issuer.organization
- * @param {string}
- * validationData.issuer.organizationUnit
- * @param {string}
- * validationData.issuer.country
- * @param {string}
- * validationData.time
- * @param {string}
- * validationData.keytype CA, Sign or Encryption
- * @param {string}
- * validationData.caCertificatePem
- * @memberof certificates
- */
- box.certificates.CryptoX509CertificateValidator = function(validationData) {
- var parseIsoDate = function(isoDate) {
- var dateChunks = isoDate.split(/\D/);
- return new Date(Date.UTC(
- +dateChunks[0], --dateChunks[1], +dateChunks[2], +dateChunks[5],
- +dateChunks[6], +dateChunks[7], 0));
- };
- /**
- * Validates the content of the X.509 certificate.
- *
- * @function
- * @param certificatePem
- * certificate as string in PEM format.
- * @return an array containing the errors type, if any.
- */
- this.validate = function(x509CertificatePem) {
- var cryptoX509Certificate =
- new box.certificates.CryptoX509Certificate(x509CertificatePem);
- var failedValidations = [];
- var validateSubject = function(subject) {
- if (subject.commonName !== cryptoX509Certificate.getSubjectCN() ||
- subject.organization !== cryptoX509Certificate.getSubjectOrg() ||
- subject.organizationUnit !==
- cryptoX509Certificate.getSubjectOrgUnit() ||
- subject.country !== cryptoX509Certificate.getSubjectCountry()) {
- failedValidations.push('SUBJECT');
- }
- };
- var validateIssuer = function(issuer) {
- if (issuer.commonName !== cryptoX509Certificate.getIssuerCN() ||
- issuer.organization !== cryptoX509Certificate.getIssuerOrg() ||
- issuer.organizationUnit !==
- cryptoX509Certificate.getIssuerOrgUnit() ||
- issuer.country !== cryptoX509Certificate.getIssuerCountry()) {
- failedValidations.push('ISSUER');
- }
- };
- var validateTime = function(isoDate) {
- var time = parseIsoDate(isoDate);
- if (time.toString() === 'Invalid Date' ||
- time - cryptoX509Certificate.getNotBefore() < 0 ||
- time - cryptoX509Certificate.getNotAfter() > 0) {
- failedValidations.push('TIME');
- }
- };
- var areBasicConstraintsValid = function(keyType) {
- var returnValue = true;
- var basicConstraints = cryptoX509Certificate.getBasicConstraints();
- if (keyType === 'CA' &&
- (!basicConstraints || basicConstraints.ca !== true)) {
- returnValue = false;
- }
- return returnValue;
- };
- var isKeyUsageValid = function(keyType) {
- var returnValue = true;
- var keyUsageExtension = cryptoX509Certificate.getKeyUsageExtension();
- if (!keyUsageExtension) {
- return false;
- }
- switch (keyType) {
- case 'CA':
- if (keyUsageExtension.keyCertSign() !== true ||
- keyUsageExtension.crlSign() !== true) {
- returnValue = false;
- }
- break;
- case 'Sign':
- if (keyUsageExtension.digitalSignature() !== true ||
- keyUsageExtension.nonRepudiation() !== true) {
- returnValue = false;
- }
- break;
- case 'Encryption':
- if (keyUsageExtension.keyEncipherment() !== true ||
- keyUsageExtension.dataEncipherment() !== true) {
- returnValue = false;
- }
- break;
- default:
- returnValue = false;
- }
- return returnValue;
- };
- var validateKeyType = function(keyType) {
- if (!areBasicConstraintsValid(keyType) || !isKeyUsageValid(keyType)) {
- failedValidations.push('KEY_TYPE');
- }
- };
- var validateSignature = function(caCertificatePem) {
- var caCryptoX509Certificate =
- new box.certificates.CryptoX509Certificate(caCertificatePem);
- if (caCryptoX509Certificate === null) {
- failedValidations.push('SIGNATURE');
- throw new exceptions.CryptoLibException('CA certificate is null.');
- }
- try {
- if (!caCryptoX509Certificate.verify(x509CertificatePem)) {
- failedValidations.push('SIGNATURE');
- }
- } catch (error) {
- failedValidations.push('SIGNATURE');
- throw new exceptions.CryptoLibException(
- 'Signature verification process failed.');
- }
- };
- if (validationData.subject) {
- validateSubject(validationData.subject);
- }
- if (validationData.issuer) {
- validateIssuer(validationData.issuer);
- }
- if (validationData.time) {
- validateTime(validationData.time);
- }
- if (validationData.keyType) {
- validateKeyType(validationData.keyType);
- }
- if (validationData.caCertificatePem) {
- validateSignature(validationData.caCertificatePem);
- }
- return failedValidations;
- };
- };
- /**
- * Class that validates the content of a CryptoX509Certificate.
- *
- * @function
- * @param validationData
- * {json}
- * @param {string}
- * validationData.subject the subject data.
- * @param {string}
- * validationData.subject.commonName
- * @param {string}
- * validationData.subject.organization
- * @param {string}
- * validationData.subject.organizationUnit
- * @param {string}
- * validationData.subject.country
- * @param {string}
- * validationData.issuer the issuer data.
- * @param {string}
- * validationData.issuer.commonName
- * @param {string}
- * validationData.issuer.organization
- * @param {string}
- * validationData.issuer.organizationUnit
- * @param {string}
- * validationData.issuer.country
- * @param {string}
- * validationData.time time to be checked.
- * @param {string}
- * validationData.keyType CA, Sign or Encryption.
- * @param {string}
- * validationData.caCertificatePem
- * @param certificate
- * certificate as string in PEM format.
- * @returns {Array} an array that contains the error types occurred, if any.
- */
- box.certificates.service.validateCertificate = function(
- validationData, certificate) {
- if (validationData === null) {
- throw new exceptions.CryptoLibException('Validation data is null.');
- }
- if (Object.getOwnPropertyNames(validationData).length) {
- throw new exceptions.CryptoLibException('Validation data is empty.');
- }
- if (certificate === null) {
- throw new exceptions.CryptoLibException('Certificate is null.');
- }
- return (new box.certificates.CryptoX509CertificateValidator(validationData))
- .validate(certificate);
- };
- /**
- * Validates the certificate information provided to the constructor. The
- * validation process loops through all certificates, starting with the leaf
- * certificate, until it reaches the trusted certificate. For each
- * certificate, except the trusted certificate, it checks that the following
- * conditions hold:
- * <ul>
- * <li>Subject DN is that expected for given certificate.</li>
- * <li>Issuer DN is same as subject DN of next certificate in chain.</li>
- * <li>Key type is that expected: "signing" or "encryption" for leaf
- * certificate and "CA" for rest of certificates in chain.</li>
- * <li>Signature can be verified with public key of next certificate in
- * chain.</li>
- * <li>Starting time is earlier than ending time.</li>
- * <li>Starting time is equal to or later than starting time of next
- * certificate in chain.</li>
- * <li>Ending time is equal to or earlier than ending time of next
- * certificate in chain.</li>
- * </ul>
- * In addition, if a non-null value is provided to the constructor for the
- * time reference, it will be checked whether this time reference is within
- * the dates of validity of the leaf certificate. After the validation
- * process has completed, a list of strings will be returned. If this list
- * is empty, then the validation was successful. Otherwise, the list will
- * contain string identifiers for each type of validation that failed.
- *
- * @function
- * @param {json}
- * chain certificate chain.
- * @param {json}
- * chain.leaf leaf certificate.
- * @param {string}
- * chain.leaf.pem X.509 certificate.
- * @param {string}
- * chain.leaf.keyType keyType.
- * @param {string}
- * chain.leaf.subject subject.
- * @param {string}
- * chain.leaf.time time reference (Optional). Only mandatory if
- * the 'Time' validation is going to be performed.
- * @param {json}
- * chain.certificates chain of certificates.
- * @param {Array}
- * chain.certificates.pems list of X.509 Certificates as string
- * in pem format. The list starts by the one closer to the root.
- * @param {Array}
- * chain.certificates.subjects list of subjects
- * @param {Array}
- * chain.root X.509 trusted Certificate.
- * @returns {Array} a two dimension array where the first dimension is the
- * index of the element starting by 1 -leaf certificate - and the
- * second dimension holds the error types.
- */
- box.certificates.service.validateX509CertificateChain = function(chain) {
- var validateDateRange = function(certificate, failedValidation) {
- if (certificate.getNotBefore() > certificate.getNotAfter()) {
- failedValidation.push('VALIDITY_PERIOD');
- }
- };
- var validateNotBefore = function(
- notBefore, previousNotBefore, failedValidation) {
- if (notBefore < previousNotBefore) {
- failedValidation.push('NOT_BEFORE');
- }
- };
- var validateNotAfter = function(
- notAfter, previousNotAfter, failedValidation) {
- if (notAfter > previousNotAfter) {
- failedValidation.push('NOT_AFTER');
- }
- };
- var validateLeafCertificate = function(
- issuer, signature, previousNotBefore, previousNotAfter) {
- var validationData = {
- subject: chain.leaf.subject,
- issuer: issuer,
- keyType: chain.leaf.keyType,
- caCertificatePem: signature
- };
- if (chain.leaf.time) {
- validationData.time = chain.leaf.time;
- }
- var certificateValidator =
- new box.certificates.CryptoX509CertificateValidator(validationData);
- var failedValidations = certificateValidator.validate(chain.leaf.pem);
- var certificate =
- new box.certificates.CryptoX509Certificate(chain.leaf.pem);
- validateDateRange(certificate, failedValidations);
- validateNotBefore(
- certificate.getNotBefore(), previousNotBefore, failedValidations);
- validateNotAfter(
- certificate.getNotAfter(), previousNotAfter, failedValidations);
- return failedValidations;
- };
- var rootCertificate =
- new box.certificates.CryptoX509Certificate(chain.root);
- var issuer = {
- commonName: rootCertificate.getSubjectCN(),
- organization: rootCertificate.getSubjectOrg(),
- organizationUnit: rootCertificate.getSubjectOrgUnit(),
- country: rootCertificate.getSubjectCountry()
- };
- var signature = chain.root;
- var failedValidation = [];
- var previousNotBefore = rootCertificate.getNotBefore();
- var previousNotAfter = rootCertificate.getNotAfter();
- var certificateValidator;
- chain.certificates.pems.reverse();
- chain.certificates.subjects.reverse();
- for (var i = 0; i < chain.certificates.pems.length; i++) {
- var certificate = new box.certificates.CryptoX509Certificate(
- chain.certificates.pems[i]);
- var validationData = {
- subject: chain.certificates.subjects[i],
- issuer: issuer,
- keyType: 'CA',
- signature: signature
- };
- certificateValidator =
- new box.certificates.CryptoX509CertificateValidator(validationData);
- failedValidation[i] =
- certificateValidator.validate(chain.certificates.pems[i]);
- validateDateRange(certificate, failedValidation[i]);
- validateNotBefore(
- certificate.getNotBefore(), previousNotBefore, failedValidation[i]);
- validateNotAfter(
- certificate.getNotAfter(), previousNotAfter, failedValidation[i]);
- issuer = {
- commonName: certificate.getSubjectCN(),
- organization: certificate.getSubjectOrg(),
- organizationUnit: certificate.getSubjectOrgUnit(),
- country: certificate.getSubjectCountry()
- };
- signature = chain.certificates.pems[i];
- previousNotBefore = certificate.getNotBefore();
- previousNotAfter = certificate.getNotAfter();
- }
- failedValidation.push(validateLeafCertificate(
- issuer, signature, previousNotBefore, previousNotAfter));
- failedValidation.reverse();
- return failedValidation;
- };
- /**
- * Flattens failed validations to a one dimensional array to a one
- * dimensional array.
- *
- * @function
- * @param {array}
- * failedValidations a two-dimensional failed validations array.
- * @returns {array} a flat array in which each element contains the error
- * type message piped with the index of the element, i.e.
- * ERROR_<element index>.
- */
- box.certificates.service.flattenFailedValidations = function(
- failedValidations) {
- var flattenFailedValidations = Array();
- for (var i = 0; i < failedValidations.length; i++) {
- if (failedValidations[i] !== undefined) {
- for (var j = 0; j < failedValidations[i].length; j++) {
- flattenFailedValidations.push(
- failedValidations[i][j].toLowerCase() + '_' + (i));
- }
- }
- }
- return flattenFailedValidations;
- };
- };
- cryptolib.modules.certificates.service = cryptolib.modules.certificates;
|