source: trip-planner-front/node_modules/node-forge/lib/pkcs7.js@ 1ad8e64

Last change on this file since 1ad8e64 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 38.8 KB
Line 
1/**
2 * Javascript implementation of PKCS#7 v1.5.
3 *
4 * @author Stefan Siegl
5 * @author Dave Longley
6 *
7 * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
8 * Copyright (c) 2012-2015 Digital Bazaar, Inc.
9 *
10 * Currently this implementation only supports ContentType of EnvelopedData,
11 * EncryptedData, or SignedData at the root level. The top level elements may
12 * contain only a ContentInfo of ContentType Data, i.e. plain data. Further
13 * nesting is not (yet) supported.
14 *
15 * The Forge validators for PKCS #7's ASN.1 structures are available from
16 * a separate file pkcs7asn1.js, since those are referenced from other
17 * PKCS standards like PKCS #12.
18 */
19var forge = require('./forge');
20require('./aes');
21require('./asn1');
22require('./des');
23require('./oids');
24require('./pem');
25require('./pkcs7asn1');
26require('./random');
27require('./util');
28require('./x509');
29
30// shortcut for ASN.1 API
31var asn1 = forge.asn1;
32
33// shortcut for PKCS#7 API
34var p7 = module.exports = forge.pkcs7 = forge.pkcs7 || {};
35
36/**
37 * Converts a PKCS#7 message from PEM format.
38 *
39 * @param pem the PEM-formatted PKCS#7 message.
40 *
41 * @return the PKCS#7 message.
42 */
43p7.messageFromPem = function(pem) {
44 var msg = forge.pem.decode(pem)[0];
45
46 if(msg.type !== 'PKCS7') {
47 var error = new Error('Could not convert PKCS#7 message from PEM; PEM ' +
48 'header type is not "PKCS#7".');
49 error.headerType = msg.type;
50 throw error;
51 }
52 if(msg.procType && msg.procType.type === 'ENCRYPTED') {
53 throw new Error('Could not convert PKCS#7 message from PEM; PEM is encrypted.');
54 }
55
56 // convert DER to ASN.1 object
57 var obj = asn1.fromDer(msg.body);
58
59 return p7.messageFromAsn1(obj);
60};
61
62/**
63 * Converts a PKCS#7 message to PEM format.
64 *
65 * @param msg The PKCS#7 message object
66 * @param maxline The maximum characters per line, defaults to 64.
67 *
68 * @return The PEM-formatted PKCS#7 message.
69 */
70p7.messageToPem = function(msg, maxline) {
71 // convert to ASN.1, then DER, then PEM-encode
72 var pemObj = {
73 type: 'PKCS7',
74 body: asn1.toDer(msg.toAsn1()).getBytes()
75 };
76 return forge.pem.encode(pemObj, {maxline: maxline});
77};
78
79/**
80 * Converts a PKCS#7 message from an ASN.1 object.
81 *
82 * @param obj the ASN.1 representation of a ContentInfo.
83 *
84 * @return the PKCS#7 message.
85 */
86p7.messageFromAsn1 = function(obj) {
87 // validate root level ContentInfo and capture data
88 var capture = {};
89 var errors = [];
90 if(!asn1.validate(obj, p7.asn1.contentInfoValidator, capture, errors)) {
91 var error = new Error('Cannot read PKCS#7 message. ' +
92 'ASN.1 object is not an PKCS#7 ContentInfo.');
93 error.errors = errors;
94 throw error;
95 }
96
97 var contentType = asn1.derToOid(capture.contentType);
98 var msg;
99
100 switch(contentType) {
101 case forge.pki.oids.envelopedData:
102 msg = p7.createEnvelopedData();
103 break;
104
105 case forge.pki.oids.encryptedData:
106 msg = p7.createEncryptedData();
107 break;
108
109 case forge.pki.oids.signedData:
110 msg = p7.createSignedData();
111 break;
112
113 default:
114 throw new Error('Cannot read PKCS#7 message. ContentType with OID ' +
115 contentType + ' is not (yet) supported.');
116 }
117
118 msg.fromAsn1(capture.content.value[0]);
119 return msg;
120};
121
122p7.createSignedData = function() {
123 var msg = null;
124 msg = {
125 type: forge.pki.oids.signedData,
126 version: 1,
127 certificates: [],
128 crls: [],
129 // TODO: add json-formatted signer stuff here?
130 signers: [],
131 // populated during sign()
132 digestAlgorithmIdentifiers: [],
133 contentInfo: null,
134 signerInfos: [],
135
136 fromAsn1: function(obj) {
137 // validate SignedData content block and capture data.
138 _fromAsn1(msg, obj, p7.asn1.signedDataValidator);
139 msg.certificates = [];
140 msg.crls = [];
141 msg.digestAlgorithmIdentifiers = [];
142 msg.contentInfo = null;
143 msg.signerInfos = [];
144
145 if(msg.rawCapture.certificates) {
146 var certs = msg.rawCapture.certificates.value;
147 for(var i = 0; i < certs.length; ++i) {
148 msg.certificates.push(forge.pki.certificateFromAsn1(certs[i]));
149 }
150 }
151
152 // TODO: parse crls
153 },
154
155 toAsn1: function() {
156 // degenerate case with no content
157 if(!msg.contentInfo) {
158 msg.sign();
159 }
160
161 var certs = [];
162 for(var i = 0; i < msg.certificates.length; ++i) {
163 certs.push(forge.pki.certificateToAsn1(msg.certificates[i]));
164 }
165
166 var crls = [];
167 // TODO: implement CRLs
168
169 // [0] SignedData
170 var signedData = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
171 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
172 // Version
173 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
174 asn1.integerToDer(msg.version).getBytes()),
175 // DigestAlgorithmIdentifiers
176 asn1.create(
177 asn1.Class.UNIVERSAL, asn1.Type.SET, true,
178 msg.digestAlgorithmIdentifiers),
179 // ContentInfo
180 msg.contentInfo
181 ])
182 ]);
183 if(certs.length > 0) {
184 // [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL
185 signedData.value[0].value.push(
186 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, certs));
187 }
188 if(crls.length > 0) {
189 // [1] IMPLICIT CertificateRevocationLists OPTIONAL
190 signedData.value[0].value.push(
191 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, crls));
192 }
193 // SignerInfos
194 signedData.value[0].value.push(
195 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
196 msg.signerInfos));
197
198 // ContentInfo
199 return asn1.create(
200 asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
201 // ContentType
202 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
203 asn1.oidToDer(msg.type).getBytes()),
204 // [0] SignedData
205 signedData
206 ]);
207 },
208
209 /**
210 * Add (another) entity to list of signers.
211 *
212 * Note: If authenticatedAttributes are provided, then, per RFC 2315,
213 * they must include at least two attributes: content type and
214 * message digest. The message digest attribute value will be
215 * auto-calculated during signing and will be ignored if provided.
216 *
217 * Here's an example of providing these two attributes:
218 *
219 * forge.pkcs7.createSignedData();
220 * p7.addSigner({
221 * issuer: cert.issuer.attributes,
222 * serialNumber: cert.serialNumber,
223 * key: privateKey,
224 * digestAlgorithm: forge.pki.oids.sha1,
225 * authenticatedAttributes: [{
226 * type: forge.pki.oids.contentType,
227 * value: forge.pki.oids.data
228 * }, {
229 * type: forge.pki.oids.messageDigest
230 * }]
231 * });
232 *
233 * TODO: Support [subjectKeyIdentifier] as signer's ID.
234 *
235 * @param signer the signer information:
236 * key the signer's private key.
237 * [certificate] a certificate containing the public key
238 * associated with the signer's private key; use this option as
239 * an alternative to specifying signer.issuer and
240 * signer.serialNumber.
241 * [issuer] the issuer attributes (eg: cert.issuer.attributes).
242 * [serialNumber] the signer's certificate's serial number in
243 * hexadecimal (eg: cert.serialNumber).
244 * [digestAlgorithm] the message digest OID, as a string, to use
245 * (eg: forge.pki.oids.sha1).
246 * [authenticatedAttributes] an optional array of attributes
247 * to also sign along with the content.
248 */
249 addSigner: function(signer) {
250 var issuer = signer.issuer;
251 var serialNumber = signer.serialNumber;
252 if(signer.certificate) {
253 var cert = signer.certificate;
254 if(typeof cert === 'string') {
255 cert = forge.pki.certificateFromPem(cert);
256 }
257 issuer = cert.issuer.attributes;
258 serialNumber = cert.serialNumber;
259 }
260 var key = signer.key;
261 if(!key) {
262 throw new Error(
263 'Could not add PKCS#7 signer; no private key specified.');
264 }
265 if(typeof key === 'string') {
266 key = forge.pki.privateKeyFromPem(key);
267 }
268
269 // ensure OID known for digest algorithm
270 var digestAlgorithm = signer.digestAlgorithm || forge.pki.oids.sha1;
271 switch(digestAlgorithm) {
272 case forge.pki.oids.sha1:
273 case forge.pki.oids.sha256:
274 case forge.pki.oids.sha384:
275 case forge.pki.oids.sha512:
276 case forge.pki.oids.md5:
277 break;
278 default:
279 throw new Error(
280 'Could not add PKCS#7 signer; unknown message digest algorithm: ' +
281 digestAlgorithm);
282 }
283
284 // if authenticatedAttributes is present, then the attributes
285 // must contain at least PKCS #9 content-type and message-digest
286 var authenticatedAttributes = signer.authenticatedAttributes || [];
287 if(authenticatedAttributes.length > 0) {
288 var contentType = false;
289 var messageDigest = false;
290 for(var i = 0; i < authenticatedAttributes.length; ++i) {
291 var attr = authenticatedAttributes[i];
292 if(!contentType && attr.type === forge.pki.oids.contentType) {
293 contentType = true;
294 if(messageDigest) {
295 break;
296 }
297 continue;
298 }
299 if(!messageDigest && attr.type === forge.pki.oids.messageDigest) {
300 messageDigest = true;
301 if(contentType) {
302 break;
303 }
304 continue;
305 }
306 }
307
308 if(!contentType || !messageDigest) {
309 throw new Error('Invalid signer.authenticatedAttributes. If ' +
310 'signer.authenticatedAttributes is specified, then it must ' +
311 'contain at least two attributes, PKCS #9 content-type and ' +
312 'PKCS #9 message-digest.');
313 }
314 }
315
316 msg.signers.push({
317 key: key,
318 version: 1,
319 issuer: issuer,
320 serialNumber: serialNumber,
321 digestAlgorithm: digestAlgorithm,
322 signatureAlgorithm: forge.pki.oids.rsaEncryption,
323 signature: null,
324 authenticatedAttributes: authenticatedAttributes,
325 unauthenticatedAttributes: []
326 });
327 },
328
329 /**
330 * Signs the content.
331 * @param options Options to apply when signing:
332 * [detached] boolean. If signing should be done in detached mode. Defaults to false.
333 */
334 sign: function(options) {
335 options = options || {};
336 // auto-generate content info
337 if(typeof msg.content !== 'object' || msg.contentInfo === null) {
338 // use Data ContentInfo
339 msg.contentInfo = asn1.create(
340 asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
341 // ContentType
342 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
343 asn1.oidToDer(forge.pki.oids.data).getBytes())
344 ]);
345
346 // add actual content, if present
347 if('content' in msg) {
348 var content;
349 if(msg.content instanceof forge.util.ByteBuffer) {
350 content = msg.content.bytes();
351 } else if(typeof msg.content === 'string') {
352 content = forge.util.encodeUtf8(msg.content);
353 }
354
355 if (options.detached) {
356 msg.detachedContent = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, content);
357 } else {
358 msg.contentInfo.value.push(
359 // [0] EXPLICIT content
360 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
361 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
362 content)
363 ]));
364 }
365 }
366 }
367
368 // no signers, return early (degenerate case for certificate container)
369 if(msg.signers.length === 0) {
370 return;
371 }
372
373 // generate digest algorithm identifiers
374 var mds = addDigestAlgorithmIds();
375
376 // generate signerInfos
377 addSignerInfos(mds);
378 },
379
380 verify: function() {
381 throw new Error('PKCS#7 signature verification not yet implemented.');
382 },
383
384 /**
385 * Add a certificate.
386 *
387 * @param cert the certificate to add.
388 */
389 addCertificate: function(cert) {
390 // convert from PEM
391 if(typeof cert === 'string') {
392 cert = forge.pki.certificateFromPem(cert);
393 }
394 msg.certificates.push(cert);
395 },
396
397 /**
398 * Add a certificate revokation list.
399 *
400 * @param crl the certificate revokation list to add.
401 */
402 addCertificateRevokationList: function(crl) {
403 throw new Error('PKCS#7 CRL support not yet implemented.');
404 }
405 };
406 return msg;
407
408 function addDigestAlgorithmIds() {
409 var mds = {};
410
411 for(var i = 0; i < msg.signers.length; ++i) {
412 var signer = msg.signers[i];
413 var oid = signer.digestAlgorithm;
414 if(!(oid in mds)) {
415 // content digest
416 mds[oid] = forge.md[forge.pki.oids[oid]].create();
417 }
418 if(signer.authenticatedAttributes.length === 0) {
419 // no custom attributes to digest; use content message digest
420 signer.md = mds[oid];
421 } else {
422 // custom attributes to be digested; use own message digest
423 // TODO: optimize to just copy message digest state if that
424 // feature is ever supported with message digests
425 signer.md = forge.md[forge.pki.oids[oid]].create();
426 }
427 }
428
429 // add unique digest algorithm identifiers
430 msg.digestAlgorithmIdentifiers = [];
431 for(var oid in mds) {
432 msg.digestAlgorithmIdentifiers.push(
433 // AlgorithmIdentifier
434 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
435 // algorithm
436 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
437 asn1.oidToDer(oid).getBytes()),
438 // parameters (null)
439 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
440 ]));
441 }
442
443 return mds;
444 }
445
446 function addSignerInfos(mds) {
447 var content;
448
449 if (msg.detachedContent) {
450 // Signature has been made in detached mode.
451 content = msg.detachedContent;
452 } else {
453 // Note: ContentInfo is a SEQUENCE with 2 values, second value is
454 // the content field and is optional for a ContentInfo but required here
455 // since signers are present
456 // get ContentInfo content
457 content = msg.contentInfo.value[1];
458 // skip [0] EXPLICIT content wrapper
459 content = content.value[0];
460 }
461
462 if(!content) {
463 throw new Error(
464 'Could not sign PKCS#7 message; there is no content to sign.');
465 }
466
467 // get ContentInfo content type
468 var contentType = asn1.derToOid(msg.contentInfo.value[0].value);
469
470 // serialize content
471 var bytes = asn1.toDer(content);
472
473 // skip identifier and length per RFC 2315 9.3
474 // skip identifier (1 byte)
475 bytes.getByte();
476 // read and discard length bytes
477 asn1.getBerValueLength(bytes);
478 bytes = bytes.getBytes();
479
480 // digest content DER value bytes
481 for(var oid in mds) {
482 mds[oid].start().update(bytes);
483 }
484
485 // sign content
486 var signingTime = new Date();
487 for(var i = 0; i < msg.signers.length; ++i) {
488 var signer = msg.signers[i];
489
490 if(signer.authenticatedAttributes.length === 0) {
491 // if ContentInfo content type is not "Data", then
492 // authenticatedAttributes must be present per RFC 2315
493 if(contentType !== forge.pki.oids.data) {
494 throw new Error(
495 'Invalid signer; authenticatedAttributes must be present ' +
496 'when the ContentInfo content type is not PKCS#7 Data.');
497 }
498 } else {
499 // process authenticated attributes
500 // [0] IMPLICIT
501 signer.authenticatedAttributesAsn1 = asn1.create(
502 asn1.Class.CONTEXT_SPECIFIC, 0, true, []);
503
504 // per RFC 2315, attributes are to be digested using a SET container
505 // not the above [0] IMPLICIT container
506 var attrsAsn1 = asn1.create(
507 asn1.Class.UNIVERSAL, asn1.Type.SET, true, []);
508
509 for(var ai = 0; ai < signer.authenticatedAttributes.length; ++ai) {
510 var attr = signer.authenticatedAttributes[ai];
511 if(attr.type === forge.pki.oids.messageDigest) {
512 // use content message digest as value
513 attr.value = mds[signer.digestAlgorithm].digest();
514 } else if(attr.type === forge.pki.oids.signingTime) {
515 // auto-populate signing time if not already set
516 if(!attr.value) {
517 attr.value = signingTime;
518 }
519 }
520
521 // convert to ASN.1 and push onto Attributes SET (for signing) and
522 // onto authenticatedAttributesAsn1 to complete SignedData ASN.1
523 // TODO: optimize away duplication
524 attrsAsn1.value.push(_attributeToAsn1(attr));
525 signer.authenticatedAttributesAsn1.value.push(_attributeToAsn1(attr));
526 }
527
528 // DER-serialize and digest SET OF attributes only
529 bytes = asn1.toDer(attrsAsn1).getBytes();
530 signer.md.start().update(bytes);
531 }
532
533 // sign digest
534 signer.signature = signer.key.sign(signer.md, 'RSASSA-PKCS1-V1_5');
535 }
536
537 // add signer info
538 msg.signerInfos = _signersToAsn1(msg.signers);
539 }
540};
541
542/**
543 * Creates an empty PKCS#7 message of type EncryptedData.
544 *
545 * @return the message.
546 */
547p7.createEncryptedData = function() {
548 var msg = null;
549 msg = {
550 type: forge.pki.oids.encryptedData,
551 version: 0,
552 encryptedContent: {
553 algorithm: forge.pki.oids['aes256-CBC']
554 },
555
556 /**
557 * Reads an EncryptedData content block (in ASN.1 format)
558 *
559 * @param obj The ASN.1 representation of the EncryptedData content block
560 */
561 fromAsn1: function(obj) {
562 // Validate EncryptedData content block and capture data.
563 _fromAsn1(msg, obj, p7.asn1.encryptedDataValidator);
564 },
565
566 /**
567 * Decrypt encrypted content
568 *
569 * @param key The (symmetric) key as a byte buffer
570 */
571 decrypt: function(key) {
572 if(key !== undefined) {
573 msg.encryptedContent.key = key;
574 }
575 _decryptContent(msg);
576 }
577 };
578 return msg;
579};
580
581/**
582 * Creates an empty PKCS#7 message of type EnvelopedData.
583 *
584 * @return the message.
585 */
586p7.createEnvelopedData = function() {
587 var msg = null;
588 msg = {
589 type: forge.pki.oids.envelopedData,
590 version: 0,
591 recipients: [],
592 encryptedContent: {
593 algorithm: forge.pki.oids['aes256-CBC']
594 },
595
596 /**
597 * Reads an EnvelopedData content block (in ASN.1 format)
598 *
599 * @param obj the ASN.1 representation of the EnvelopedData content block.
600 */
601 fromAsn1: function(obj) {
602 // validate EnvelopedData content block and capture data
603 var capture = _fromAsn1(msg, obj, p7.asn1.envelopedDataValidator);
604 msg.recipients = _recipientsFromAsn1(capture.recipientInfos.value);
605 },
606
607 toAsn1: function() {
608 // ContentInfo
609 return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
610 // ContentType
611 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
612 asn1.oidToDer(msg.type).getBytes()),
613 // [0] EnvelopedData
614 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
615 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
616 // Version
617 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
618 asn1.integerToDer(msg.version).getBytes()),
619 // RecipientInfos
620 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
621 _recipientsToAsn1(msg.recipients)),
622 // EncryptedContentInfo
623 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true,
624 _encryptedContentToAsn1(msg.encryptedContent))
625 ])
626 ])
627 ]);
628 },
629
630 /**
631 * Find recipient by X.509 certificate's issuer.
632 *
633 * @param cert the certificate with the issuer to look for.
634 *
635 * @return the recipient object.
636 */
637 findRecipient: function(cert) {
638 var sAttr = cert.issuer.attributes;
639
640 for(var i = 0; i < msg.recipients.length; ++i) {
641 var r = msg.recipients[i];
642 var rAttr = r.issuer;
643
644 if(r.serialNumber !== cert.serialNumber) {
645 continue;
646 }
647
648 if(rAttr.length !== sAttr.length) {
649 continue;
650 }
651
652 var match = true;
653 for(var j = 0; j < sAttr.length; ++j) {
654 if(rAttr[j].type !== sAttr[j].type ||
655 rAttr[j].value !== sAttr[j].value) {
656 match = false;
657 break;
658 }
659 }
660
661 if(match) {
662 return r;
663 }
664 }
665
666 return null;
667 },
668
669 /**
670 * Decrypt enveloped content
671 *
672 * @param recipient The recipient object related to the private key
673 * @param privKey The (RSA) private key object
674 */
675 decrypt: function(recipient, privKey) {
676 if(msg.encryptedContent.key === undefined && recipient !== undefined &&
677 privKey !== undefined) {
678 switch(recipient.encryptedContent.algorithm) {
679 case forge.pki.oids.rsaEncryption:
680 case forge.pki.oids.desCBC:
681 var key = privKey.decrypt(recipient.encryptedContent.content);
682 msg.encryptedContent.key = forge.util.createBuffer(key);
683 break;
684
685 default:
686 throw new Error('Unsupported asymmetric cipher, ' +
687 'OID ' + recipient.encryptedContent.algorithm);
688 }
689 }
690
691 _decryptContent(msg);
692 },
693
694 /**
695 * Add (another) entity to list of recipients.
696 *
697 * @param cert The certificate of the entity to add.
698 */
699 addRecipient: function(cert) {
700 msg.recipients.push({
701 version: 0,
702 issuer: cert.issuer.attributes,
703 serialNumber: cert.serialNumber,
704 encryptedContent: {
705 // We simply assume rsaEncryption here, since forge.pki only
706 // supports RSA so far. If the PKI module supports other
707 // ciphers one day, we need to modify this one as well.
708 algorithm: forge.pki.oids.rsaEncryption,
709 key: cert.publicKey
710 }
711 });
712 },
713
714 /**
715 * Encrypt enveloped content.
716 *
717 * This function supports two optional arguments, cipher and key, which
718 * can be used to influence symmetric encryption. Unless cipher is
719 * provided, the cipher specified in encryptedContent.algorithm is used
720 * (defaults to AES-256-CBC). If no key is provided, encryptedContent.key
721 * is (re-)used. If that one's not set, a random key will be generated
722 * automatically.
723 *
724 * @param [key] The key to be used for symmetric encryption.
725 * @param [cipher] The OID of the symmetric cipher to use.
726 */
727 encrypt: function(key, cipher) {
728 // Part 1: Symmetric encryption
729 if(msg.encryptedContent.content === undefined) {
730 cipher = cipher || msg.encryptedContent.algorithm;
731 key = key || msg.encryptedContent.key;
732
733 var keyLen, ivLen, ciphFn;
734 switch(cipher) {
735 case forge.pki.oids['aes128-CBC']:
736 keyLen = 16;
737 ivLen = 16;
738 ciphFn = forge.aes.createEncryptionCipher;
739 break;
740
741 case forge.pki.oids['aes192-CBC']:
742 keyLen = 24;
743 ivLen = 16;
744 ciphFn = forge.aes.createEncryptionCipher;
745 break;
746
747 case forge.pki.oids['aes256-CBC']:
748 keyLen = 32;
749 ivLen = 16;
750 ciphFn = forge.aes.createEncryptionCipher;
751 break;
752
753 case forge.pki.oids['des-EDE3-CBC']:
754 keyLen = 24;
755 ivLen = 8;
756 ciphFn = forge.des.createEncryptionCipher;
757 break;
758
759 default:
760 throw new Error('Unsupported symmetric cipher, OID ' + cipher);
761 }
762
763 if(key === undefined) {
764 key = forge.util.createBuffer(forge.random.getBytes(keyLen));
765 } else if(key.length() != keyLen) {
766 throw new Error('Symmetric key has wrong length; ' +
767 'got ' + key.length() + ' bytes, expected ' + keyLen + '.');
768 }
769
770 // Keep a copy of the key & IV in the object, so the caller can
771 // use it for whatever reason.
772 msg.encryptedContent.algorithm = cipher;
773 msg.encryptedContent.key = key;
774 msg.encryptedContent.parameter = forge.util.createBuffer(
775 forge.random.getBytes(ivLen));
776
777 var ciph = ciphFn(key);
778 ciph.start(msg.encryptedContent.parameter.copy());
779 ciph.update(msg.content);
780
781 // The finish function does PKCS#7 padding by default, therefore
782 // no action required by us.
783 if(!ciph.finish()) {
784 throw new Error('Symmetric encryption failed.');
785 }
786
787 msg.encryptedContent.content = ciph.output;
788 }
789
790 // Part 2: asymmetric encryption for each recipient
791 for(var i = 0; i < msg.recipients.length; ++i) {
792 var recipient = msg.recipients[i];
793
794 // Nothing to do, encryption already done.
795 if(recipient.encryptedContent.content !== undefined) {
796 continue;
797 }
798
799 switch(recipient.encryptedContent.algorithm) {
800 case forge.pki.oids.rsaEncryption:
801 recipient.encryptedContent.content =
802 recipient.encryptedContent.key.encrypt(
803 msg.encryptedContent.key.data);
804 break;
805
806 default:
807 throw new Error('Unsupported asymmetric cipher, OID ' +
808 recipient.encryptedContent.algorithm);
809 }
810 }
811 }
812 };
813 return msg;
814};
815
816/**
817 * Converts a single recipient from an ASN.1 object.
818 *
819 * @param obj the ASN.1 RecipientInfo.
820 *
821 * @return the recipient object.
822 */
823function _recipientFromAsn1(obj) {
824 // validate EnvelopedData content block and capture data
825 var capture = {};
826 var errors = [];
827 if(!asn1.validate(obj, p7.asn1.recipientInfoValidator, capture, errors)) {
828 var error = new Error('Cannot read PKCS#7 RecipientInfo. ' +
829 'ASN.1 object is not an PKCS#7 RecipientInfo.');
830 error.errors = errors;
831 throw error;
832 }
833
834 return {
835 version: capture.version.charCodeAt(0),
836 issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
837 serialNumber: forge.util.createBuffer(capture.serial).toHex(),
838 encryptedContent: {
839 algorithm: asn1.derToOid(capture.encAlgorithm),
840 parameter: capture.encParameter.value,
841 content: capture.encKey
842 }
843 };
844}
845
846/**
847 * Converts a single recipient object to an ASN.1 object.
848 *
849 * @param obj the recipient object.
850 *
851 * @return the ASN.1 RecipientInfo.
852 */
853function _recipientToAsn1(obj) {
854 return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
855 // Version
856 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
857 asn1.integerToDer(obj.version).getBytes()),
858 // IssuerAndSerialNumber
859 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
860 // Name
861 forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),
862 // Serial
863 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
864 forge.util.hexToBytes(obj.serialNumber))
865 ]),
866 // KeyEncryptionAlgorithmIdentifier
867 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
868 // Algorithm
869 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
870 asn1.oidToDer(obj.encryptedContent.algorithm).getBytes()),
871 // Parameter, force NULL, only RSA supported for now.
872 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
873 ]),
874 // EncryptedKey
875 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
876 obj.encryptedContent.content)
877 ]);
878}
879
880/**
881 * Map a set of RecipientInfo ASN.1 objects to recipient objects.
882 *
883 * @param infos an array of ASN.1 representations RecipientInfo (i.e. SET OF).
884 *
885 * @return an array of recipient objects.
886 */
887function _recipientsFromAsn1(infos) {
888 var ret = [];
889 for(var i = 0; i < infos.length; ++i) {
890 ret.push(_recipientFromAsn1(infos[i]));
891 }
892 return ret;
893}
894
895/**
896 * Map an array of recipient objects to ASN.1 RecipientInfo objects.
897 *
898 * @param recipients an array of recipientInfo objects.
899 *
900 * @return an array of ASN.1 RecipientInfos.
901 */
902function _recipientsToAsn1(recipients) {
903 var ret = [];
904 for(var i = 0; i < recipients.length; ++i) {
905 ret.push(_recipientToAsn1(recipients[i]));
906 }
907 return ret;
908}
909
910/**
911 * Converts a single signer from an ASN.1 object.
912 *
913 * @param obj the ASN.1 representation of a SignerInfo.
914 *
915 * @return the signer object.
916 */
917function _signerFromAsn1(obj) {
918 // validate EnvelopedData content block and capture data
919 var capture = {};
920 var errors = [];
921 if(!asn1.validate(obj, p7.asn1.signerInfoValidator, capture, errors)) {
922 var error = new Error('Cannot read PKCS#7 SignerInfo. ' +
923 'ASN.1 object is not an PKCS#7 SignerInfo.');
924 error.errors = errors;
925 throw error;
926 }
927
928 var rval = {
929 version: capture.version.charCodeAt(0),
930 issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
931 serialNumber: forge.util.createBuffer(capture.serial).toHex(),
932 digestAlgorithm: asn1.derToOid(capture.digestAlgorithm),
933 signatureAlgorithm: asn1.derToOid(capture.signatureAlgorithm),
934 signature: capture.signature,
935 authenticatedAttributes: [],
936 unauthenticatedAttributes: []
937 };
938
939 // TODO: convert attributes
940 var authenticatedAttributes = capture.authenticatedAttributes || [];
941 var unauthenticatedAttributes = capture.unauthenticatedAttributes || [];
942
943 return rval;
944}
945
946/**
947 * Converts a single signerInfo object to an ASN.1 object.
948 *
949 * @param obj the signerInfo object.
950 *
951 * @return the ASN.1 representation of a SignerInfo.
952 */
953function _signerToAsn1(obj) {
954 // SignerInfo
955 var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
956 // version
957 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
958 asn1.integerToDer(obj.version).getBytes()),
959 // issuerAndSerialNumber
960 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
961 // name
962 forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),
963 // serial
964 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
965 forge.util.hexToBytes(obj.serialNumber))
966 ]),
967 // digestAlgorithm
968 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
969 // algorithm
970 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
971 asn1.oidToDer(obj.digestAlgorithm).getBytes()),
972 // parameters (null)
973 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
974 ])
975 ]);
976
977 // authenticatedAttributes (OPTIONAL)
978 if(obj.authenticatedAttributesAsn1) {
979 // add ASN.1 previously generated during signing
980 rval.value.push(obj.authenticatedAttributesAsn1);
981 }
982
983 // digestEncryptionAlgorithm
984 rval.value.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
985 // algorithm
986 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
987 asn1.oidToDer(obj.signatureAlgorithm).getBytes()),
988 // parameters (null)
989 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
990 ]));
991
992 // encryptedDigest
993 rval.value.push(asn1.create(
994 asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, obj.signature));
995
996 // unauthenticatedAttributes (OPTIONAL)
997 if(obj.unauthenticatedAttributes.length > 0) {
998 // [1] IMPLICIT
999 var attrsAsn1 = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, []);
1000 for(var i = 0; i < obj.unauthenticatedAttributes.length; ++i) {
1001 var attr = obj.unauthenticatedAttributes[i];
1002 attrsAsn1.values.push(_attributeToAsn1(attr));
1003 }
1004 rval.value.push(attrsAsn1);
1005 }
1006
1007 return rval;
1008}
1009
1010/**
1011 * Map a set of SignerInfo ASN.1 objects to an array of signer objects.
1012 *
1013 * @param signerInfoAsn1s an array of ASN.1 SignerInfos (i.e. SET OF).
1014 *
1015 * @return an array of signers objects.
1016 */
1017function _signersFromAsn1(signerInfoAsn1s) {
1018 var ret = [];
1019 for(var i = 0; i < signerInfoAsn1s.length; ++i) {
1020 ret.push(_signerFromAsn1(signerInfoAsn1s[i]));
1021 }
1022 return ret;
1023}
1024
1025/**
1026 * Map an array of signer objects to ASN.1 objects.
1027 *
1028 * @param signers an array of signer objects.
1029 *
1030 * @return an array of ASN.1 SignerInfos.
1031 */
1032function _signersToAsn1(signers) {
1033 var ret = [];
1034 for(var i = 0; i < signers.length; ++i) {
1035 ret.push(_signerToAsn1(signers[i]));
1036 }
1037 return ret;
1038}
1039
1040/**
1041 * Convert an attribute object to an ASN.1 Attribute.
1042 *
1043 * @param attr the attribute object.
1044 *
1045 * @return the ASN.1 Attribute.
1046 */
1047function _attributeToAsn1(attr) {
1048 var value;
1049
1050 // TODO: generalize to support more attributes
1051 if(attr.type === forge.pki.oids.contentType) {
1052 value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1053 asn1.oidToDer(attr.value).getBytes());
1054 } else if(attr.type === forge.pki.oids.messageDigest) {
1055 value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
1056 attr.value.bytes());
1057 } else if(attr.type === forge.pki.oids.signingTime) {
1058 /* Note per RFC 2985: Dates between 1 January 1950 and 31 December 2049
1059 (inclusive) MUST be encoded as UTCTime. Any dates with year values
1060 before 1950 or after 2049 MUST be encoded as GeneralizedTime. [Further,]
1061 UTCTime values MUST be expressed in Greenwich Mean Time (Zulu) and MUST
1062 include seconds (i.e., times are YYMMDDHHMMSSZ), even where the
1063 number of seconds is zero. Midnight (GMT) must be represented as
1064 "YYMMDD000000Z". */
1065 // TODO: make these module-level constants
1066 var jan_1_1950 = new Date('1950-01-01T00:00:00Z');
1067 var jan_1_2050 = new Date('2050-01-01T00:00:00Z');
1068 var date = attr.value;
1069 if(typeof date === 'string') {
1070 // try to parse date
1071 var timestamp = Date.parse(date);
1072 if(!isNaN(timestamp)) {
1073 date = new Date(timestamp);
1074 } else if(date.length === 13) {
1075 // YYMMDDHHMMSSZ (13 chars for UTCTime)
1076 date = asn1.utcTimeToDate(date);
1077 } else {
1078 // assume generalized time
1079 date = asn1.generalizedTimeToDate(date);
1080 }
1081 }
1082
1083 if(date >= jan_1_1950 && date < jan_1_2050) {
1084 value = asn1.create(
1085 asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,
1086 asn1.dateToUtcTime(date));
1087 } else {
1088 value = asn1.create(
1089 asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false,
1090 asn1.dateToGeneralizedTime(date));
1091 }
1092 }
1093
1094 // TODO: expose as common API call
1095 // create a RelativeDistinguishedName set
1096 // each value in the set is an AttributeTypeAndValue first
1097 // containing the type (an OID) and second the value
1098 return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1099 // AttributeType
1100 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1101 asn1.oidToDer(attr.type).getBytes()),
1102 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
1103 // AttributeValue
1104 value
1105 ])
1106 ]);
1107}
1108
1109/**
1110 * Map messages encrypted content to ASN.1 objects.
1111 *
1112 * @param ec The encryptedContent object of the message.
1113 *
1114 * @return ASN.1 representation of the encryptedContent object (SEQUENCE).
1115 */
1116function _encryptedContentToAsn1(ec) {
1117 return [
1118 // ContentType, always Data for the moment
1119 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1120 asn1.oidToDer(forge.pki.oids.data).getBytes()),
1121 // ContentEncryptionAlgorithmIdentifier
1122 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1123 // Algorithm
1124 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1125 asn1.oidToDer(ec.algorithm).getBytes()),
1126 // Parameters (IV)
1127 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
1128 ec.parameter.getBytes())
1129 ]),
1130 // [0] EncryptedContent
1131 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
1132 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
1133 ec.content.getBytes())
1134 ])
1135 ];
1136}
1137
1138/**
1139 * Reads the "common part" of an PKCS#7 content block (in ASN.1 format)
1140 *
1141 * This function reads the "common part" of the PKCS#7 content blocks
1142 * EncryptedData and EnvelopedData, i.e. version number and symmetrically
1143 * encrypted content block.
1144 *
1145 * The result of the ASN.1 validate and capture process is returned
1146 * to allow the caller to extract further data, e.g. the list of recipients
1147 * in case of a EnvelopedData object.
1148 *
1149 * @param msg the PKCS#7 object to read the data to.
1150 * @param obj the ASN.1 representation of the content block.
1151 * @param validator the ASN.1 structure validator object to use.
1152 *
1153 * @return the value map captured by validator object.
1154 */
1155function _fromAsn1(msg, obj, validator) {
1156 var capture = {};
1157 var errors = [];
1158 if(!asn1.validate(obj, validator, capture, errors)) {
1159 var error = new Error('Cannot read PKCS#7 message. ' +
1160 'ASN.1 object is not a supported PKCS#7 message.');
1161 error.errors = error;
1162 throw error;
1163 }
1164
1165 // Check contentType, so far we only support (raw) Data.
1166 var contentType = asn1.derToOid(capture.contentType);
1167 if(contentType !== forge.pki.oids.data) {
1168 throw new Error('Unsupported PKCS#7 message. ' +
1169 'Only wrapped ContentType Data supported.');
1170 }
1171
1172 if(capture.encryptedContent) {
1173 var content = '';
1174 if(forge.util.isArray(capture.encryptedContent)) {
1175 for(var i = 0; i < capture.encryptedContent.length; ++i) {
1176 if(capture.encryptedContent[i].type !== asn1.Type.OCTETSTRING) {
1177 throw new Error('Malformed PKCS#7 message, expecting encrypted ' +
1178 'content constructed of only OCTET STRING objects.');
1179 }
1180 content += capture.encryptedContent[i].value;
1181 }
1182 } else {
1183 content = capture.encryptedContent;
1184 }
1185 msg.encryptedContent = {
1186 algorithm: asn1.derToOid(capture.encAlgorithm),
1187 parameter: forge.util.createBuffer(capture.encParameter.value),
1188 content: forge.util.createBuffer(content)
1189 };
1190 }
1191
1192 if(capture.content) {
1193 var content = '';
1194 if(forge.util.isArray(capture.content)) {
1195 for(var i = 0; i < capture.content.length; ++i) {
1196 if(capture.content[i].type !== asn1.Type.OCTETSTRING) {
1197 throw new Error('Malformed PKCS#7 message, expecting ' +
1198 'content constructed of only OCTET STRING objects.');
1199 }
1200 content += capture.content[i].value;
1201 }
1202 } else {
1203 content = capture.content;
1204 }
1205 msg.content = forge.util.createBuffer(content);
1206 }
1207
1208 msg.version = capture.version.charCodeAt(0);
1209 msg.rawCapture = capture;
1210
1211 return capture;
1212}
1213
1214/**
1215 * Decrypt the symmetrically encrypted content block of the PKCS#7 message.
1216 *
1217 * Decryption is skipped in case the PKCS#7 message object already has a
1218 * (decrypted) content attribute. The algorithm, key and cipher parameters
1219 * (probably the iv) are taken from the encryptedContent attribute of the
1220 * message object.
1221 *
1222 * @param The PKCS#7 message object.
1223 */
1224function _decryptContent(msg) {
1225 if(msg.encryptedContent.key === undefined) {
1226 throw new Error('Symmetric key not available.');
1227 }
1228
1229 if(msg.content === undefined) {
1230 var ciph;
1231
1232 switch(msg.encryptedContent.algorithm) {
1233 case forge.pki.oids['aes128-CBC']:
1234 case forge.pki.oids['aes192-CBC']:
1235 case forge.pki.oids['aes256-CBC']:
1236 ciph = forge.aes.createDecryptionCipher(msg.encryptedContent.key);
1237 break;
1238
1239 case forge.pki.oids['desCBC']:
1240 case forge.pki.oids['des-EDE3-CBC']:
1241 ciph = forge.des.createDecryptionCipher(msg.encryptedContent.key);
1242 break;
1243
1244 default:
1245 throw new Error('Unsupported symmetric cipher, OID ' +
1246 msg.encryptedContent.algorithm);
1247 }
1248 ciph.start(msg.encryptedContent.parameter);
1249 ciph.update(msg.encryptedContent.content);
1250
1251 if(!ciph.finish()) {
1252 throw new Error('Symmetric decryption failed.');
1253 }
1254
1255 msg.content = ciph.output;
1256 }
1257}
Note: See TracBrowser for help on using the repository browser.