2 * Javascript implementation of PKCS#7 v1.5.
7 * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
8 * Copyright (c) 2012-2015 Digital Bazaar, Inc.
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.
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.
21 forge = require('../node-forge/lib/forge');
22 require('../node-forge/lib/aes');
23 require('../node-forge/lib/asn1');
24 require('../node-forge/lib/des');
25 require('../node-forge/lib/oids');
26 require('../node-forge/lib/pem');
27 require('../node-forge/lib/pkcs7asn1');
28 require('../node-forge/lib/random');
29 require('../node-forge/lib/util');
30 require('../node-forge/lib/x509'); f
34 forge = require('./node_modules/node-forge/lib/forge');
35 require('./node_modules/node-forge/lib/aes');
36 require('./node_modules/node-forge/lib/asn1');
37 require('./node_modules/node-forge/lib/des');
38 require('./node_modules/node-forge/lib/oids');
39 require('./node_modules/node-forge/lib/pem');
40 require('./node_modules/node-forge/lib/pkcs7asn1');
41 require('./node_modules/node-forge/lib/random');
42 require('./node_modules/node-forge/lib/util');
43 require('./node_modules/node-forge/lib/x509');
46// shortcut for ASN.1 API
49// shortcut for PKCS#7 API
50var p7 = module.exports = forge.pkcs7 = forge.pkcs7 || {};
53 * Converts a PKCS#7 message from PEM format.
55 * @param pem the PEM-formatted PKCS#7 message.
57 * @return the PKCS#7 message.
59p7.messageFromPem = function(pem) {
60 var msg = forge.pem.decode(pem)[0];
62 if(msg.type !== 'PKCS7') {
63 var error = new Error('Could not convert PKCS#7 message from PEM; PEM ' +
64 'header type is not "PKCS#7".');
65 error.headerType = msg.type;
68 if(msg.procType && msg.procType.type === 'ENCRYPTED') {
69 throw new Error('Could not convert PKCS#7 message from PEM; PEM is encrypted.');
72 // convert DER to ASN.1 object
73 var obj = asn1.fromDer(msg.body);
75 return p7.messageFromAsn1(obj);
79 * Converts a PKCS#7 message to PEM format.
81 * @param msg The PKCS#7 message object
82 * @param maxline The maximum characters per line, defaults to 64.
84 * @return The PEM-formatted PKCS#7 message.
86p7.messageToPem = function(msg, maxline) {
87 // convert to ASN.1, then DER, then PEM-encode
90 body: asn1.toDer(msg.toAsn1()).getBytes()
92 return forge.pem.encode(pemObj, {maxline: maxline});
96 * Converts a PKCS#7 message from an ASN.1 object.
98 * @param obj the ASN.1 representation of a ContentInfo.
100 * @return the PKCS#7 message.
102p7.messageFromAsn1 = function(obj) {
103 // validate root level ContentInfo and capture data
106 if(!asn1.validate(obj, p7.asn1.contentInfoValidator, capture, errors)) {
107 var error = new Error('Cannot read PKCS#7 message. ' +
108 'ASN.1 object is not an PKCS#7 ContentInfo.');
109 error.errors = errors;
113 var contentType = asn1.derToOid(capture.contentType);
116 switch(contentType) {
117 case forge.pki.oids.envelopedData:
118 msg = p7.createEnvelopedData();
121 case forge.pki.oids.encryptedData:
122 msg = p7.createEncryptedData();
125 case forge.pki.oids.signedData:
126 msg = p7.createSignedData();
130 throw new Error('Cannot read PKCS#7 message. ContentType with OID ' +
131 contentType + ' is not (yet) supported.');
134 msg.fromAsn1(capture.content.value[0]);
138p7.createSignedData = function() {
141 type: forge.pki.oids.signedData,
145 // TODO: add json-formatted signer stuff here?
147 // populated during sign()
148 digestAlgorithmIdentifiers: [],
152 fromAsn1: function(obj) {
153 // validate SignedData content block and capture data.
154 _fromAsn1(msg, obj, p7.asn1.signedDataValidator);
155 msg.certificates = [];
157 msg.digestAlgorithmIdentifiers = [];
158 msg.contentInfo = null;
159 msg.signerInfos = [];
161 if(msg.rawCapture.certificates) {
162 var certs = msg.rawCapture.certificates.value;
163 for(var i = 0; i < certs.length; ++i) {
164 msg.certificates.push(forge.pki.certificateFromAsn1(certs[i]));
172 // degenerate case with no content
173 if(!msg.contentInfo) {
178 for(var i = 0; i < msg.certificates.length; ++i) {
179 certs.push(forge.pki.certificateToAsn1(msg.certificates[i]));
183 // TODO: implement CRLs
186 var signedData = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
187 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
189 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
190 asn1.integerToDer(msg.version).getBytes()),
191 // DigestAlgorithmIdentifiers
193 asn1.Class.UNIVERSAL, asn1.Type.SET, true,
194 msg.digestAlgorithmIdentifiers),
199 if(certs.length > 0) {
200 // [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL
201 signedData.value[0].value.push(
202 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, certs));
204 if(crls.length > 0) {
205 // [1] IMPLICIT CertificateRevocationLists OPTIONAL
206 signedData.value[0].value.push(
207 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, crls));
210 signedData.value[0].value.push(
211 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
216 asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
218 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
219 asn1.oidToDer(msg.type).getBytes()),
226 * Add (another) entity to list of signers.
228 * Note: If authenticatedAttributes are provided, then, per RFC 2315,
229 * they must include at least two attributes: content type and
230 * message digest. The message digest attribute value will be
231 * auto-calculated during signing and will be ignored if provided.
233 * Here's an example of providing these two attributes:
235 * forge.pkcs7.createSignedData();
237 * issuer: cert.issuer.attributes,
238 * serialNumber: cert.serialNumber,
240 * digestAlgorithm: forge.pki.oids.sha1,
241 * authenticatedAttributes: [{
242 * type: forge.pki.oids.contentType,
243 * value: forge.pki.oids.data
245 * type: forge.pki.oids.messageDigest
249 * TODO: Support [subjectKeyIdentifier] as signer's ID.
251 * @param signer the signer information:
252 * key the signer's private key.
253 * [certificate] a certificate containing the public key
254 * associated with the signer's private key; use this option as
255 * an alternative to specifying signer.issuer and
256 * signer.serialNumber.
257 * [issuer] the issuer attributes (eg: cert.issuer.attributes).
258 * [serialNumber] the signer's certificate's serial number in
259 * hexadecimal (eg: cert.serialNumber).
260 * [digestAlgorithm] the message digest OID, as a string, to use
261 * (eg: forge.pki.oids.sha1).
262 * [authenticatedAttributes] an optional array of attributes
263 * to also sign along with the content.
265 addSigner: function(signer) {
266 var issuer = signer.issuer;
267 var serialNumber = signer.serialNumber;
268 if(signer.certificate) {
269 var cert = signer.certificate;
270 if(typeof cert === 'string') {
271 cert = forge.pki.certificateFromPem(cert);
273 issuer = cert.issuer.attributes;
274 serialNumber = cert.serialNumber;
276 var key = signer.key;
279 'Could not add PKCS#7 signer; no private key specified.');
281 if(typeof key === 'string') {
282 key = forge.pki.privateKeyFromPem(key);
285 // ensure OID known for digest algorithm
286 var digestAlgorithm = signer.digestAlgorithm || forge.pki.oids.sha1;
287 switch(digestAlgorithm) {
288 case forge.pki.oids.sha1:
289 case forge.pki.oids.sha256:
290 case forge.pki.oids.sha384:
291 case forge.pki.oids.sha512:
292 case forge.pki.oids.md5:
296 'Could not add PKCS#7 signer; unknown message digest algorithm: ' +
300 // if authenticatedAttributes is present, then the attributes
301 // must contain at least PKCS #9 content-type and message-digest
302 var authenticatedAttributes = signer.authenticatedAttributes || [];
303 if(authenticatedAttributes.length > 0) {
304 var contentType = false;
305 var messageDigest = false;
306 for(var i = 0; i < authenticatedAttributes.length; ++i) {
307 var attr = authenticatedAttributes[i];
308 if(!contentType && attr.type === forge.pki.oids.contentType) {
315 if(!messageDigest && attr.type === forge.pki.oids.messageDigest) {
316 messageDigest = true;
324 if(!contentType || !messageDigest) {
325 throw new Error('Invalid signer.authenticatedAttributes. If ' +
326 'signer.authenticatedAttributes is specified, then it must ' +
327 'contain at least two attributes, PKCS #9 content-type and ' +
328 'PKCS #9 message-digest.');
336 serialNumber: serialNumber,
337 digestAlgorithm: digestAlgorithm,
338 signatureAlgorithm: forge.pki.oids.rsaEncryption,
340 authenticatedAttributes: authenticatedAttributes,
341 unauthenticatedAttributes: []
347 * @param options Options to apply when signing:
348 * [detached] boolean. If signing should be done in detached mode. Defaults to false.
350 sign: function(options) {
351 options = options || {};
352 // auto-generate content info
353 if(typeof msg.content !== 'object' || msg.contentInfo === null) {
354 // use Data ContentInfo
355 msg.contentInfo = asn1.create(
356 asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
358 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
359 asn1.oidToDer(forge.pki.oids.data).getBytes())
362 // add actual content, if present
363 if('content' in msg) {
365 if(msg.content instanceof forge.util.ByteBuffer) {
366 content = msg.content.bytes();
367 } else if(typeof msg.content === 'string') {
368 content = forge.util.encodeUtf8(msg.content);
371 if (options.detached) {
372 msg.detachedContent = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, content);
374 msg.contentInfo.value.push(
375 // [0] EXPLICIT content
376 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
377 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
384 // no signers, return early (degenerate case for certificate container)
385 if(msg.signers.length === 0) {
389 // generate digest algorithm identifiers
390 var mds = addDigestAlgorithmIds();
392 // generate signerInfos
397 throw new Error('PKCS#7 signature verification not yet implemented.');
403 * @param cert the certificate to add.
405 addCertificate: function(cert) {
407 if(typeof cert === 'string') {
408 cert = forge.pki.certificateFromPem(cert);
410 msg.certificates.push(cert);
414 * Add a certificate revokation list.
416 * @param crl the certificate revokation list to add.
418 addCertificateRevokationList: function(crl) {
419 throw new Error('PKCS#7 CRL support not yet implemented.');
424 function addDigestAlgorithmIds() {
427 for(var i = 0; i < msg.signers.length; ++i) {
428 var signer = msg.signers[i];
429 var oid = signer.digestAlgorithm;
432 mds[oid] = forge.md[forge.pki.oids[oid]].create();
434 if(signer.authenticatedAttributes.length === 0) {
435 // no custom attributes to digest; use content message digest
436 signer.md = mds[oid];
438 // custom attributes to be digested; use own message digest
439 // TODO: optimize to just copy message digest state if that
440 // feature is ever supported with message digests
441 signer.md = forge.md[forge.pki.oids[oid]].create();
445 // add unique digest algorithm identifiers
446 msg.digestAlgorithmIdentifiers = [];
447 for(var oid in mds) {
448 msg.digestAlgorithmIdentifiers.push(
449 // AlgorithmIdentifier
450 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
452 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
453 asn1.oidToDer(oid).getBytes()),
455 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
462 function addSignerInfos(mds) {
465 if (msg.detachedContent) {
466 // Signature has been made in detached mode.
467 content = msg.detachedContent;
469 // Note: ContentInfo is a SEQUENCE with 2 values, second value is
470 // the content field and is optional for a ContentInfo but required here
471 // since signers are present
472 // get ContentInfo content
473 content = msg.contentInfo.value[1];
474 // skip [0] EXPLICIT content wrapper
475 content = content.value[0];
480 'Could not sign PKCS#7 message; there is no content to sign.');
483 // get ContentInfo content type
484 var contentType = asn1.derToOid(msg.contentInfo.value[0].value);
487 var bytes = asn1.toDer(content);
489 // skip identifier and length per RFC 2315 9.3
490 // skip identifier (1 byte)
492 // read and discard length bytes
493 asn1.getBerValueLength(bytes);
494 bytes = bytes.getBytes();
496 // digest content DER value bytes
497 for(var oid in mds) {
498 mds[oid].start().update(bytes);
502 var signingTime = new Date();
503 for(var i = 0; i < msg.signers.length; ++i) {
504 var signer = msg.signers[i];
506 if(signer.authenticatedAttributes.length === 0) {
507 // if ContentInfo content type is not "Data", then
508 // authenticatedAttributes must be present per RFC 2315
509 if(contentType !== forge.pki.oids.data) {
511 'Invalid signer; authenticatedAttributes must be present ' +
512 'when the ContentInfo content type is not PKCS#7 Data.');
515 // process authenticated attributes
517 signer.authenticatedAttributesAsn1 = asn1.create(
518 asn1.Class.CONTEXT_SPECIFIC, 0, true, []);
520 // per RFC 2315, attributes are to be digested using a SET container
521 // not the above [0] IMPLICIT container
522 var attrsAsn1 = asn1.create(
523 asn1.Class.UNIVERSAL, asn1.Type.SET, true, []);
525 for(var ai = 0; ai < signer.authenticatedAttributes.length; ++ai) {
526 var attr = signer.authenticatedAttributes[ai];
527 if(attr.type === forge.pki.oids.messageDigest) {
528 // use content message digest as value
529 attr.value = mds[signer.digestAlgorithm].digest();
530 } else if(attr.type === forge.pki.oids.signingTime) {
531 // auto-populate signing time if not already set
533 attr.value = signingTime;
537 // convert to ASN.1 and push onto Attributes SET (for signing) and
538 // onto authenticatedAttributesAsn1 to complete SignedData ASN.1
539 // TODO: optimize away duplication
540 attrsAsn1.value.push(_attributeToAsn1(attr));
541 signer.authenticatedAttributesAsn1.value.push(_attributeToAsn1(attr));
544 // DER-serialize and digest SET OF attributes only
545 bytes = asn1.toDer(attrsAsn1).getBytes();
546 signer.md.start().update(bytes);
550 signer.signature = signer.key.sign(signer.md, 'RSASSA-PKCS1-V1_5');
554 msg.signerInfos = _signersToAsn1(msg.signers);
559 * Creates an empty PKCS#7 message of type EncryptedData.
561 * @return the message.
563p7.createEncryptedData = function() {
566 type: forge.pki.oids.encryptedData,
569 algorithm: forge.pki.oids['aes256-CBC']
573 * Reads an EncryptedData content block (in ASN.1 format)
575 * @param obj The ASN.1 representation of the EncryptedData content block
577 fromAsn1: function(obj) {
578 // Validate EncryptedData content block and capture data.
579 _fromAsn1(msg, obj, p7.asn1.encryptedDataValidator);
583 * Decrypt encrypted content
585 * @param key The (symmetric) key as a byte buffer
587 decrypt: function(key) {
588 if(key !== undefined) {
589 msg.encryptedContent.key = key;
591 _decryptContent(msg);
598 * Creates an empty PKCS#7 message of type EnvelopedData.
600 * @return the message.
602p7.createEnvelopedData = function() {
605 type: forge.pki.oids.envelopedData,
609 algorithm: forge.pki.oids['aes256-CBC']
613 * Reads an EnvelopedData content block (in ASN.1 format)
615 * @param obj the ASN.1 representation of the EnvelopedData content block.
617 fromAsn1: function(obj) {
618 // validate EnvelopedData content block and capture data
619 var capture = _fromAsn1(msg, obj, p7.asn1.envelopedDataValidator);
620 msg.recipients = _recipientsFromAsn1(capture.recipientInfos.value);
625 return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
627 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
628 asn1.oidToDer(msg.type).getBytes()),
630 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
631 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
633 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
634 asn1.integerToDer(msg.version).getBytes()),
636 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
637 _recipientsToAsn1(msg.recipients)),
638 // EncryptedContentInfo
639 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true,
640 _encryptedContentToAsn1(msg.encryptedContent))
647 * Find recipient by X.509 certificate's issuer.
649 * @param cert the certificate with the issuer to look for.
651 * @return the recipient object.
653 findRecipient: function(cert) {
654 var sAttr = cert.issuer.attributes;
656 for(var i = 0; i < msg.recipients.length; ++i) {
657 var r = msg.recipients[i];
658 var rAttr = r.issuer;
660 if(r.serialNumber !== cert.serialNumber) {
664 if(rAttr.length !== sAttr.length) {
669 for(var j = 0; j < sAttr.length; ++j) {
670 if(rAttr[j].type !== sAttr[j].type ||
671 rAttr[j].value !== sAttr[j].value) {
686 * Decrypt enveloped content
688 * @param recipient The recipient object related to the private key
689 * @param privKey The (RSA) private key object
691 decrypt: function(recipient, privKey) {
692 if(msg.encryptedContent.key === undefined && recipient !== undefined &&
693 privKey !== undefined) {
694 switch(recipient.encryptedContent.algorithm) {
695 case forge.pki.oids.rsaEncryption:
696 case forge.pki.oids.desCBC:
697 var key = privKey.decrypt(recipient.encryptedContent.content);
698 msg.encryptedContent.key = forge.util.createBuffer(key);
702 throw new Error('Unsupported asymmetric cipher, ' +
703 'OID ' + recipient.encryptedContent.algorithm);
707 _decryptContent(msg);
711 * Add (another) entity to list of recipients.
713 * @param cert The certificate of the entity to add.
715 addRecipient: function(cert) {
716 msg.recipients.push({
718 issuer: cert.issuer.attributes,
719 serialNumber: cert.serialNumber,
721 // We simply assume rsaEncryption here, since forge.pki only
722 // supports RSA so far. If the PKI module supports other
723 // ciphers one day, we need to modify this one as well.
724 algorithm: forge.pki.oids.rsaEncryption,
731 * Encrypt enveloped content.
733 * This function supports two optional arguments, cipher and key, which
734 * can be used to influence symmetric encryption. Unless cipher is
735 * provided, the cipher specified in encryptedContent.algorithm is used
736 * (defaults to AES-256-CBC). If no key is provided, encryptedContent.key
737 * is (re-)used. If that one's not set, a random key will be generated
740 * @param [key] The key to be used for symmetric encryption.
741 * @param [cipher] The OID of the symmetric cipher to use.
743 encrypt: function(key, cipher) {
744 // Part 1: Symmetric encryption
745 if(msg.encryptedContent.content === undefined) {
746 cipher = cipher || msg.encryptedContent.algorithm;
747 key = key || msg.encryptedContent.key;
749 var keyLen, ivLen, ciphFn;
751 case forge.pki.oids['aes128-CBC']:
754 ciphFn = forge.aes.createEncryptionCipher;
757 case forge.pki.oids['aes192-CBC']:
760 ciphFn = forge.aes.createEncryptionCipher;
763 case forge.pki.oids['aes256-CBC']:
766 ciphFn = forge.aes.createEncryptionCipher;
769 case forge.pki.oids['des-EDE3-CBC']:
772 ciphFn = forge.des.createEncryptionCipher;
776 throw new Error('Unsupported symmetric cipher, OID ' + cipher);
779 if(key === undefined) {
780 key = forge.util.createBuffer(forge.random.getBytes(keyLen));
781 } else if(key.length() != keyLen) {
782 throw new Error('Symmetric key has wrong length; ' +
783 'got ' + key.length() + ' bytes, expected ' + keyLen + '.');
786 // Keep a copy of the key & IV in the object, so the caller can
787 // use it for whatever reason.
788 msg.encryptedContent.algorithm = cipher;
789 msg.encryptedContent.key = key;
790 msg.encryptedContent.parameter = forge.util.createBuffer(
791 forge.random.getBytes(ivLen));
793 var ciph = ciphFn(key);
794 ciph.start(msg.encryptedContent.parameter.copy());
795 ciph.update(msg.content);
797 // The finish function does PKCS#7 padding by default, therefore
798 // no action required by us.
800 throw new Error('Symmetric encryption failed.');
803 msg.encryptedContent.content = ciph.output;
806 // Part 2: asymmetric encryption for each recipient
807 for(var i = 0; i < msg.recipients.length; ++i) {
808 var recipient = msg.recipients[i];
810 // Nothing to do, encryption already done.
811 if(recipient.encryptedContent.content !== undefined) {
815 switch(recipient.encryptedContent.algorithm) {
816 case forge.pki.oids.rsaEncryption:
817 recipient.encryptedContent.content =
818 recipient.encryptedContent.key.encrypt(
819 msg.encryptedContent.key.data);
823 throw new Error('Unsupported asymmetric cipher, OID ' +
824 recipient.encryptedContent.algorithm);
833 * Converts a single recipient from an ASN.1 object.
835 * @param obj the ASN.1 RecipientInfo.
837 * @return the recipient object.
839function _recipientFromAsn1(obj) {
840 // validate EnvelopedData content block and capture data
843 if(!asn1.validate(obj, p7.asn1.recipientInfoValidator, capture, errors)) {
844 var error = new Error('Cannot read PKCS#7 RecipientInfo. ' +
845 'ASN.1 object is not an PKCS#7 RecipientInfo.');
846 error.errors = errors;
851 version: capture.version.charCodeAt(0),
852 issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
853 serialNumber: forge.util.createBuffer(capture.serial).toHex(),
855 algorithm: asn1.derToOid(capture.encAlgorithm),
856 parameter: capture.encParameter ? capture.encParameter.value : undefined,
857 content: capture.encKey
863 * Converts a single recipient object to an ASN.1 object.
865 * @param obj the recipient object.
867 * @return the ASN.1 RecipientInfo.
869function _recipientToAsn1(obj) {
870 return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
872 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
873 asn1.integerToDer(obj.version).getBytes()),
874 // IssuerAndSerialNumber
875 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
877 forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),
879 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
880 forge.util.hexToBytes(obj.serialNumber))
882 // KeyEncryptionAlgorithmIdentifier
883 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
885 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
886 asn1.oidToDer(obj.encryptedContent.algorithm).getBytes()),
887 // Parameter, force NULL, only RSA supported for now.
888 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
891 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
892 obj.encryptedContent.content)
897 * Map a set of RecipientInfo ASN.1 objects to recipient objects.
899 * @param infos an array of ASN.1 representations RecipientInfo (i.e. SET OF).
901 * @return an array of recipient objects.
903function _recipientsFromAsn1(infos) {
905 for(var i = 0; i < infos.length; ++i) {
906 ret.push(_recipientFromAsn1(infos[i]));
912 * Map an array of recipient objects to ASN.1 RecipientInfo objects.
914 * @param recipients an array of recipientInfo objects.
916 * @return an array of ASN.1 RecipientInfos.
918function _recipientsToAsn1(recipients) {
920 for(var i = 0; i < recipients.length; ++i) {
921 ret.push(_recipientToAsn1(recipients[i]));
927 * Converts a single signer from an ASN.1 object.
929 * @param obj the ASN.1 representation of a SignerInfo.
931 * @return the signer object.
933function _signerFromAsn1(obj) {
934 // validate EnvelopedData content block and capture data
937 if(!asn1.validate(obj, p7.asn1.signerInfoValidator, capture, errors)) {
938 var error = new Error('Cannot read PKCS#7 SignerInfo. ' +
939 'ASN.1 object is not an PKCS#7 SignerInfo.');
940 error.errors = errors;
945 version: capture.version.charCodeAt(0),
946 issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
947 serialNumber: forge.util.createBuffer(capture.serial).toHex(),
948 digestAlgorithm: asn1.derToOid(capture.digestAlgorithm),
949 signatureAlgorithm: asn1.derToOid(capture.signatureAlgorithm),
950 signature: capture.signature,
951 authenticatedAttributes: [],
952 unauthenticatedAttributes: []
955 // TODO: convert attributes
956 var authenticatedAttributes = capture.authenticatedAttributes || [];
957 var unauthenticatedAttributes = capture.unauthenticatedAttributes || [];
963 * Converts a single signerInfo object to an ASN.1 object.
965 * @param obj the signerInfo object.
967 * @return the ASN.1 representation of a SignerInfo.
969function _signerToAsn1(obj) {
971 var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
973 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
974 asn1.integerToDer(obj.version).getBytes()),
975 // issuerAndSerialNumber
976 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
978 forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),
980 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
981 forge.util.hexToBytes(obj.serialNumber))
984 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
986 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
987 asn1.oidToDer(obj.digestAlgorithm).getBytes()),
989 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
993 // authenticatedAttributes (OPTIONAL)
994 if(obj.authenticatedAttributesAsn1) {
995 // add ASN.1 previously generated during signing
996 rval.value.push(obj.authenticatedAttributesAsn1);
999 // digestEncryptionAlgorithm
1000 rval.value.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1002 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1003 asn1.oidToDer(obj.signatureAlgorithm).getBytes()),
1004 // parameters (null)
1005 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
1009 rval.value.push(asn1.create(
1010 asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, obj.signature));
1012 // unauthenticatedAttributes (OPTIONAL)
1013 if(obj.unauthenticatedAttributes.length > 0) {
1015 var attrsAsn1 = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, []);
1016 for(var i = 0; i < obj.unauthenticatedAttributes.length; ++i) {
1017 var attr = obj.unauthenticatedAttributes[i];
1018 attrsAsn1.values.push(_attributeToAsn1(attr));
1020 rval.value.push(attrsAsn1);
1027 * Map a set of SignerInfo ASN.1 objects to an array of signer objects.
1029 * @param signerInfoAsn1s an array of ASN.1 SignerInfos (i.e. SET OF).
1031 * @return an array of signers objects.
1033function _signersFromAsn1(signerInfoAsn1s) {
1035 for(var i = 0; i < signerInfoAsn1s.length; ++i) {
1036 ret.push(_signerFromAsn1(signerInfoAsn1s[i]));
1042 * Map an array of signer objects to ASN.1 objects.
1044 * @param signers an array of signer objects.
1046 * @return an array of ASN.1 SignerInfos.
1048function _signersToAsn1(signers) {
1050 for(var i = 0; i < signers.length; ++i) {
1051 ret.push(_signerToAsn1(signers[i]));
1057 * Convert an attribute object to an ASN.1 Attribute.
1059 * @param attr the attribute object.
1061 * @return the ASN.1 Attribute.
1063function _attributeToAsn1(attr) {
1066 // TODO: generalize to support more attributes
1067 if(attr.type === forge.pki.oids.contentType) {
1068 value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1069 asn1.oidToDer(attr.value).getBytes());
1070 } else if(attr.type === forge.pki.oids.messageDigest) {
1071 value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
1072 attr.value.bytes());
1073 } else if(attr.type === forge.pki.oids.signingTime) {
1074 /* Note per RFC 2985: Dates between 1 January 1950 and 31 December 2049
1075 (inclusive) MUST be encoded as UTCTime. Any dates with year values
1076 before 1950 or after 2049 MUST be encoded as GeneralizedTime. [Further,]
1077 UTCTime values MUST be expressed in Greenwich Mean Time (Zulu) and MUST
1078 include seconds (i.e., times are YYMMDDHHMMSSZ), even where the
1079 number of seconds is zero. Midnight (GMT) must be represented as
1081 // TODO: make these module-level constants
1082 var jan_1_1950 = new Date('1950-01-01T00:00:00Z');
1083 var jan_1_2050 = new Date('2050-01-01T00:00:00Z');
1084 var date = attr.value;
1085 if(typeof date === 'string') {
1086 // try to parse date
1087 var timestamp = Date.parse(date);
1088 if(!isNaN(timestamp)) {
1089 date = new Date(timestamp);
1090 } else if(date.length === 13) {
1091 // YYMMDDHHMMSSZ (13 chars for UTCTime)
1092 date = asn1.utcTimeToDate(date);
1094 // assume generalized time
1095 date = asn1.generalizedTimeToDate(date);
1099 if(date >= jan_1_1950 && date < jan_1_2050) {
1100 value = asn1.create(
1101 asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,
1102 asn1.dateToUtcTime(date));
1104 value = asn1.create(
1105 asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false,
1106 asn1.dateToGeneralizedTime(date));
1110 // Added this line to support custom attributes
1111 if ((value == null) && (attr != null) && (attr.value != null)) { value = attr.value; }
1113 // TODO: expose as common API call
1114 // create a RelativeDistinguishedName set
1115 // each value in the set is an AttributeTypeAndValue first
1116 // containing the type (an OID) and second the value
1117 return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1119 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1120 asn1.oidToDer(attr.type).getBytes()),
1121 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
1129 * Map messages encrypted content to ASN.1 objects.
1131 * @param ec The encryptedContent object of the message.
1133 * @return ASN.1 representation of the encryptedContent object (SEQUENCE).
1135function _encryptedContentToAsn1(ec) {
1137 // ContentType, always Data for the moment
1138 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1139 asn1.oidToDer(forge.pki.oids.data).getBytes()),
1140 // ContentEncryptionAlgorithmIdentifier
1141 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1143 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1144 asn1.oidToDer(ec.algorithm).getBytes()),
1149 asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
1150 ec.parameter.getBytes())
1152 // [0] EncryptedContent
1153 asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
1154 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
1155 ec.content.getBytes())
1161 * Reads the "common part" of an PKCS#7 content block (in ASN.1 format)
1163 * This function reads the "common part" of the PKCS#7 content blocks
1164 * EncryptedData and EnvelopedData, i.e. version number and symmetrically
1165 * encrypted content block.
1167 * The result of the ASN.1 validate and capture process is returned
1168 * to allow the caller to extract further data, e.g. the list of recipients
1169 * in case of a EnvelopedData object.
1171 * @param msg the PKCS#7 object to read the data to.
1172 * @param obj the ASN.1 representation of the content block.
1173 * @param validator the ASN.1 structure validator object to use.
1175 * @return the value map captured by validator object.
1177function _fromAsn1(msg, obj, validator) {
1180 if(!asn1.validate(obj, validator, capture, errors)) {
1181 var error = new Error('Cannot read PKCS#7 message. ' +
1182 'ASN.1 object is not a supported PKCS#7 message.');
1183 error.errors = error;
1187 // Check contentType, so far we only support (raw) Data.
1188 var contentType = asn1.derToOid(capture.contentType);
1189 if(contentType !== forge.pki.oids.data) {
1190 throw new Error('Unsupported PKCS#7 message. ' +
1191 'Only wrapped ContentType Data supported.');
1194 if(capture.encryptedContent) {
1196 if(forge.util.isArray(capture.encryptedContent)) {
1197 for(var i = 0; i < capture.encryptedContent.length; ++i) {
1198 if(capture.encryptedContent[i].type !== asn1.Type.OCTETSTRING) {
1199 throw new Error('Malformed PKCS#7 message, expecting encrypted ' +
1200 'content constructed of only OCTET STRING objects.');
1202 content += capture.encryptedContent[i].value;
1205 content = capture.encryptedContent;
1207 msg.encryptedContent = {
1208 algorithm: asn1.derToOid(capture.encAlgorithm),
1209 parameter: forge.util.createBuffer(capture.encParameter.value),
1210 content: forge.util.createBuffer(content)
1214 if(capture.content) {
1216 if(forge.util.isArray(capture.content)) {
1217 for(var i = 0; i < capture.content.length; ++i) {
1218 if(capture.content[i].type !== asn1.Type.OCTETSTRING) {
1219 throw new Error('Malformed PKCS#7 message, expecting ' +
1220 'content constructed of only OCTET STRING objects.');
1222 content += capture.content[i].value;
1225 content = capture.content;
1227 msg.content = forge.util.createBuffer(content);
1230 msg.version = capture.version.charCodeAt(0);
1231 msg.rawCapture = capture;
1237 * Decrypt the symmetrically encrypted content block of the PKCS#7 message.
1239 * Decryption is skipped in case the PKCS#7 message object already has a
1240 * (decrypted) content attribute. The algorithm, key and cipher parameters
1241 * (probably the iv) are taken from the encryptedContent attribute of the
1244 * @param The PKCS#7 message object.
1246function _decryptContent(msg) {
1247 if(msg.encryptedContent.key === undefined) {
1248 throw new Error('Symmetric key not available.');
1251 if(msg.content === undefined) {
1254 switch(msg.encryptedContent.algorithm) {
1255 case forge.pki.oids['aes128-CBC']:
1256 case forge.pki.oids['aes192-CBC']:
1257 case forge.pki.oids['aes256-CBC']:
1258 ciph = forge.aes.createDecryptionCipher(msg.encryptedContent.key);
1261 case forge.pki.oids['desCBC']:
1262 case forge.pki.oids['des-EDE3-CBC']:
1263 ciph = forge.des.createDecryptionCipher(msg.encryptedContent.key);
1267 throw new Error('Unsupported symmetric cipher, OID ' +
1268 msg.encryptedContent.algorithm);
1270 ciph.start(msg.encryptedContent.parameter);
1271 ciph.update(msg.encryptedContent.content);
1273 if(!ciph.finish()) {
1274 throw new Error('Symmetric decryption failed.');
1277 msg.content = ciph.output;