certificates.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. /*
  2. * Copyright 2018 Scytl Secure Electronic Voting SA
  3. *
  4. * All rights reserved
  5. *
  6. * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
  7. */
  8. /** @namespace certificates */
  9. cryptolib.modules.certificates = function(box) {
  10. 'use strict';
  11. if (box.certificates) {
  12. return;
  13. }
  14. box.certificates = {};
  15. /**
  16. * Provides an API that can be used to perform operations with certificates.
  17. *
  18. * @exports certificates/service
  19. */
  20. box.certificates.service = {};
  21. var converters;
  22. var exceptions;
  23. cryptolib('commons', function(box) {
  24. converters = new box.commons.utils.Converters();
  25. exceptions = box.commons.exceptions;
  26. });
  27. /**
  28. * Loads a certificate in pem format.
  29. *
  30. * @function
  31. * @param {string}
  32. * certificatePem certificate as string in PEM format.
  33. * @returns {certificates.box.certificates.CryptoX509Certificate} the
  34. * certificate.
  35. */
  36. box.certificates.service.load = function(certificatePem) {
  37. return new box.certificates.CryptoX509Certificate(certificatePem);
  38. };
  39. /**
  40. * Provides methods to access X509 certificate data.
  41. *
  42. * @class
  43. * @param certificatePem
  44. * certificate as string in PEM format.
  45. * @memberof certificates
  46. */
  47. box.certificates.CryptoX509Certificate = function(certificatePem) {
  48. /** @property {object} certificate the certificate. * */
  49. this.certificate = forge.pki.certificateFromPem(certificatePem, true);
  50. };
  51. box.certificates.CryptoX509Certificate.prototype = {
  52. /**
  53. * Retrieves the public key of the certificate.
  54. *
  55. * @function
  56. * @returns public key, as string in PEM format.
  57. * @memberof certificates.box.certificates.CryptoX509Certificate
  58. */
  59. getPublicKey: function() {
  60. var publicKey = this.certificate.publicKey;
  61. if (publicKey !== null) {
  62. return forge.pki.publicKeyToPem(publicKey);
  63. } else {
  64. throw new exceptions.CryptoLibException(
  65. 'Could not find public key in certificate.');
  66. }
  67. },
  68. /**
  69. * Retrieves the start time of the certificate's validity.
  70. *
  71. * @function
  72. * @returns start time of certificate validity, as Date object.
  73. * @memberof certificates.box.certificates.CryptoX509Certificate
  74. */
  75. getNotBefore: function() {
  76. var notBeforeField = this.certificate.validity.notBefore;
  77. if (notBeforeField !== null) {
  78. return notBeforeField;
  79. } else {
  80. throw new exceptions.CryptoLibException(
  81. 'Could not find validity start time in certificate.');
  82. }
  83. },
  84. /**
  85. * Retrieves the end time of the certificate's validity.
  86. *
  87. * @function
  88. * @returns end time of certificate validity, as Date object.
  89. * @memberof certificates.box.certificates.CryptoX509Certificate
  90. */
  91. getNotAfter: function() {
  92. var notAfterField = this.certificate.validity.notAfter;
  93. if (notAfterField !== null) {
  94. return notAfterField;
  95. } else {
  96. throw new exceptions.CryptoLibException(
  97. 'Could not find validity end time in certificate.');
  98. }
  99. },
  100. /**
  101. * Retrieves the serial number of the certificate.
  102. *
  103. * @function
  104. * @returns serial number of certificate, as hexadecimal string.
  105. * @memberof certificates.box.certificates.CryptoX509Certificate
  106. */
  107. getSerialNumber: function() {
  108. var serialNumber = this.certificate.serialNumber;
  109. if (serialNumber !== null) {
  110. return serialNumber;
  111. } else {
  112. return '';
  113. }
  114. },
  115. /**
  116. * Retrieves the issuer common name of the certificate.
  117. *
  118. * @function
  119. * @returns issuer common name of certificate, as string.
  120. * @memberof certificates.box.certificates.CryptoX509Certificate
  121. */
  122. getIssuerCN: function() {
  123. var issuerCNField =
  124. this.certificate.issuer.getField({name: 'commonName'});
  125. if (issuerCNField !== null) {
  126. return issuerCNField.value;
  127. } else {
  128. return '';
  129. }
  130. },
  131. /**
  132. * Retrieves the issuer organizational unit of the certificate.
  133. *
  134. * @function
  135. * @returns issuer organizational unit of certificate, as string.
  136. * @memberof certificates.box.certificates.CryptoX509Certificate
  137. */
  138. getIssuerOrgUnit: function() {
  139. var orgUnitField = this.certificate.issuer.getField({shortName: 'OU'});
  140. if (orgUnitField !== null) {
  141. return orgUnitField.value;
  142. } else {
  143. return '';
  144. }
  145. },
  146. /**
  147. * Retrieves the issuer organization name of the certificate.
  148. *
  149. * @function
  150. * @returns Issuer organization name of certificate, as string.
  151. * @memberof certificates.box.certificates.CryptoX509Certificate
  152. */
  153. getIssuerOrg: function() {
  154. var orgNameField =
  155. this.certificate.issuer.getField({name: 'organizationName'});
  156. if (orgNameField !== null) {
  157. return orgNameField.value;
  158. } else {
  159. return '';
  160. }
  161. },
  162. /**
  163. * Retrieves the issuer locality name of the certificate.
  164. *
  165. * @function
  166. * @returns Locality name of certificate, as string.
  167. * @memberof certificates.box.certificates.CryptoX509Certificate
  168. */
  169. getIssuerLocality: function() {
  170. var localityField =
  171. this.certificate.issuer.getField({name: 'localityName'});
  172. if (localityField !== null) {
  173. return localityField.value;
  174. } else {
  175. return '';
  176. }
  177. },
  178. /**
  179. * Retrieves the issuer country name of the certificate.
  180. *
  181. * @function
  182. * @returns Issuer country name of certificate, as string.
  183. * @memberof certificates.box.certificates.CryptoX509Certificate
  184. */
  185. getIssuerCountry: function() {
  186. var countryField =
  187. this.certificate.issuer.getField({name: 'countryName'});
  188. if (countryField !== null) {
  189. return countryField.value;
  190. } else {
  191. return '';
  192. }
  193. },
  194. /**
  195. * Retrieves the subject common name of the certificate.
  196. *
  197. * @function
  198. * @returns Subject common name of certificate, as string.
  199. * @memberof certificates.box.certificates.CryptoX509Certificate
  200. */
  201. getSubjectCN: function() {
  202. var subjectCNField =
  203. this.certificate.subject.getField({name: 'commonName'});
  204. if (subjectCNField !== null) {
  205. return subjectCNField.value;
  206. } else {
  207. return '';
  208. }
  209. },
  210. /**
  211. * Retrieves the subject organizational unit of the certificate.
  212. *
  213. * @function
  214. * @returns Subject organizational unit of certificate, as string.
  215. * @memberof certificates.box.certificates.CryptoX509Certificate
  216. */
  217. getSubjectOrgUnit: function() {
  218. var orgUnitField = this.certificate.subject.getField({shortName: 'OU'});
  219. if (orgUnitField !== null) {
  220. return orgUnitField.value;
  221. } else {
  222. return '';
  223. }
  224. },
  225. /**
  226. * Retrieves the subject organization name of the certificate.
  227. *
  228. * @function
  229. * @return Subject organization name of certificate, as string.
  230. * @memberof certificates.box.certificates.CryptoX509Certificate
  231. */
  232. getSubjectOrg: function() {
  233. var organizationNameField =
  234. this.certificate.subject.getField({name: 'organizationName'});
  235. if (organizationNameField !== null) {
  236. return organizationNameField.value;
  237. } else {
  238. return '';
  239. }
  240. },
  241. /**
  242. * Retrieves the subject locality name of the certificate.
  243. *
  244. * @function
  245. * @returns Subject locality name of certificate, as string.
  246. * @memberof certificates.box.certificates.CryptoX509Certificate
  247. */
  248. getSubjectLocality: function() {
  249. var localityField =
  250. this.certificate.subject.getField({name: 'localityName'});
  251. if (localityField !== null) {
  252. return localityField.value;
  253. } else {
  254. return '';
  255. }
  256. },
  257. /**
  258. * Retrieves the subject country name of the certificate.
  259. *
  260. * @function
  261. * @returns Subject country name of certificate, as string.
  262. * @memberof certificates.box.certificates.CryptoX509Certificate
  263. */
  264. getSubjectCountry: function() {
  265. var countryField =
  266. this.certificate.subject.getField({name: 'countryName'});
  267. if (countryField !== null) {
  268. return countryField.value;
  269. } else {
  270. return '';
  271. }
  272. },
  273. /**
  274. * Retrieves the key usage extension of the certificate.
  275. *
  276. * @function
  277. * @return KeyUsageExtension key usage extension, as KeyUsageExtension
  278. * object.
  279. * @memberof certificates.box.certificates.CryptoX509Certificate
  280. */
  281. getKeyUsageExtension: function() {
  282. var keyUsageExt = this.certificate.getExtension({name: 'keyUsage'});
  283. if (keyUsageExt !== null) {
  284. var keyUsageMap = {};
  285. keyUsageMap.digitalSignature = keyUsageExt.digitalSignature;
  286. keyUsageMap.nonRepudiation = keyUsageExt.nonRepudiation;
  287. keyUsageMap.keyEncipherment = keyUsageExt.keyEncipherment;
  288. keyUsageMap.dataEncipherment = keyUsageExt.dataEncipherment;
  289. keyUsageMap.keyAgreement = keyUsageExt.keyAgreement;
  290. keyUsageMap.keyCertSign = keyUsageExt.keyCertSign;
  291. keyUsageMap.crlSign = keyUsageExt.cRLSign;
  292. keyUsageMap.encipherOnly = keyUsageExt.encipherOnly;
  293. keyUsageMap.decipherOnly = keyUsageExt.decipherOnly;
  294. return new box.certificates.KeyUsageExtension(keyUsageMap);
  295. } else {
  296. return null;
  297. }
  298. },
  299. /**
  300. * Retrieves the basic constraints of the certificate.
  301. *
  302. * @function
  303. * @returns map containing the basic constraints.
  304. * @memberof certificates.box.certificates.CryptoX509Certificate
  305. */
  306. getBasicConstraints: function() {
  307. var basicConstraints =
  308. this.certificate.getExtension({name: 'basicConstraints'});
  309. if (basicConstraints !== null) {
  310. var basicConstraintsMap = {};
  311. basicConstraintsMap.ca = basicConstraints.cA;
  312. return basicConstraintsMap;
  313. } else {
  314. return null;
  315. }
  316. },
  317. /**
  318. * Retrieves the digital signature of the certificate.
  319. *
  320. * @function
  321. * @returns digital signature of certificate, as string in Base64
  322. * encoded format.
  323. * @memberof certificates.box.certificates.CryptoX509Certificate
  324. */
  325. getSignature: function() {
  326. var signature = this.certificate.signature;
  327. if (signature !== null) {
  328. var signatureB64 =
  329. converters.base64Encode(signature, box.RSA_LINE_LENGTH);
  330. if (signatureB64 !== null) {
  331. return signatureB64;
  332. } else {
  333. throw new exceptions.CryptoLibException(
  334. 'Base64 encoding of signature is null.');
  335. }
  336. } else {
  337. throw new exceptions.CryptoLibException('Signature is null.');
  338. }
  339. },
  340. /**
  341. * Verifies the digital signature of the certificate provided as input,
  342. * using this certificate's public key.
  343. *
  344. * @function
  345. * @param certificatePem
  346. * certificate whose signature is to be verified, as string
  347. * in PEM format.
  348. * @returns boolean indicating whether signature was verified.
  349. * @memberof certificates.box.certificates.CryptoX509Certificate
  350. */
  351. verify: function(certificatePem) {
  352. var certificate = forge.pki.certificateFromPem(certificatePem);
  353. if (certificate !== null) {
  354. return this.certificate.verify(certificate);
  355. } else {
  356. throw new exceptions.CryptoLibException('Certificate is null.');
  357. }
  358. },
  359. /**
  360. * Retrieves the certificate, in PEM format.
  361. *
  362. * @function
  363. * @returns certificate, as string in PEM format
  364. * @memberof certificates.box.certificates.CryptoX509Certificate
  365. */
  366. toPem: function() {
  367. if (this.certificate !== null) {
  368. return forge.pki.certificateToPem(
  369. this.certificate, box.RSA_LINE_LENGTH);
  370. } else {
  371. throw new exceptions.CryptoLibException('Certificate is null.');
  372. }
  373. }
  374. };
  375. /**
  376. * Container class for the key usage extension flags of a digital
  377. * certificate.
  378. *
  379. * @class
  380. * @param keyUsageMap
  381. * map containing name-value pairs of key usage extension flags.
  382. * @memberof certificates
  383. */
  384. box.certificates.KeyUsageExtension = function(keyUsageMap) {
  385. this.keyUsageMap = keyUsageMap;
  386. };
  387. box.certificates.KeyUsageExtension.prototype = {
  388. /**
  389. * Retrieves the digital signature flag of the key usage extension.
  390. *
  391. * @function
  392. * @returns boolean indicating whether digital signature flag is set.
  393. * @memberof certificates.box.certificates.KeyUsageExtension
  394. */
  395. digitalSignature: function() {
  396. return this.keyUsageMap.digitalSignature;
  397. },
  398. /**
  399. * Retrieves the non-repudiation flag of the key usage extension.
  400. *
  401. * @function
  402. * @return boolean indicating whether non-repudiation flag is set.
  403. * @memberof certificates.box.certificates.KeyUsageExtension
  404. */
  405. nonRepudiation: function() {
  406. return this.keyUsageMap.nonRepudiation;
  407. },
  408. /**
  409. * Retrieves the key encipherment flag of the key usage extension.
  410. *
  411. * @function
  412. * @returns boolean indicating whether key encipherment flag is set.
  413. * @memberof certificates.box.certificates.KeyUsageExtension
  414. */
  415. keyEncipherment: function() {
  416. return this.keyUsageMap.keyEncipherment;
  417. },
  418. /**
  419. * Retrieves the data encipherment flag of the key usage extension.
  420. *
  421. * @function
  422. * @returns boolean indicating whether data encipherment flag is set.
  423. * @memberof certificates.box.certificates.KeyUsageExtension
  424. */
  425. dataEncipherment: function() {
  426. return this.keyUsageMap.dataEncipherment;
  427. },
  428. /**
  429. * Retrieves the key agreement flag of the key usage extension.
  430. *
  431. * @function
  432. * @returns boolean indicating whether key agreement flag is set.
  433. * @memberof certificates.box.certificates.KeyUsageExtension
  434. */
  435. keyAgreement: function() {
  436. return this.keyUsageMap.keyAgreement;
  437. },
  438. /**
  439. * Retrieves the key certificate sign flag of the key usage extension.
  440. *
  441. * @function
  442. * @returns Boolean indicating whether key certificate sign flag is set.
  443. * @memberof certificates.box.certificates.KeyUsageExtension
  444. */
  445. keyCertSign: function() {
  446. return this.keyUsageMap.keyCertSign;
  447. },
  448. /**
  449. * Retrieves the CRL sign flag of the key usage extension.
  450. *
  451. * @function
  452. * @returns boolean indicating whether CRL sign flag is set.
  453. * @memberof certificates.box.certificates.KeyUsageExtension
  454. */
  455. crlSign: function() {
  456. return this.keyUsageMap.crlSign;
  457. },
  458. /**
  459. * Retrieves the encipher only flag of the key usage extension.
  460. *
  461. * @function
  462. * @returns boolean indicating whether encipher only flag is set.
  463. * @memberof certificates.box.certificates.KeyUsageExtension
  464. */
  465. encipherOnly: function() {
  466. return this.keyUsageMap.encipherOnly;
  467. },
  468. /**
  469. * Retrieves the decipher only flag of the key usage extension.
  470. *
  471. * @function
  472. * @returns boolean indicating whether decipher only flag is set.
  473. * @memberof certificates.box.certificates.KeyUsageExtension
  474. */
  475. decipherOnly: function() {
  476. return this.keyUsageMap.decipherOnly;
  477. },
  478. /**
  479. * Retrieves the CA flag of the key usage extension.
  480. *
  481. * @function
  482. * @return boolean indicating whether CA flag is set.
  483. * @memberof certificates.box.certificates.KeyUsageExtension
  484. */
  485. ca: function() {
  486. return this.keyUsageMap.ca;
  487. }
  488. };
  489. /**
  490. * Class that validates the content of a CryptoX509Certificate.
  491. *
  492. * @class
  493. * @param validationData
  494. * @param {string}
  495. * validationData.subject
  496. * @param {string}
  497. * validationData.subject.commonName
  498. * @param {string}
  499. * validationData.subject.organization
  500. * @param {string}
  501. * validationData.subject.organizationUnit
  502. * @param {string}
  503. * validationData.subject.country
  504. * @param {string}
  505. * validationData.issuer
  506. * @param {string}
  507. * validationData.issuer.commonName
  508. * @param {string}
  509. * validationData.issuer.organization
  510. * @param {string}
  511. * validationData.issuer.organizationUnit
  512. * @param {string}
  513. * validationData.issuer.country
  514. * @param {string}
  515. * validationData.time
  516. * @param {string}
  517. * validationData.keytype CA, Sign or Encryption
  518. * @param {string}
  519. * validationData.caCertificatePem
  520. * @memberof certificates
  521. */
  522. box.certificates.CryptoX509CertificateValidator = function(validationData) {
  523. var parseIsoDate = function(isoDate) {
  524. var dateChunks = isoDate.split(/\D/);
  525. return new Date(Date.UTC(
  526. +dateChunks[0], --dateChunks[1], +dateChunks[2], +dateChunks[5],
  527. +dateChunks[6], +dateChunks[7], 0));
  528. };
  529. /**
  530. * Validates the content of the X.509 certificate.
  531. *
  532. * @function
  533. * @param certificatePem
  534. * certificate as string in PEM format.
  535. * @return an array containing the errors type, if any.
  536. */
  537. this.validate = function(x509CertificatePem) {
  538. var cryptoX509Certificate =
  539. new box.certificates.CryptoX509Certificate(x509CertificatePem);
  540. var failedValidations = [];
  541. var validateSubject = function(subject) {
  542. if (subject.commonName !== cryptoX509Certificate.getSubjectCN() ||
  543. subject.organization !== cryptoX509Certificate.getSubjectOrg() ||
  544. subject.organizationUnit !==
  545. cryptoX509Certificate.getSubjectOrgUnit() ||
  546. subject.country !== cryptoX509Certificate.getSubjectCountry()) {
  547. failedValidations.push('SUBJECT');
  548. }
  549. };
  550. var validateIssuer = function(issuer) {
  551. if (issuer.commonName !== cryptoX509Certificate.getIssuerCN() ||
  552. issuer.organization !== cryptoX509Certificate.getIssuerOrg() ||
  553. issuer.organizationUnit !==
  554. cryptoX509Certificate.getIssuerOrgUnit() ||
  555. issuer.country !== cryptoX509Certificate.getIssuerCountry()) {
  556. failedValidations.push('ISSUER');
  557. }
  558. };
  559. var validateTime = function(isoDate) {
  560. var time = parseIsoDate(isoDate);
  561. if (time.toString() === 'Invalid Date' ||
  562. time - cryptoX509Certificate.getNotBefore() < 0 ||
  563. time - cryptoX509Certificate.getNotAfter() > 0) {
  564. failedValidations.push('TIME');
  565. }
  566. };
  567. var areBasicConstraintsValid = function(keyType) {
  568. var returnValue = true;
  569. var basicConstraints = cryptoX509Certificate.getBasicConstraints();
  570. if (keyType === 'CA' &&
  571. (!basicConstraints || basicConstraints.ca !== true)) {
  572. returnValue = false;
  573. }
  574. return returnValue;
  575. };
  576. var isKeyUsageValid = function(keyType) {
  577. var returnValue = true;
  578. var keyUsageExtension = cryptoX509Certificate.getKeyUsageExtension();
  579. if (!keyUsageExtension) {
  580. return false;
  581. }
  582. switch (keyType) {
  583. case 'CA':
  584. if (keyUsageExtension.keyCertSign() !== true ||
  585. keyUsageExtension.crlSign() !== true) {
  586. returnValue = false;
  587. }
  588. break;
  589. case 'Sign':
  590. if (keyUsageExtension.digitalSignature() !== true ||
  591. keyUsageExtension.nonRepudiation() !== true) {
  592. returnValue = false;
  593. }
  594. break;
  595. case 'Encryption':
  596. if (keyUsageExtension.keyEncipherment() !== true ||
  597. keyUsageExtension.dataEncipherment() !== true) {
  598. returnValue = false;
  599. }
  600. break;
  601. default:
  602. returnValue = false;
  603. }
  604. return returnValue;
  605. };
  606. var validateKeyType = function(keyType) {
  607. if (!areBasicConstraintsValid(keyType) || !isKeyUsageValid(keyType)) {
  608. failedValidations.push('KEY_TYPE');
  609. }
  610. };
  611. var validateSignature = function(caCertificatePem) {
  612. var caCryptoX509Certificate =
  613. new box.certificates.CryptoX509Certificate(caCertificatePem);
  614. if (caCryptoX509Certificate === null) {
  615. failedValidations.push('SIGNATURE');
  616. throw new exceptions.CryptoLibException('CA certificate is null.');
  617. }
  618. try {
  619. if (!caCryptoX509Certificate.verify(x509CertificatePem)) {
  620. failedValidations.push('SIGNATURE');
  621. }
  622. } catch (error) {
  623. failedValidations.push('SIGNATURE');
  624. throw new exceptions.CryptoLibException(
  625. 'Signature verification process failed.');
  626. }
  627. };
  628. if (validationData.subject) {
  629. validateSubject(validationData.subject);
  630. }
  631. if (validationData.issuer) {
  632. validateIssuer(validationData.issuer);
  633. }
  634. if (validationData.time) {
  635. validateTime(validationData.time);
  636. }
  637. if (validationData.keyType) {
  638. validateKeyType(validationData.keyType);
  639. }
  640. if (validationData.caCertificatePem) {
  641. validateSignature(validationData.caCertificatePem);
  642. }
  643. return failedValidations;
  644. };
  645. };
  646. /**
  647. * Class that validates the content of a CryptoX509Certificate.
  648. *
  649. * @function
  650. * @param validationData
  651. * {json}
  652. * @param {string}
  653. * validationData.subject the subject data.
  654. * @param {string}
  655. * validationData.subject.commonName
  656. * @param {string}
  657. * validationData.subject.organization
  658. * @param {string}
  659. * validationData.subject.organizationUnit
  660. * @param {string}
  661. * validationData.subject.country
  662. * @param {string}
  663. * validationData.issuer the issuer data.
  664. * @param {string}
  665. * validationData.issuer.commonName
  666. * @param {string}
  667. * validationData.issuer.organization
  668. * @param {string}
  669. * validationData.issuer.organizationUnit
  670. * @param {string}
  671. * validationData.issuer.country
  672. * @param {string}
  673. * validationData.time time to be checked.
  674. * @param {string}
  675. * validationData.keyType CA, Sign or Encryption.
  676. * @param {string}
  677. * validationData.caCertificatePem
  678. * @param certificate
  679. * certificate as string in PEM format.
  680. * @returns {Array} an array that contains the error types occurred, if any.
  681. */
  682. box.certificates.service.validateCertificate = function(
  683. validationData, certificate) {
  684. if (validationData === null) {
  685. throw new exceptions.CryptoLibException('Validation data is null.');
  686. }
  687. if (Object.getOwnPropertyNames(validationData).length) {
  688. throw new exceptions.CryptoLibException('Validation data is empty.');
  689. }
  690. if (certificate === null) {
  691. throw new exceptions.CryptoLibException('Certificate is null.');
  692. }
  693. return (new box.certificates.CryptoX509CertificateValidator(validationData))
  694. .validate(certificate);
  695. };
  696. /**
  697. * Validates the certificate information provided to the constructor. The
  698. * validation process loops through all certificates, starting with the leaf
  699. * certificate, until it reaches the trusted certificate. For each
  700. * certificate, except the trusted certificate, it checks that the following
  701. * conditions hold:
  702. * <ul>
  703. * <li>Subject DN is that expected for given certificate.</li>
  704. * <li>Issuer DN is same as subject DN of next certificate in chain.</li>
  705. * <li>Key type is that expected: "signing" or "encryption" for leaf
  706. * certificate and "CA" for rest of certificates in chain.</li>
  707. * <li>Signature can be verified with public key of next certificate in
  708. * chain.</li>
  709. * <li>Starting time is earlier than ending time.</li>
  710. * <li>Starting time is equal to or later than starting time of next
  711. * certificate in chain.</li>
  712. * <li>Ending time is equal to or earlier than ending time of next
  713. * certificate in chain.</li>
  714. * </ul>
  715. * In addition, if a non-null value is provided to the constructor for the
  716. * time reference, it will be checked whether this time reference is within
  717. * the dates of validity of the leaf certificate. After the validation
  718. * process has completed, a list of strings will be returned. If this list
  719. * is empty, then the validation was successful. Otherwise, the list will
  720. * contain string identifiers for each type of validation that failed.
  721. *
  722. * @function
  723. * @param {json}
  724. * chain certificate chain.
  725. * @param {json}
  726. * chain.leaf leaf certificate.
  727. * @param {string}
  728. * chain.leaf.pem X.509 certificate.
  729. * @param {string}
  730. * chain.leaf.keyType keyType.
  731. * @param {string}
  732. * chain.leaf.subject subject.
  733. * @param {string}
  734. * chain.leaf.time time reference (Optional). Only mandatory if
  735. * the 'Time' validation is going to be performed.
  736. * @param {json}
  737. * chain.certificates chain of certificates.
  738. * @param {Array}
  739. * chain.certificates.pems list of X.509 Certificates as string
  740. * in pem format. The list starts by the one closer to the root.
  741. * @param {Array}
  742. * chain.certificates.subjects list of subjects
  743. * @param {Array}
  744. * chain.root X.509 trusted Certificate.
  745. * @returns {Array} a two dimension array where the first dimension is the
  746. * index of the element starting by 1 -leaf certificate - and the
  747. * second dimension holds the error types.
  748. */
  749. box.certificates.service.validateX509CertificateChain = function(chain) {
  750. var validateDateRange = function(certificate, failedValidation) {
  751. if (certificate.getNotBefore() > certificate.getNotAfter()) {
  752. failedValidation.push('VALIDITY_PERIOD');
  753. }
  754. };
  755. var validateNotBefore = function(
  756. notBefore, previousNotBefore, failedValidation) {
  757. if (notBefore < previousNotBefore) {
  758. failedValidation.push('NOT_BEFORE');
  759. }
  760. };
  761. var validateNotAfter = function(
  762. notAfter, previousNotAfter, failedValidation) {
  763. if (notAfter > previousNotAfter) {
  764. failedValidation.push('NOT_AFTER');
  765. }
  766. };
  767. var validateLeafCertificate = function(
  768. issuer, signature, previousNotBefore, previousNotAfter) {
  769. var validationData = {
  770. subject: chain.leaf.subject,
  771. issuer: issuer,
  772. keyType: chain.leaf.keyType,
  773. caCertificatePem: signature
  774. };
  775. if (chain.leaf.time) {
  776. validationData.time = chain.leaf.time;
  777. }
  778. var certificateValidator =
  779. new box.certificates.CryptoX509CertificateValidator(validationData);
  780. var failedValidations = certificateValidator.validate(chain.leaf.pem);
  781. var certificate =
  782. new box.certificates.CryptoX509Certificate(chain.leaf.pem);
  783. validateDateRange(certificate, failedValidations);
  784. validateNotBefore(
  785. certificate.getNotBefore(), previousNotBefore, failedValidations);
  786. validateNotAfter(
  787. certificate.getNotAfter(), previousNotAfter, failedValidations);
  788. return failedValidations;
  789. };
  790. var rootCertificate =
  791. new box.certificates.CryptoX509Certificate(chain.root);
  792. var issuer = {
  793. commonName: rootCertificate.getSubjectCN(),
  794. organization: rootCertificate.getSubjectOrg(),
  795. organizationUnit: rootCertificate.getSubjectOrgUnit(),
  796. country: rootCertificate.getSubjectCountry()
  797. };
  798. var signature = chain.root;
  799. var failedValidation = [];
  800. var previousNotBefore = rootCertificate.getNotBefore();
  801. var previousNotAfter = rootCertificate.getNotAfter();
  802. var certificateValidator;
  803. chain.certificates.pems.reverse();
  804. chain.certificates.subjects.reverse();
  805. for (var i = 0; i < chain.certificates.pems.length; i++) {
  806. var certificate = new box.certificates.CryptoX509Certificate(
  807. chain.certificates.pems[i]);
  808. var validationData = {
  809. subject: chain.certificates.subjects[i],
  810. issuer: issuer,
  811. keyType: 'CA',
  812. signature: signature
  813. };
  814. certificateValidator =
  815. new box.certificates.CryptoX509CertificateValidator(validationData);
  816. failedValidation[i] =
  817. certificateValidator.validate(chain.certificates.pems[i]);
  818. validateDateRange(certificate, failedValidation[i]);
  819. validateNotBefore(
  820. certificate.getNotBefore(), previousNotBefore, failedValidation[i]);
  821. validateNotAfter(
  822. certificate.getNotAfter(), previousNotAfter, failedValidation[i]);
  823. issuer = {
  824. commonName: certificate.getSubjectCN(),
  825. organization: certificate.getSubjectOrg(),
  826. organizationUnit: certificate.getSubjectOrgUnit(),
  827. country: certificate.getSubjectCountry()
  828. };
  829. signature = chain.certificates.pems[i];
  830. previousNotBefore = certificate.getNotBefore();
  831. previousNotAfter = certificate.getNotAfter();
  832. }
  833. failedValidation.push(validateLeafCertificate(
  834. issuer, signature, previousNotBefore, previousNotAfter));
  835. failedValidation.reverse();
  836. return failedValidation;
  837. };
  838. /**
  839. * Flattens failed validations to a one dimensional array to a one
  840. * dimensional array.
  841. *
  842. * @function
  843. * @param {array}
  844. * failedValidations a two-dimensional failed validations array.
  845. * @returns {array} a flat array in which each element contains the error
  846. * type message piped with the index of the element, i.e.
  847. * ERROR_<element index>.
  848. */
  849. box.certificates.service.flattenFailedValidations = function(
  850. failedValidations) {
  851. var flattenFailedValidations = Array();
  852. for (var i = 0; i < failedValidations.length; i++) {
  853. if (failedValidations[i] !== undefined) {
  854. for (var j = 0; j < failedValidations[i].length; j++) {
  855. flattenFailedValidations.push(
  856. failedValidations[i][j].toLowerCase() + '_' + (i));
  857. }
  858. }
  859. }
  860. return flattenFailedValidations;
  861. };
  862. };
  863. cryptolib.modules.certificates.service = cryptolib.modules.certificates;