pkcs12.js 33 KB

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