You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1074 lines
33 KiB

4 years ago
  1. /**
  2. * Javascript implementation of PKCS#12.
  3. *
  4. * @author Dave Longley
  5. * @author Stefan Siegl <stesie@brokenpipe.de>
  6. *
  7. * Copyright (c) 2010-2014 Digital Bazaar, Inc.
  8. * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
  9. *
  10. * The ASN.1 representation of PKCS#12 is as follows
  11. * (see ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12-tc1.pdf for details)
  12. *
  13. * PFX ::= SEQUENCE {
  14. * version INTEGER {v3(3)}(v3,...),
  15. * authSafe ContentInfo,
  16. * macData MacData OPTIONAL
  17. * }
  18. *
  19. * MacData ::= SEQUENCE {
  20. * mac DigestInfo,
  21. * macSalt OCTET STRING,
  22. * iterations INTEGER DEFAULT 1
  23. * }
  24. * Note: The iterations default is for historical reasons and its use is
  25. * deprecated. A higher value, like 1024, is recommended.
  26. *
  27. * DigestInfo is defined in PKCS#7 as follows:
  28. *
  29. * DigestInfo ::= SEQUENCE {
  30. * digestAlgorithm DigestAlgorithmIdentifier,
  31. * digest Digest
  32. * }
  33. *
  34. * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
  35. *
  36. * The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
  37. * for the algorithm, if any. In the case of SHA1 there is none.
  38. *
  39. * AlgorithmIdentifer ::= SEQUENCE {
  40. * algorithm OBJECT IDENTIFIER,
  41. * parameters ANY DEFINED BY algorithm OPTIONAL
  42. * }
  43. *
  44. * Digest ::= OCTET STRING
  45. *
  46. *
  47. * ContentInfo ::= SEQUENCE {
  48. * contentType ContentType,
  49. * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
  50. * }
  51. *
  52. * ContentType ::= OBJECT IDENTIFIER
  53. *
  54. * AuthenticatedSafe ::= SEQUENCE OF ContentInfo
  55. * -- Data if unencrypted
  56. * -- EncryptedData if password-encrypted
  57. * -- EnvelopedData if public key-encrypted
  58. *
  59. *
  60. * SafeContents ::= SEQUENCE OF SafeBag
  61. *
  62. * SafeBag ::= SEQUENCE {
  63. * bagId BAG-TYPE.&id ({PKCS12BagSet})
  64. * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
  65. * bagAttributes SET OF PKCS12Attribute OPTIONAL
  66. * }
  67. *
  68. * PKCS12Attribute ::= SEQUENCE {
  69. * attrId ATTRIBUTE.&id ({PKCS12AttrSet}),
  70. * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId})
  71. * } -- This type is compatible with the X.500 type 'Attribute'
  72. *
  73. * PKCS12AttrSet ATTRIBUTE ::= {
  74. * friendlyName | -- from PKCS #9
  75. * localKeyId, -- from PKCS #9
  76. * ... -- Other attributes are allowed
  77. * }
  78. *
  79. * CertBag ::= SEQUENCE {
  80. * certId BAG-TYPE.&id ({CertTypes}),
  81. * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId})
  82. * }
  83. *
  84. * x509Certificate BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}}
  85. * -- DER-encoded X.509 certificate stored in OCTET STRING
  86. *
  87. * sdsiCertificate BAG-TYPE ::= {IA5String IDENTIFIED BY {certTypes 2}}
  88. * -- Base64-encoded SDSI certificate stored in IA5String
  89. *
  90. * CertTypes BAG-TYPE ::= {
  91. * x509Certificate |
  92. * sdsiCertificate,
  93. * ... -- For future extensions
  94. * }
  95. */
  96. var forge = require('./forge');
  97. require('./asn1');
  98. require('./hmac');
  99. require('./oids');
  100. require('./pkcs7asn1');
  101. require('./pbe');
  102. require('./random');
  103. require('./rsa');
  104. require('./sha1');
  105. require('./util');
  106. require('./x509');
  107. // shortcut for asn.1 & PKI API
  108. var asn1 = forge.asn1;
  109. var pki = forge.pki;
  110. // shortcut for PKCS#12 API
  111. var p12 = module.exports = forge.pkcs12 = forge.pkcs12 || {};
  112. var contentInfoValidator = {
  113. name: 'ContentInfo',
  114. tagClass: asn1.Class.UNIVERSAL,
  115. type: asn1.Type.SEQUENCE, // a ContentInfo
  116. constructed: true,
  117. value: [{
  118. name: 'ContentInfo.contentType',
  119. tagClass: asn1.Class.UNIVERSAL,
  120. type: asn1.Type.OID,
  121. constructed: false,
  122. capture: 'contentType'
  123. }, {
  124. name: 'ContentInfo.content',
  125. tagClass: asn1.Class.CONTEXT_SPECIFIC,
  126. constructed: true,
  127. captureAsn1: 'content'
  128. }]
  129. };
  130. var pfxValidator = {
  131. name: 'PFX',
  132. tagClass: asn1.Class.UNIVERSAL,
  133. type: asn1.Type.SEQUENCE,
  134. constructed: true,
  135. value: [{
  136. name: 'PFX.version',
  137. tagClass: asn1.Class.UNIVERSAL,
  138. type: asn1.Type.INTEGER,
  139. constructed: false,
  140. capture: 'version'
  141. },
  142. contentInfoValidator, {
  143. name: 'PFX.macData',
  144. tagClass: asn1.Class.UNIVERSAL,
  145. type: asn1.Type.SEQUENCE,
  146. constructed: true,
  147. optional: true,
  148. captureAsn1: 'mac',
  149. value: [{
  150. name: 'PFX.macData.mac',
  151. tagClass: asn1.Class.UNIVERSAL,
  152. type: asn1.Type.SEQUENCE, // DigestInfo
  153. constructed: true,
  154. value: [{
  155. name: 'PFX.macData.mac.digestAlgorithm',
  156. tagClass: asn1.Class.UNIVERSAL,
  157. type: asn1.Type.SEQUENCE, // DigestAlgorithmIdentifier
  158. constructed: true,
  159. value: [{
  160. name: 'PFX.macData.mac.digestAlgorithm.algorithm',
  161. tagClass: asn1.Class.UNIVERSAL,
  162. type: asn1.Type.OID,
  163. constructed: false,
  164. capture: 'macAlgorithm'
  165. }, {
  166. name: 'PFX.macData.mac.digestAlgorithm.parameters',
  167. tagClass: asn1.Class.UNIVERSAL,
  168. captureAsn1: 'macAlgorithmParameters'
  169. }]
  170. }, {
  171. name: 'PFX.macData.mac.digest',
  172. tagClass: asn1.Class.UNIVERSAL,
  173. type: asn1.Type.OCTETSTRING,
  174. constructed: false,
  175. capture: 'macDigest'
  176. }]
  177. }, {
  178. name: 'PFX.macData.macSalt',
  179. tagClass: asn1.Class.UNIVERSAL,
  180. type: asn1.Type.OCTETSTRING,
  181. constructed: false,
  182. capture: 'macSalt'
  183. }, {
  184. name: 'PFX.macData.iterations',
  185. tagClass: asn1.Class.UNIVERSAL,
  186. type: asn1.Type.INTEGER,
  187. constructed: false,
  188. optional: true,
  189. capture: 'macIterations'
  190. }]
  191. }]
  192. };
  193. var safeBagValidator = {
  194. name: 'SafeBag',
  195. tagClass: asn1.Class.UNIVERSAL,
  196. type: asn1.Type.SEQUENCE,
  197. constructed: true,
  198. value: [{
  199. name: 'SafeBag.bagId',
  200. tagClass: asn1.Class.UNIVERSAL,
  201. type: asn1.Type.OID,
  202. constructed: false,
  203. capture: 'bagId'
  204. }, {
  205. name: 'SafeBag.bagValue',
  206. tagClass: asn1.Class.CONTEXT_SPECIFIC,
  207. constructed: true,
  208. captureAsn1: 'bagValue'
  209. }, {
  210. name: 'SafeBag.bagAttributes',
  211. tagClass: asn1.Class.UNIVERSAL,
  212. type: asn1.Type.SET,
  213. constructed: true,
  214. optional: true,
  215. capture: 'bagAttributes'
  216. }]
  217. };
  218. var attributeValidator = {
  219. name: 'Attribute',
  220. tagClass: asn1.Class.UNIVERSAL,
  221. type: asn1.Type.SEQUENCE,
  222. constructed: true,
  223. value: [{
  224. name: 'Attribute.attrId',
  225. tagClass: asn1.Class.UNIVERSAL,
  226. type: asn1.Type.OID,
  227. constructed: false,
  228. capture: 'oid'
  229. }, {
  230. name: 'Attribute.attrValues',
  231. tagClass: asn1.Class.UNIVERSAL,
  232. type: asn1.Type.SET,
  233. constructed: true,
  234. capture: 'values'
  235. }]
  236. };
  237. var certBagValidator = {
  238. name: 'CertBag',
  239. tagClass: asn1.Class.UNIVERSAL,
  240. type: asn1.Type.SEQUENCE,
  241. constructed: true,
  242. value: [{
  243. name: 'CertBag.certId',
  244. tagClass: asn1.Class.UNIVERSAL,
  245. type: asn1.Type.OID,
  246. constructed: false,
  247. capture: 'certId'
  248. }, {
  249. name: 'CertBag.certValue',
  250. tagClass: asn1.Class.CONTEXT_SPECIFIC,
  251. constructed: true,
  252. /* So far we only support X.509 certificates (which are wrapped in
  253. an OCTET STRING, hence hard code that here). */
  254. value: [{
  255. name: 'CertBag.certValue[0]',
  256. tagClass: asn1.Class.UNIVERSAL,
  257. type: asn1.Class.OCTETSTRING,
  258. constructed: false,
  259. capture: 'cert'
  260. }]
  261. }]
  262. };
  263. /**
  264. * Search SafeContents structure for bags with matching attributes.
  265. *
  266. * The search can optionally be narrowed by a certain bag type.
  267. *
  268. * @param safeContents the SafeContents structure to search in.
  269. * @param attrName the name of the attribute to compare against.
  270. * @param attrValue the attribute value to search for.
  271. * @param [bagType] bag type to narrow search by.
  272. *
  273. * @return an array of matching bags.
  274. */
  275. function _getBagsByAttribute(safeContents, attrName, attrValue, bagType) {
  276. var result = [];
  277. for(var i = 0; i < safeContents.length; i++) {
  278. for(var j = 0; j < safeContents[i].safeBags.length; j++) {
  279. var bag = safeContents[i].safeBags[j];
  280. if(bagType !== undefined && bag.type !== bagType) {
  281. continue;
  282. }
  283. // only filter by bag type, no attribute specified
  284. if(attrName === null) {
  285. result.push(bag);
  286. continue;
  287. }
  288. if(bag.attributes[attrName] !== undefined &&
  289. bag.attributes[attrName].indexOf(attrValue) >= 0) {
  290. result.push(bag);
  291. }
  292. }
  293. }
  294. return result;
  295. }
  296. /**
  297. * Converts a PKCS#12 PFX in ASN.1 notation into a PFX object.
  298. *
  299. * @param obj The PKCS#12 PFX in ASN.1 notation.
  300. * @param strict true to use strict DER decoding, false not to (default: true).
  301. * @param {String} password Password to decrypt with (optional).
  302. *
  303. * @return PKCS#12 PFX object.
  304. */
  305. p12.pkcs12FromAsn1 = function(obj, strict, password) {
  306. // handle args
  307. if(typeof strict === 'string') {
  308. password = strict;
  309. strict = true;
  310. } else if(strict === undefined) {
  311. strict = true;
  312. }
  313. // validate PFX and capture data
  314. var capture = {};
  315. var errors = [];
  316. if(!asn1.validate(obj, pfxValidator, capture, errors)) {
  317. var error = new Error('Cannot read PKCS#12 PFX. ' +
  318. 'ASN.1 object is not an PKCS#12 PFX.');
  319. error.errors = error;
  320. throw error;
  321. }
  322. var pfx = {
  323. version: capture.version.charCodeAt(0),
  324. safeContents: [],
  325. /**
  326. * Gets bags with matching attributes.
  327. *
  328. * @param filter the attributes to filter by:
  329. * [localKeyId] the localKeyId to search for.
  330. * [localKeyIdHex] the localKeyId in hex to search for.
  331. * [friendlyName] the friendly name to search for.
  332. * [bagType] bag type to narrow each attribute search by.
  333. *
  334. * @return a map of attribute type to an array of matching bags or, if no
  335. * attribute was given but a bag type, the map key will be the
  336. * bag type.
  337. */
  338. getBags: function(filter) {
  339. var rval = {};
  340. var localKeyId;
  341. if('localKeyId' in filter) {
  342. localKeyId = filter.localKeyId;
  343. } else if('localKeyIdHex' in filter) {
  344. localKeyId = forge.util.hexToBytes(filter.localKeyIdHex);
  345. }
  346. // filter on bagType only
  347. if(localKeyId === undefined && !('friendlyName' in filter) &&
  348. 'bagType' in filter) {
  349. rval[filter.bagType] = _getBagsByAttribute(
  350. pfx.safeContents, null, null, filter.bagType);
  351. }
  352. if(localKeyId !== undefined) {
  353. rval.localKeyId = _getBagsByAttribute(
  354. pfx.safeContents, 'localKeyId',
  355. localKeyId, filter.bagType);
  356. }
  357. if('friendlyName' in filter) {
  358. rval.friendlyName = _getBagsByAttribute(
  359. pfx.safeContents, 'friendlyName',
  360. filter.friendlyName, filter.bagType);
  361. }
  362. return rval;
  363. },
  364. /**
  365. * DEPRECATED: use getBags() instead.
  366. *
  367. * Get bags with matching friendlyName attribute.
  368. *
  369. * @param friendlyName the friendly name to search for.
  370. * @param [bagType] bag type to narrow search by.
  371. *
  372. * @return an array of bags with matching friendlyName attribute.
  373. */
  374. getBagsByFriendlyName: function(friendlyName, bagType) {
  375. return _getBagsByAttribute(
  376. pfx.safeContents, 'friendlyName', friendlyName, bagType);
  377. },
  378. /**
  379. * DEPRECATED: use getBags() instead.
  380. *
  381. * Get bags with matching localKeyId attribute.
  382. *
  383. * @param localKeyId the localKeyId to search for.
  384. * @param [bagType] bag type to narrow search by.
  385. *
  386. * @return an array of bags with matching localKeyId attribute.
  387. */
  388. getBagsByLocalKeyId: function(localKeyId, bagType) {
  389. return _getBagsByAttribute(
  390. pfx.safeContents, 'localKeyId', localKeyId, bagType);
  391. }
  392. };
  393. if(capture.version.charCodeAt(0) !== 3) {
  394. var error = new Error('PKCS#12 PFX of version other than 3 not supported.');
  395. error.version = capture.version.charCodeAt(0);
  396. throw error;
  397. }
  398. if(asn1.derToOid(capture.contentType) !== pki.oids.data) {
  399. var error = new Error('Only PKCS#12 PFX in password integrity mode supported.');
  400. error.oid = asn1.derToOid(capture.contentType);
  401. throw error;
  402. }
  403. var data = capture.content.value[0];
  404. if(data.tagClass !== asn1.Class.UNIVERSAL ||
  405. data.type !== asn1.Type.OCTETSTRING) {
  406. throw new Error('PKCS#12 authSafe content data is not an OCTET STRING.');
  407. }
  408. data = _decodePkcs7Data(data);
  409. // check for MAC
  410. if(capture.mac) {
  411. var md = null;
  412. var macKeyBytes = 0;
  413. var macAlgorithm = asn1.derToOid(capture.macAlgorithm);
  414. switch(macAlgorithm) {
  415. case pki.oids.sha1:
  416. md = forge.md.sha1.create();
  417. macKeyBytes = 20;
  418. break;
  419. case pki.oids.sha256:
  420. md = forge.md.sha256.create();
  421. macKeyBytes = 32;
  422. break;
  423. case pki.oids.sha384:
  424. md = forge.md.sha384.create();
  425. macKeyBytes = 48;
  426. break;
  427. case pki.oids.sha512:
  428. md = forge.md.sha512.create();
  429. macKeyBytes = 64;
  430. break;
  431. case pki.oids.md5:
  432. md = forge.md.md5.create();
  433. macKeyBytes = 16;
  434. break;
  435. }
  436. if(md === null) {
  437. throw new Error('PKCS#12 uses unsupported MAC algorithm: ' + macAlgorithm);
  438. }
  439. // verify MAC (iterations default to 1)
  440. var macSalt = new forge.util.ByteBuffer(capture.macSalt);
  441. var macIterations = (('macIterations' in capture) ?
  442. parseInt(forge.util.bytesToHex(capture.macIterations), 16) : 1);
  443. var macKey = p12.generateKey(
  444. password, macSalt, 3, macIterations, macKeyBytes, md);
  445. var mac = forge.hmac.create();
  446. mac.start(md, macKey);
  447. mac.update(data.value);
  448. var macValue = mac.getMac();
  449. if(macValue.getBytes() !== capture.macDigest) {
  450. throw new Error('PKCS#12 MAC could not be verified. Invalid password?');
  451. }
  452. }
  453. _decodeAuthenticatedSafe(pfx, data.value, strict, password);
  454. return pfx;
  455. };
  456. /**
  457. * Decodes PKCS#7 Data. PKCS#7 (RFC 2315) defines "Data" as an OCTET STRING,
  458. * but it is sometimes an OCTET STRING that is composed/constructed of chunks,
  459. * each its own OCTET STRING. This is BER-encoding vs. DER-encoding. This
  460. * function transforms this corner-case into the usual simple,
  461. * non-composed/constructed OCTET STRING.
  462. *
  463. * This function may be moved to ASN.1 at some point to better deal with
  464. * more BER-encoding issues, should they arise.
  465. *
  466. * @param data the ASN.1 Data object to transform.
  467. */
  468. function _decodePkcs7Data(data) {
  469. // handle special case of "chunked" data content: an octet string composed
  470. // of other octet strings
  471. if(data.composed || data.constructed) {
  472. var value = forge.util.createBuffer();
  473. for(var i = 0; i < data.value.length; ++i) {
  474. value.putBytes(data.value[i].value);
  475. }
  476. data.composed = data.constructed = false;
  477. data.value = value.getBytes();
  478. }
  479. return data;
  480. }
  481. /**
  482. * Decode PKCS#12 AuthenticatedSafe (BER encoded) into PFX object.
  483. *
  484. * The AuthenticatedSafe is a BER-encoded SEQUENCE OF ContentInfo.
  485. *
  486. * @param pfx The PKCS#12 PFX object to fill.
  487. * @param {String} authSafe BER-encoded AuthenticatedSafe.
  488. * @param strict true to use strict DER decoding, false not to.
  489. * @param {String} password Password to decrypt with (optional).
  490. */
  491. function _decodeAuthenticatedSafe(pfx, authSafe, strict, password) {
  492. authSafe = asn1.fromDer(authSafe, strict); /* actually it's BER encoded */
  493. if(authSafe.tagClass !== asn1.Class.UNIVERSAL ||
  494. authSafe.type !== asn1.Type.SEQUENCE ||
  495. authSafe.constructed !== true) {
  496. throw new Error('PKCS#12 AuthenticatedSafe expected to be a ' +
  497. 'SEQUENCE OF ContentInfo');
  498. }
  499. for(var i = 0; i < authSafe.value.length; i++) {
  500. var contentInfo = authSafe.value[i];
  501. // validate contentInfo and capture data
  502. var capture = {};
  503. var errors = [];
  504. if(!asn1.validate(contentInfo, contentInfoValidator, capture, errors)) {
  505. var error = new Error('Cannot read ContentInfo.');
  506. error.errors = errors;
  507. throw error;
  508. }
  509. var obj = {
  510. encrypted: false
  511. };
  512. var safeContents = null;
  513. var data = capture.content.value[0];
  514. switch(asn1.derToOid(capture.contentType)) {
  515. case pki.oids.data:
  516. if(data.tagClass !== asn1.Class.UNIVERSAL ||
  517. data.type !== asn1.Type.OCTETSTRING) {
  518. throw new Error('PKCS#12 SafeContents Data is not an OCTET STRING.');
  519. }
  520. safeContents = _decodePkcs7Data(data).value;
  521. break;
  522. case pki.oids.encryptedData:
  523. safeContents = _decryptSafeContents(data, password);
  524. obj.encrypted = true;
  525. break;
  526. default:
  527. var error = new Error('Unsupported PKCS#12 contentType.');
  528. error.contentType = asn1.derToOid(capture.contentType);
  529. throw error;
  530. }
  531. obj.safeBags = _decodeSafeContents(safeContents, strict, password);
  532. pfx.safeContents.push(obj);
  533. }
  534. }
  535. /**
  536. * Decrypt PKCS#7 EncryptedData structure.
  537. *
  538. * @param data ASN.1 encoded EncryptedContentInfo object.
  539. * @param password The user-provided password.
  540. *
  541. * @return The decrypted SafeContents (ASN.1 object).
  542. */
  543. function _decryptSafeContents(data, password) {
  544. var capture = {};
  545. var errors = [];
  546. if(!asn1.validate(
  547. data, forge.pkcs7.asn1.encryptedDataValidator, capture, errors)) {
  548. var error = new Error('Cannot read EncryptedContentInfo.');
  549. error.errors = errors;
  550. throw error;
  551. }
  552. var oid = asn1.derToOid(capture.contentType);
  553. if(oid !== pki.oids.data) {
  554. var error = new Error(
  555. 'PKCS#12 EncryptedContentInfo ContentType is not Data.');
  556. error.oid = oid;
  557. throw error;
  558. }
  559. // get cipher
  560. oid = asn1.derToOid(capture.encAlgorithm);
  561. var cipher = pki.pbe.getCipher(oid, capture.encParameter, password);
  562. // get encrypted data
  563. var encryptedContentAsn1 = _decodePkcs7Data(capture.encryptedContentAsn1);
  564. var encrypted = forge.util.createBuffer(encryptedContentAsn1.value);
  565. cipher.update(encrypted);
  566. if(!cipher.finish()) {
  567. throw new Error('Failed to decrypt PKCS#12 SafeContents.');
  568. }
  569. return cipher.output.getBytes();
  570. }
  571. /**
  572. * Decode PKCS#12 SafeContents (BER-encoded) into array of Bag objects.
  573. *
  574. * The safeContents is a BER-encoded SEQUENCE OF SafeBag.
  575. *
  576. * @param {String} safeContents BER-encoded safeContents.
  577. * @param strict true to use strict DER decoding, false not to.
  578. * @param {String} password Password to decrypt with (optional).
  579. *
  580. * @return {Array} Array of Bag objects.
  581. */
  582. function _decodeSafeContents(safeContents, strict, password) {
  583. // if strict and no safe contents, return empty safes
  584. if(!strict && safeContents.length === 0) {
  585. return [];
  586. }
  587. // actually it's BER-encoded
  588. safeContents = asn1.fromDer(safeContents, strict);
  589. if(safeContents.tagClass !== asn1.Class.UNIVERSAL ||
  590. safeContents.type !== asn1.Type.SEQUENCE ||
  591. safeContents.constructed !== true) {
  592. throw new Error(
  593. 'PKCS#12 SafeContents expected to be a SEQUENCE OF SafeBag.');
  594. }
  595. var res = [];
  596. for(var i = 0; i < safeContents.value.length; i++) {
  597. var safeBag = safeContents.value[i];
  598. // validate SafeBag and capture data
  599. var capture = {};
  600. var errors = [];
  601. if(!asn1.validate(safeBag, safeBagValidator, capture, errors)) {
  602. var error = new Error('Cannot read SafeBag.');
  603. error.errors = errors;
  604. throw error;
  605. }
  606. /* Create bag object and push to result array. */
  607. var bag = {
  608. type: asn1.derToOid(capture.bagId),
  609. attributes: _decodeBagAttributes(capture.bagAttributes)
  610. };
  611. res.push(bag);
  612. var validator, decoder;
  613. var bagAsn1 = capture.bagValue.value[0];
  614. switch(bag.type) {
  615. case pki.oids.pkcs8ShroudedKeyBag:
  616. /* bagAsn1 has a EncryptedPrivateKeyInfo, which we need to decrypt.
  617. Afterwards we can handle it like a keyBag,
  618. which is a PrivateKeyInfo. */
  619. bagAsn1 = pki.decryptPrivateKeyInfo(bagAsn1, password);
  620. if(bagAsn1 === null) {
  621. throw new Error(
  622. 'Unable to decrypt PKCS#8 ShroudedKeyBag, wrong password?');
  623. }
  624. /* fall through */
  625. case pki.oids.keyBag:
  626. /* A PKCS#12 keyBag is a simple PrivateKeyInfo as understood by our
  627. PKI module, hence we don't have to do validation/capturing here,
  628. just pass what we already got. */
  629. try {
  630. bag.key = pki.privateKeyFromAsn1(bagAsn1);
  631. } catch(e) {
  632. // ignore unknown key type, pass asn1 value
  633. bag.key = null;
  634. bag.asn1 = bagAsn1;
  635. }
  636. continue; /* Nothing more to do. */
  637. case pki.oids.certBag:
  638. /* A PKCS#12 certBag can wrap both X.509 and sdsi certificates.
  639. Therefore put the SafeBag content through another validator to
  640. capture the fields. Afterwards check & store the results. */
  641. validator = certBagValidator;
  642. decoder = function() {
  643. if(asn1.derToOid(capture.certId) !== pki.oids.x509Certificate) {
  644. var error = new Error(
  645. 'Unsupported certificate type, only X.509 supported.');
  646. error.oid = asn1.derToOid(capture.certId);
  647. throw error;
  648. }
  649. // true=produce cert hash
  650. var certAsn1 = asn1.fromDer(capture.cert, strict);
  651. try {
  652. bag.cert = pki.certificateFromAsn1(certAsn1, true);
  653. } catch(e) {
  654. // ignore unknown cert type, pass asn1 value
  655. bag.cert = null;
  656. bag.asn1 = certAsn1;
  657. }
  658. };
  659. break;
  660. default:
  661. var error = new Error('Unsupported PKCS#12 SafeBag type.');
  662. error.oid = bag.type;
  663. throw error;
  664. }
  665. /* Validate SafeBag value (i.e. CertBag, etc.) and capture data if needed. */
  666. if(validator !== undefined &&
  667. !asn1.validate(bagAsn1, validator, capture, errors)) {
  668. var error = new Error('Cannot read PKCS#12 ' + validator.name);
  669. error.errors = errors;
  670. throw error;
  671. }
  672. /* Call decoder function from above to store the results. */
  673. decoder();
  674. }
  675. return res;
  676. }
  677. /**
  678. * Decode PKCS#12 SET OF PKCS12Attribute into JavaScript object.
  679. *
  680. * @param attributes SET OF PKCS12Attribute (ASN.1 object).
  681. *
  682. * @return the decoded attributes.
  683. */
  684. function _decodeBagAttributes(attributes) {
  685. var decodedAttrs = {};
  686. if(attributes !== undefined) {
  687. for(var i = 0; i < attributes.length; ++i) {
  688. var capture = {};
  689. var errors = [];
  690. if(!asn1.validate(attributes[i], attributeValidator, capture, errors)) {
  691. var error = new Error('Cannot read PKCS#12 BagAttribute.');
  692. error.errors = errors;
  693. throw error;
  694. }
  695. var oid = asn1.derToOid(capture.oid);
  696. if(pki.oids[oid] === undefined) {
  697. // unsupported attribute type, ignore.
  698. continue;
  699. }
  700. decodedAttrs[pki.oids[oid]] = [];
  701. for(var j = 0; j < capture.values.length; ++j) {
  702. decodedAttrs[pki.oids[oid]].push(capture.values[j].value);
  703. }
  704. }
  705. }
  706. return decodedAttrs;
  707. }
  708. /**
  709. * Wraps a private key and certificate in a PKCS#12 PFX wrapper. If a
  710. * password is provided then the private key will be encrypted.
  711. *
  712. * An entire certificate chain may also be included. To do this, pass
  713. * an array for the "cert" parameter where the first certificate is
  714. * the one that is paired with the private key and each subsequent one
  715. * verifies the previous one. The certificates may be in PEM format or
  716. * have been already parsed by Forge.
  717. *
  718. * @todo implement password-based-encryption for the whole package
  719. *
  720. * @param key the private key.
  721. * @param cert the certificate (may be an array of certificates in order
  722. * to specify a certificate chain).
  723. * @param password the password to use, null for none.
  724. * @param options:
  725. * algorithm the encryption algorithm to use
  726. * ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.
  727. * count the iteration count to use.
  728. * saltSize the salt size to use.
  729. * useMac true to include a MAC, false not to, defaults to true.
  730. * localKeyId the local key ID to use, in hex.
  731. * friendlyName the friendly name to use.
  732. * generateLocalKeyId true to generate a random local key ID,
  733. * false not to, defaults to true.
  734. *
  735. * @return the PKCS#12 PFX ASN.1 object.
  736. */
  737. p12.toPkcs12Asn1 = function(key, cert, password, options) {
  738. // set default options
  739. options = options || {};
  740. options.saltSize = options.saltSize || 8;
  741. options.count = options.count || 2048;
  742. options.algorithm = options.algorithm || options.encAlgorithm || 'aes128';
  743. if(!('useMac' in options)) {
  744. options.useMac = true;
  745. }
  746. if(!('localKeyId' in options)) {
  747. options.localKeyId = null;
  748. }
  749. if(!('generateLocalKeyId' in options)) {
  750. options.generateLocalKeyId = true;
  751. }
  752. var localKeyId = options.localKeyId;
  753. var bagAttrs;
  754. if(localKeyId !== null) {
  755. localKeyId = forge.util.hexToBytes(localKeyId);
  756. } else if(options.generateLocalKeyId) {
  757. // use SHA-1 of paired cert, if available
  758. if(cert) {
  759. var pairedCert = forge.util.isArray(cert) ? cert[0] : cert;
  760. if(typeof pairedCert === 'string') {
  761. pairedCert = pki.certificateFromPem(pairedCert);
  762. }
  763. var sha1 = forge.md.sha1.create();
  764. sha1.update(asn1.toDer(pki.certificateToAsn1(pairedCert)).getBytes());
  765. localKeyId = sha1.digest().getBytes();
  766. } else {
  767. // FIXME: consider using SHA-1 of public key (which can be generated
  768. // from private key components), see: cert.generateSubjectKeyIdentifier
  769. // generate random bytes
  770. localKeyId = forge.random.getBytes(20);
  771. }
  772. }
  773. var attrs = [];
  774. if(localKeyId !== null) {
  775. attrs.push(
  776. // localKeyID
  777. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  778. // attrId
  779. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  780. asn1.oidToDer(pki.oids.localKeyId).getBytes()),
  781. // attrValues
  782. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
  783. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
  784. localKeyId)
  785. ])
  786. ]));
  787. }
  788. if('friendlyName' in options) {
  789. attrs.push(
  790. // friendlyName
  791. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  792. // attrId
  793. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  794. asn1.oidToDer(pki.oids.friendlyName).getBytes()),
  795. // attrValues
  796. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
  797. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BMPSTRING, false,
  798. options.friendlyName)
  799. ])
  800. ]));
  801. }
  802. if(attrs.length > 0) {
  803. bagAttrs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs);
  804. }
  805. // collect contents for AuthenticatedSafe
  806. var contents = [];
  807. // create safe bag(s) for certificate chain
  808. var chain = [];
  809. if(cert !== null) {
  810. if(forge.util.isArray(cert)) {
  811. chain = cert;
  812. } else {
  813. chain = [cert];
  814. }
  815. }
  816. var certSafeBags = [];
  817. for(var i = 0; i < chain.length; ++i) {
  818. // convert cert from PEM as necessary
  819. cert = chain[i];
  820. if(typeof cert === 'string') {
  821. cert = pki.certificateFromPem(cert);
  822. }
  823. // SafeBag
  824. var certBagAttrs = (i === 0) ? bagAttrs : undefined;
  825. var certAsn1 = pki.certificateToAsn1(cert);
  826. var certSafeBag =
  827. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  828. // bagId
  829. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  830. asn1.oidToDer(pki.oids.certBag).getBytes()),
  831. // bagValue
  832. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  833. // CertBag
  834. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  835. // certId
  836. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  837. asn1.oidToDer(pki.oids.x509Certificate).getBytes()),
  838. // certValue (x509Certificate)
  839. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  840. asn1.create(
  841. asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
  842. asn1.toDer(certAsn1).getBytes())
  843. ])])]),
  844. // bagAttributes (OPTIONAL)
  845. certBagAttrs
  846. ]);
  847. certSafeBags.push(certSafeBag);
  848. }
  849. if(certSafeBags.length > 0) {
  850. // SafeContents
  851. var certSafeContents = asn1.create(
  852. asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, certSafeBags);
  853. // ContentInfo
  854. var certCI =
  855. // PKCS#7 ContentInfo
  856. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  857. // contentType
  858. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  859. // OID for the content type is 'data'
  860. asn1.oidToDer(pki.oids.data).getBytes()),
  861. // content
  862. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  863. asn1.create(
  864. asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
  865. asn1.toDer(certSafeContents).getBytes())
  866. ])
  867. ]);
  868. contents.push(certCI);
  869. }
  870. // create safe contents for private key
  871. var keyBag = null;
  872. if(key !== null) {
  873. // SafeBag
  874. var pkAsn1 = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(key));
  875. if(password === null) {
  876. // no encryption
  877. keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  878. // bagId
  879. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  880. asn1.oidToDer(pki.oids.keyBag).getBytes()),
  881. // bagValue
  882. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  883. // PrivateKeyInfo
  884. pkAsn1
  885. ]),
  886. // bagAttributes (OPTIONAL)
  887. bagAttrs
  888. ]);
  889. } else {
  890. // encrypted PrivateKeyInfo
  891. keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  892. // bagId
  893. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  894. asn1.oidToDer(pki.oids.pkcs8ShroudedKeyBag).getBytes()),
  895. // bagValue
  896. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  897. // EncryptedPrivateKeyInfo
  898. pki.encryptPrivateKeyInfo(pkAsn1, password, options)
  899. ]),
  900. // bagAttributes (OPTIONAL)
  901. bagAttrs
  902. ]);
  903. }
  904. // SafeContents
  905. var keySafeContents =
  906. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [keyBag]);
  907. // ContentInfo
  908. var keyCI =
  909. // PKCS#7 ContentInfo
  910. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  911. // contentType
  912. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  913. // OID for the content type is 'data'
  914. asn1.oidToDer(pki.oids.data).getBytes()),
  915. // content
  916. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  917. asn1.create(
  918. asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
  919. asn1.toDer(keySafeContents).getBytes())
  920. ])
  921. ]);
  922. contents.push(keyCI);
  923. }
  924. // create AuthenticatedSafe by stringing together the contents
  925. var safe = asn1.create(
  926. asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, contents);
  927. var macData;
  928. if(options.useMac) {
  929. // MacData
  930. var sha1 = forge.md.sha1.create();
  931. var macSalt = new forge.util.ByteBuffer(
  932. forge.random.getBytes(options.saltSize));
  933. var count = options.count;
  934. // 160-bit key
  935. var key = p12.generateKey(password, macSalt, 3, count, 20);
  936. var mac = forge.hmac.create();
  937. mac.start(sha1, key);
  938. mac.update(asn1.toDer(safe).getBytes());
  939. var macValue = mac.getMac();
  940. macData = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  941. // mac DigestInfo
  942. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  943. // digestAlgorithm
  944. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  945. // algorithm = SHA-1
  946. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  947. asn1.oidToDer(pki.oids.sha1).getBytes()),
  948. // parameters = Null
  949. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
  950. ]),
  951. // digest
  952. asn1.create(
  953. asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING,
  954. false, macValue.getBytes())
  955. ]),
  956. // macSalt OCTET STRING
  957. asn1.create(
  958. asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, macSalt.getBytes()),
  959. // iterations INTEGER (XXX: Only support count < 65536)
  960. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
  961. asn1.integerToDer(count).getBytes()
  962. )
  963. ]);
  964. }
  965. // PFX
  966. return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  967. // version (3)
  968. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
  969. asn1.integerToDer(3).getBytes()),
  970. // PKCS#7 ContentInfo
  971. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
  972. // contentType
  973. asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
  974. // OID for the content type is 'data'
  975. asn1.oidToDer(pki.oids.data).getBytes()),
  976. // content
  977. asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
  978. asn1.create(
  979. asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
  980. asn1.toDer(safe).getBytes())
  981. ])
  982. ]),
  983. macData
  984. ]);
  985. };
  986. /**
  987. * Derives a PKCS#12 key.
  988. *
  989. * @param password the password to derive the key material from, null or
  990. * undefined for none.
  991. * @param salt the salt, as a ByteBuffer, to use.
  992. * @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).
  993. * @param iter the iteration count.
  994. * @param n the number of bytes to derive from the password.
  995. * @param md the message digest to use, defaults to SHA-1.
  996. *
  997. * @return a ByteBuffer with the bytes derived from the password.
  998. */
  999. p12.generateKey = forge.pbe.generatePkcs12Key;