stores.pkcs12.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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. cryptolib.modules.stores = cryptolib.modules.stores || {};
  9. /**
  10. * Defines a pkcs12 store.
  11. */
  12. cryptolib.modules.stores.pkcs12 = function(box) {
  13. 'use strict';
  14. box.stores = box.stores || {};
  15. box.stores.pkcs12 = {};
  16. var converters;
  17. var exceptions;
  18. cryptolib('commons', function(box) {
  19. converters = new box.commons.utils.Converters();
  20. exceptions = box.commons.exceptions;
  21. });
  22. /**
  23. * Defines a PKCS12.
  24. *
  25. * @param pkcs12DerB64
  26. * PKCS12, as string in Base64 encoded DER format.
  27. */
  28. box.stores.pkcs12.Pkcs12 = function(pkcs12DerB64) {
  29. this.pkcs12Der = converters.base64Decode(pkcs12DerB64);
  30. this.pkcs12Asn1 = forge.asn1.fromDer(this.pkcs12Der, false);
  31. };
  32. box.stores.pkcs12.Pkcs12.prototype = {
  33. /**
  34. * Retrieves the private key of the PKCS12.
  35. *
  36. * @param passwordB64
  37. * Password as string in Base64 encoded format.
  38. * @return Private key of PKCS12, as string in PEM format.
  39. */
  40. getPrivateKey: function(passwordB64) {
  41. var aliases = Array.prototype.slice.call(arguments, 1);
  42. function isAliasInList(alias) {
  43. var returnValue = false;
  44. if (aliases.indexOf(alias) >= 0) {
  45. returnValue = true;
  46. }
  47. return returnValue;
  48. }
  49. var privateKeyAlias = '';
  50. try {
  51. var password = converters.base64Decode(passwordB64);
  52. var pkcs12 =
  53. forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);
  54. // Retrieve private key safe bag from PKCS12.
  55. var privateKeys = {};
  56. for (var i = 0; i < pkcs12.safeContents.length; i++) {
  57. var safeContents = pkcs12.safeContents[i];
  58. for (var j = 0; j < safeContents.safeBags.length; j++) {
  59. var safeBag = safeContents.safeBags[j];
  60. if (safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag) {
  61. privateKeyAlias = safeBag.attributes.friendlyName[0];
  62. if (aliases.length === 0 || isAliasInList(privateKeyAlias)) {
  63. privateKeys[privateKeyAlias] =
  64. forge.pki.privateKeyToPem(safeBag.key);
  65. }
  66. }
  67. }
  68. }
  69. // Check whether any private key safe bags were found.
  70. if (Object.getOwnPropertyNames(privateKeys).length === 0) {
  71. throw new exceptions.CryptoLibException(
  72. 'Could not find any private key safe bags in PKCS12.');
  73. }
  74. return privateKeys;
  75. } catch (error) {
  76. var errorMsg = 'Could not retrieve private key from PKCS12.';
  77. if (privateKeyAlias.length > 0) {
  78. errorMsg = 'Could not retrieve private key with alias ' +
  79. privateKeyAlias + ' from PKCS12.';
  80. }
  81. throw new exceptions.CryptoLibException(errorMsg);
  82. }
  83. },
  84. /**
  85. * Retrieves the private key chain from the PKCS12.
  86. *
  87. * @param passwordB64
  88. * Password as string in Base64 encoded format.
  89. * @return Private key chain of PKCS12, as PrivateKeyChain object.
  90. */
  91. getPrivateKeyChain: function(passwordB64) {
  92. try {
  93. var password = converters.base64Decode(passwordB64);
  94. var pkcs12 =
  95. forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);
  96. // Retrieve private key safe bags from PKCS12.
  97. var privateKeyChain = new box.stores.pkcs12.PrivateKeyChain();
  98. for (var i = 0; i < pkcs12.safeContents.length; i++) {
  99. var safeContents = pkcs12.safeContents[i];
  100. for (var j = 0; j < safeContents.safeBags.length; j++) {
  101. var safeBag = safeContents.safeBags[j];
  102. if (safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag) {
  103. var alias = safeBag.attributes.friendlyName[0];
  104. var privateKeyPem = forge.pki.privateKeyToPem(safeBag.key);
  105. privateKeyChain.add(alias, privateKeyPem);
  106. }
  107. }
  108. }
  109. // Check whether any private key safe bags were found.
  110. if (privateKeyChain.length === 0) {
  111. throw new exceptions.CryptoLibException(
  112. 'Could not find any private key safe bags in PKCS12.');
  113. }
  114. return privateKeyChain;
  115. } catch (error) {
  116. throw new exceptions.CryptoLibException(
  117. 'Could not retrieve private key chain from PKCS12.', error);
  118. }
  119. },
  120. /**
  121. * Retrieves the certificate chain of the PKCS12.
  122. *
  123. * @param passwordB64
  124. * Password as string in Base64 encoded format.
  125. * @return Certificate chain of PKCS12, as array of strings in PEM
  126. * format.
  127. */
  128. getCertificateChain: function(passwordB64) {
  129. try {
  130. var password = converters.base64Decode(passwordB64);
  131. var pkcs12 =
  132. forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);
  133. // Retrieve certificate safe bags from PKCS12.
  134. var certificatePemChain = [];
  135. for (var i = 0; i < pkcs12.safeContents.length; i++) {
  136. var safeContents = pkcs12.safeContents[i];
  137. for (var j = 0; j < safeContents.safeBags.length; j++) {
  138. var safeBag = safeContents.safeBags[j];
  139. if (safeBag.type === forge.pki.oids.certBag) {
  140. var certificatePem = forge.pki.certificateToPem(safeBag.cert);
  141. certificatePemChain.push(certificatePem);
  142. }
  143. }
  144. }
  145. // Check whether any certificate safe bags were found.
  146. if (certificatePemChain.length === 0) {
  147. throw new exceptions.CryptoLibException(
  148. 'Could not find any certificate safe bags in PKCS12.');
  149. }
  150. return certificatePemChain;
  151. } catch (error) {
  152. throw new exceptions.CryptoLibException(
  153. 'Could not retrieve certificate chain from PKCS12.', error);
  154. }
  155. },
  156. /**
  157. * Retrieves the certificate chain of the PKCS12 filtered by alias.
  158. *
  159. * @param passwordB64
  160. * Password as string in Base64 encoded format.
  161. * @param alias
  162. * Alias to filter
  163. * @return Certificate chain of PKCS12, as array of strings in PEM
  164. * format.
  165. */
  166. getCertificateChainByAlias: function(passwordB64, alias) {
  167. try {
  168. var password = converters.base64Decode(passwordB64);
  169. var pkcs12 =
  170. forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);
  171. var discardedBags = {};
  172. var nextBagId = null;
  173. // Retrieve certificate safe bags from PKCS12.
  174. var certificatePemChain = [];
  175. for (var i = 0; i < pkcs12.safeContents.length; i++) {
  176. var safeContents = pkcs12.safeContents[i];
  177. for (var j = 0; j < safeContents.safeBags.length; j++) {
  178. var safeBag = safeContents.safeBags[j];
  179. if (safeBag.type === forge.pki.oids.certBag) {
  180. var bagFound = null;
  181. // if not initializated the chain, find the one with alias
  182. // or if is the one that goes next into the list take it
  183. if ((!nextBagId && safeBag.attributes &&
  184. safeBag.attributes.friendlyName &&
  185. safeBag.attributes.friendlyName[0] === alias) ||
  186. (nextBagId && nextBagId === safeBag.cert.subject.hash)) {
  187. bagFound = safeBag;
  188. }
  189. // otherwise, if not initializated and it's not the alias, save it
  190. // for later use
  191. else {
  192. discardedBags[safeBag.cert.subject.hash] = safeBag;
  193. }
  194. // add the bag and check if we have the next one in the list we
  195. // had
  196. while (bagFound) {
  197. // transform the current to PEM and save it
  198. var certificatePem = forge.pki.certificateToPem(bagFound.cert);
  199. certificatePemChain.push(certificatePem);
  200. // we arrived to the root so we are done!
  201. if (bagFound.cert.subject.hash === bagFound.cert.issuer.hash) {
  202. return certificatePemChain;
  203. }
  204. // save which is the next bag we need
  205. nextBagId = bagFound.cert.issuer.hash;
  206. // go for the next in the list of discarded ones
  207. bagFound = discardedBags[nextBagId];
  208. }
  209. }
  210. }
  211. }
  212. // Check whether any certificate safe bags were found.
  213. if (certificatePemChain.length === 0) {
  214. throw new exceptions.CryptoLibException(
  215. 'Could not find any certificates for that alias.');
  216. }
  217. return certificatePemChain;
  218. } catch (error) {
  219. throw new exceptions.CryptoLibException(
  220. 'Could not retrieve certificate chain from PKCS12.', error);
  221. }
  222. },
  223. /**
  224. * Retrieves an issuer certificate from the certificate chain of the
  225. * PKCS12, given the issuer common name.
  226. *
  227. * @param passwordB64
  228. * Password as string in Base64 encoded format.
  229. * @param issuerCn
  230. * Issuer common name of certificate to retrieve, as string.
  231. * @return Certificate with given issuer common name, as string in PEM
  232. * format.
  233. */
  234. getCertificateByIssuer: function(passwordB64, issuerCn) {
  235. try {
  236. var password = converters.base64Decode(passwordB64);
  237. var pkcs12 =
  238. forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);
  239. // Retrieve certificate from PKCS12.
  240. var certificate = null;
  241. var certificateFound = false;
  242. for (var i = 0; i < pkcs12.safeContents.length; i++) {
  243. var safeContents = pkcs12.safeContents[i];
  244. for (var j = 0; j < safeContents.safeBags.length; j++) {
  245. var safeBag = safeContents.safeBags[j];
  246. if (safeBag.type === forge.pki.oids.certBag &&
  247. safeBag.cert.issuer.getField('CN').value === issuerCn) {
  248. certificate = safeBag.cert;
  249. certificateFound = true;
  250. break;
  251. }
  252. }
  253. if (certificateFound) {
  254. break;
  255. }
  256. }
  257. // Check whether certificate was found.
  258. if (certificate === null) {
  259. throw new exceptions.CryptoLibException(
  260. 'Could not find certificate in PKCS12 with issuer common name ' +
  261. issuerCn);
  262. }
  263. return forge.pki.certificateToPem(certificate);
  264. } catch (error) {
  265. throw new exceptions.CryptoLibException(
  266. 'Could not retrieve certificate with issuer common name ' +
  267. issuerCn + ' from PKCS12.',
  268. error);
  269. }
  270. },
  271. /**
  272. * Retrieves the subject certificate from the certificate chain of the
  273. * PKCS12, given the subject common name.
  274. *
  275. * @param passwordB64
  276. * Password as string in Base64 encoded format.
  277. * @param subjectCn
  278. * Subject common name of certificate to retrieve, as string.
  279. * @return Certificate with given issuer common name, as string in PEM
  280. * format.
  281. */
  282. getCertificateBySubject: function(passwordB64, subjectCn) {
  283. try {
  284. var password = converters.base64Decode(passwordB64);
  285. var pkcs12 =
  286. forge.pkcs12.pkcs12FromAsn1(this.pkcs12Asn1, false, password);
  287. // Retrieve subject certificate from PKCS12.
  288. var certificate = null;
  289. var certificateFound = false;
  290. for (var i = 0; i < pkcs12.safeContents.length; i++) {
  291. var safeContents = pkcs12.safeContents[i];
  292. for (var j = 0; j < safeContents.safeBags.length; j++) {
  293. var safeBag = safeContents.safeBags[j];
  294. if (safeBag.type === forge.pki.oids.certBag &&
  295. safeBag.cert.subject.getField('CN').value === subjectCn) {
  296. certificate = safeBag.cert;
  297. certificateFound = true;
  298. break;
  299. }
  300. }
  301. if (certificateFound) {
  302. break;
  303. }
  304. }
  305. // Check whether certificate was found.
  306. if (certificate === null) {
  307. throw new exceptions.CryptoLibException(
  308. 'Could not find certificate in PKCS12 with subject common name ' +
  309. subjectCn);
  310. }
  311. return forge.pki.certificateToPem(certificate);
  312. } catch (error) {
  313. throw new exceptions.CryptoLibException(
  314. 'Could not retrieve certificate with subject common name ' +
  315. subjectCn + ' from PKCS12.',
  316. error);
  317. }
  318. },
  319. /**
  320. * Retrieves the PKCS12 as a Base64 encoded DER string.
  321. *
  322. * @return PKCS12, as Base64 encoded DER string.
  323. */
  324. getBase64Encoded: function() {
  325. return converters.base64Encode(this.pkcs12Der);
  326. }
  327. };
  328. /**
  329. * Container class for a chain of private keys to be inserted in a key
  330. * store.
  331. */
  332. box.stores.pkcs12.PrivateKeyChain = function() {
  333. this.map = {};
  334. };
  335. box.stores.pkcs12.PrivateKeyChain.prototype = {
  336. /**
  337. * Adds a private key to the chain.
  338. *
  339. * @param alias
  340. * Alias of private key, as string.
  341. * @param privateKeyPem
  342. * Private key to add, as string in PEM format.
  343. */
  344. add: function(alias, privateKeyPem) {
  345. this.map[alias] = privateKeyPem;
  346. },
  347. /**
  348. * Retrieves a private key from the chain.
  349. *
  350. * @return Private key chain, as array of strings in PEM format.
  351. */
  352. get: function(alias) {
  353. return this.map[alias];
  354. }
  355. };
  356. };