RFC 5652

Cryptographic Message Syntax (CMS)

๐Ÿ“… published September 2009, from ietf

Part 1 of 11 ๐ŸŽ€

Cryptographic Message Syntax (CMS) ๐ŸŽ€

bestie, this one is the RFC 5652 glow-up ๐Ÿฆ‹ It's by R. Housley from Vigil Security, published in September 2009, and it's a Standards Track doc. It obsoletes RFC 3852 (so 3852 is officially retired now ๐Ÿ’€), and it doesn't update anything else. Okay, the vibe is set โœจ

Abstract โœจ

okay so the abstract is basically the TL;DR ๐Ÿ’… This doc describes the Cryptographic Message Syntax (CMS), which is the syntax you use to digitally sign, digest, authenticate, or encrypt arbitrary message content. Main character energy ๐Ÿ‘‘

Status of This Memo ๐ŸŒธ

This doc is an Internet standards track protocol for the Internet community, and it's asking for discussion and suggestions for improvements ๐Ÿซถ If you want to know where this protocol stands on the standardization ladder, check the current edition of the "Internet Official Protocol Standards" (STD 1). Distribution of this memo is unlimited, so share it with everyone ๐Ÿ’–

The copyright is held by the IETF Trust and the people listed as the document authors, ยฉ 2009, and all rights are reserved ๐Ÿ”’

This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they stood on the date this doc was published. Please actually read those, because they spell out your rights and restrictions for this document ๐Ÿ™ˆ Any Code Components pulled out of this doc have to include the Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. They come without warranty, as the BSD License describes.

This doc may contain material from IETF Documents or IETF Contributions that were published or made publicly available before November 10, 2008. The people who control the copyright in some of that material might not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. So unless you get a proper license from those copyright holders, this doc may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process. The one exceptions are formatting it for publication as an RFC or translating it into languages other than English ๐ŸŒˆ

Table of Contents ๐Ÿ“š

   1. Introduction ....................................................3
      1.1. Evolution of the CMS .......................................4
           1.1.1. Changes Since PKCS #7 Version 1.5 ...................4
           1.1.2. Changes Since RFC 2630 ..............................4
           1.1.3. Changes Since RFC 3369 ..............................5
           1.1.4. Changes Since RFC 3852 ..............................5
      1.2. Terminology ................................................5
      1.3. Version Numbers ............................................6
   2. General Overview ................................................6
   3. General Syntax ..................................................7
   4. Data Content Type ...............................................7
   5. Signed-data Content Type ........................................8
      5.1. SignedData Type ............................................9
      5.2. EncapsulatedContentInfo Type ..............................11
           5.2.1. Compatibility with PKCS #7 .........................12
      5.3. SignerInfo Type ...........................................13
      5.4. Message Digest Calculation Process ........................16
      5.5. Signature Generation Process ..............................16
      5.6. Signature Verification Process ............................17
   6. Enveloped-Data Content Type ....................................17
      6.1. EnvelopedData Type ........................................18
      6.2. RecipientInfo Type ........................................21
           6.2.1. KeyTransRecipientInfo Type .........................22
           6.2.2. KeyAgreeRecipientInfo Type .........................23
           6.2.3. KEKRecipientInfo Type ..............................25
           6.2.4. PasswordRecipientInfo Type .........................26
           6.2.5. OtherRecipientInfo Type ............................27
      6.3. Content-encryption Process ................................27
      6.4. Key-Encryption Process ....................................28
   7. Digested-Data Content Type .....................................28
   8. Encrypted-Data Content Type ....................................29
   9. Authenticated-Data Content Type ................................30
      9.1. AuthenticatedData Type ....................................31
      9.2. MAC Generation ............................................33
      9.3. MAC Verification ..........................................34
   10. Useful Types ..................................................34
      10.1. Algorithm Identifier Types ...............................35
           10.1.1. DigestAlgorithmIdentifier .........................35
           10.1.2. SignatureAlgorithmIdentifier ......................35
           10.1.3. KeyEncryptionAlgorithmIdentifier ..................35
           10.1.4. ContentEncryptionAlgorithmIdentifier ..............36
           10.1.5. MessageAuthenticationCodeAlgorithm ................36
           10.1.6. KeyDerivationAlgorithmIdentifier ..................36
      10.2. Other Useful Types .......................................36
           10.2.1. RevocationInfoChoices .............................36
           10.2.2. CertificateChoices ................................37
           10.2.3. CertificateSet ....................................38
           10.2.4. IssuerAndSerialNumber .............................38
           10.2.5. CMSVersion ........................................39
           10.2.6. UserKeyingMaterial ................................39
           10.2.7. OtherKeyAttribute .................................39
   11. Useful Attributes .............................................39
      11.1. Content Type .............................................40
      11.2. Message Digest ...........................................40
      11.3. Signing Time .............................................41
      11.4. Countersignature .........................................42
   12. ASN.1 Modules .................................................43
      12.1. CMS ASN.1 Module .........................................44
      12.2. Version 1 Attribute Certificate ASN.1 Module .............51
   13. References ....................................................52
      13.1. Normative References .....................................52
      13.2. Informative References ...................................53
   14. Security Considerations .......................................54
   15. Acknowledgments ...............................................56

1. Introduction ๐ŸŽ€

okay so this doc describes the Cryptographic Message Syntax (CMS), and that syntax is how you digitally sign, digest, authenticate, or encrypt arbitrary message content ๐Ÿ’…

The CMS is an encapsulation syntax for data protection, and it supports digital signatures and encryption โœจ You can nest encapsulations, so one envelope can sit inside another, which is giving Russian doll ๐Ÿช† Likewise, one party can digitally sign some data that was already encapsulated ๐Ÿ–Š๏ธ It also lets you sign arbitrary attributes, like the signing time, along with the message content, and it provides for other attributes such as countersignatures to be attached to a signature ๐Ÿ”ฅ

The CMS can work with a bunch of different architectures for certificate-based key management, like the one defined by the PKIX (Public Key Infrastructure using X.509) working group [PROFILE] ๐ŸŒธ

CMS values are generated using ASN.1 [X.208-88], encoded with BER (Basic Encoding Rules) [X.209-88]. The values are usually represented as octet strings. Lots of systems can send arbitrary octet strings reliably, but it's well known that many electronic mail systems can't. This doc doesn't cover how to encode octet strings for reliable transmission in those environments, so that's a separate problem ๐Ÿ™ˆ

1.1. Evolution of the CMS ๐Ÿงš

The CMS comes from PKCS #7 version 1.5, which is documented in RFC 2315 [PKCS#7]. PKCS #7 version 1.5 was developed outside the IETF. It was first published as an RSA Laboratories Technical Note in November 1993. Since then the IETF has taken over development and maintenance of the CMS, and today several important IETF Standards-Track protocols use it ๐Ÿ’–

This section walks through the changes the IETF made to the CMS in each published version ๐Ÿฆ‹

1.1.1. Changes Since PKCS #7 Version 1.5 ๐Ÿ’…

RFC 2630 [CMS1] was the first version of the CMS on the IETF Standards Track. Backward compatibility with PKCS #7 version 1.5 was kept wherever possible ๐Ÿซถ The changes were made to accommodate version 1 attribute certificate transfer and to support algorithm-independent key management. PKCS #7 version 1.5 only supported key transport. RFC 2630 adds support for key agreement and for the previously distributed symmetric key-encryption key techniques โœจ

1.1.2. Changes Since RFC 2630 ๐ŸŒˆ

RFC 3369 [CMS2] obsoletes RFC 2630 [CMS1] and RFC 3211 [PWRI] ๐Ÿ’œ Password-based key management is now part of the CMS specification, and there's an extension mechanism for new key management schemes that doesn't need further changes to the CMS. Backward compatibility with RFC 2630 and RFC 3211 is preserved. The catch is that version 2 attribute certificate transfer was added, and the use of version 1 attribute certificates is deprecated ๐Ÿ’€

Secure/Multipurpose Internet Mail Extensions (S/MIME) v2 signatures [MSG2], which are based on PKCS #7 version 1.5, are compatible with S/MIME v3 signatures [MSG3] and S/MIME v3.1 signatures [MSG3.1]. Still, there are some subtle compatibility issues with signatures based on PKCS #7 version 1.5. These issues are discussed in Section 5.2.1, and they remain with the current version of the CMS ๐Ÿ™ˆ

The specific cryptographic algorithms aren't discussed in this document. They were discussed in RFC 2630, but that discussion has moved to a separate document, [CMSALG] ๐Ÿ“ Splitting the protocol and algorithm specs means the IETF can update each one independently, which is so smart. This specification doesn't require implementing any particular algorithms. Instead, protocols that rely on the CMS are expected to pick appropriate algorithms for their environment, and those algorithms may come from [CMSALG] or somewhere else ๐Ÿ”ฅ

1.1.3. Changes Since RFC 3369 ๐ŸŽ€

RFC 3852 [CMS3] obsoletes RFC 3369 [CMS2] ๐Ÿ‘‘ Like we talked about in the last section, RFC 3369 added an extension mechanism for new key management schemes without further CMS changes. RFC 3852 adds a similar extension mechanism, this time for additional certificate formats and revocation status information formats, again without further CMS changes. These extensions are mainly documented in Sections 10.2.1 and 10.2.2. Backward compatibility with earlier versions of the CMS is preserved โœจ

The use of version numbers is described in Section 1.3 ๐Ÿ”ข

Since RFC 3369 was published, a few errata have come up. They're posted on the RFC Editor web site, and they've been fixed in this document ๐Ÿ› ๏ธ

The text in Section 11.4 that describes the counter signature unsigned attribute has been clarified. Hopefully the revised text is clearer about which part of the SignerInfo signature a countersignature covers ๐Ÿฅน๐Ÿ’–

1.1.4. Changes Since RFC 3852 ๐Ÿชœ

okay so this document obsoletes RFC 3852 [CMS3] ๐Ÿ’… and the main reason it's being published is to push the CMS further up the standards maturity ladder ๐ŸŒˆ

it also includes the clarifications that were first published in RFC 4853 [CMSMSIG] about how to properly handle the SignedData protected content type when there is more than one digital signature present โœ๏ธ

and since RFC 3852 came out, a few errata have been noticed ๐Ÿ‘€ they're posted on the RFC Editor web site, and these errors have been fixed in this document ๐Ÿซถ

1.2. Terminology ๐Ÿ“–

so the key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL are to be interpreted as described in [STDWORDS] ๐Ÿค“

1.3. Version Numbers ๐Ÿ”ข

every major data structure starts with a version number as its first item ๐ŸŽ€ the whole point is to dodge ASN.1 decode errors. some implementations don't check the version number before trying to decode, and if a decode error pops up, then they look at the version number in the error handling routine. ngl that's a reasonable move because it keeps error processing out of the fast path โšก it's also forgiving if the sender uses an incorrect version number ๐Ÿฅน

most of the starting version numbers were assigned in PKCS #7 version 1.5, and others got assigned when the structure was first created ๐Ÿ—‚๏ธ whenever a structure gets updated, a higher version number is assigned. BUT for maximum interoperability, the higher number is only used when the new syntax feature is actually employed. so basically the lowest version number that supports the generated syntax is the one you use ๐Ÿ’– no flexing unnecessarily, bestie ๐Ÿ’…

2. General Overview ๐ŸŒ

the CMS is general enough to support lots of different content types, so iconic ๐Ÿ‘‘ this document defines one protection content, ContentInfo. ContentInfo wraps a single identified content type, and that identified type can provide further encapsulation ๐Ÿช† this document defines six content types: data, signed-data, enveloped-data, digested-data, encrypted-data, and authenticated-data. more content types can be defined outside this document ๐Ÿงš

okay the rules here: an implementation that conforms to this specification MUST implement the protection content, ContentInfo, and MUST implement the data, signed-data, and enveloped-data content types. the other content types MAY be implemented ๐Ÿซถ

as a general design philosophy, each content type allows single pass processing using indefinite-length Basic Encoding Rules (BER) encoding ๐ŸŒŠ this is super helpful if the content is large, stored on tapes, or "piped" from another process ๐Ÿ“ผ but there is one big drawback ๐Ÿ˜ญ it's hard to do Distinguished Encoding Rules (DER) [X.509-88] encoding in a single pass, because the lengths of the various components may not be known in advance. HOWEVER, signed attributes inside signed-data and authenticated attributes inside authenticated-data need to be sent in DER form so recipients can verify content that has one or more unrecognized attributes ๐Ÿ” signed attributes and authenticated attributes are the only data types in the CMS that need DER encoding. they're the main characters of DER here ๐ŸŽญ

3. General Syntax ๐Ÿงฉ

the following object identifier identifies the content information type:

   id-ct-contentInfo OBJECT IDENTIFIER ::= { iso(1) member-body(2)
      us(840) rsadsi(113549) pkcs(1) pkcs9(9) smime(16) ct(1) 6 }

the CMS links a content type identifier with a piece of content ๐Ÿ’œ the syntax MUST have ASN.1 type ContentInfo:

   ContentInfo ::= SEQUENCE {
     contentType ContentType,
     content [0] EXPLICIT ANY DEFINED BY contentType }

   ContentType ::= OBJECT IDENTIFIER

here's what the ContentInfo fields mean ๐Ÿ“

  • contentType tells you the type of the associated content. it's an object identifier, which is a unique string of integers assigned by an authority that defines the content type ๐Ÿ”ข
  • content is the associated content. its type can be figured out uniquely from contentType. content types for data, signed-data, enveloped-data, digested-data, encrypted-data, and authenticated-data are defined in this document. if additional content types get defined in other documents, the ASN.1 type defined SHOULD NOT be a CHOICE type ๐Ÿ™ˆ

4. Data Content Type ๐Ÿ“ฆ

the following object identifier identifies the data content type:

   id-data OBJECT IDENTIFIER ::= { iso(1) member-body(2)
      us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 }

the data content type is meant to refer to arbitrary octet strings, like ASCII text files, and the interpretation is left up to the application ๐Ÿคทโ€โ™€๏ธ those strings don't need any internal structure (although they could have their own ASN.1 definition or other structure ๐Ÿง ).

S/MIME uses id-data to identify MIME-encoded content ๐Ÿ’Œ the use of this content identifier is specified in RFC 2311 for S/MIME v2 [MSG2], RFC 2633 for S/MIME v3 [MSG3], and RFC 3851 for S/MIME v3.1 [MSG3.1] โœจ

the data content type is generally encapsulated in the signed-data, enveloped-data, digested-data, encrypted-data, or authenticated-data content type ๐ŸŽ€

5. Signed-data Content Type โœ๏ธ

the signed-data content type is a content of any type plus zero or more signature values ๐Ÿ’– any number of signers in parallel can sign any type of content. it's giving teamwork ๐Ÿซถ

the typical application of signed-data is one signer's digital signature on content of the data content type ๐Ÿ’… another typical application is disseminating certificates and certificate revocation lists (CRLs) ๐Ÿ“œ

the process for constructing signed-data goes like this ๐Ÿช„

  1. for each signer, a message digest (aka hash value) gets computed on the content using a signer-specific message-digest algorithm. if the signer is signing any information other than the content, the message digest of the content and that other information are digested with the signer's message digest algorithm (see Section 5.4), and the result becomes the "message digest."
  2. for each signer, the message digest gets digitally signed using the signer's private key ๐Ÿ”‘
  3. for each signer, the signature value and other signer-specific information are collected into a SignerInfo value, as defined in Section 5.3. certificates and CRLs for each signer, and those not corresponding to any signer, get collected in this step ๐Ÿ“‡
  4. the message digest algorithms for all the signers and the SignerInfo values for all the signers are collected together with the content into a SignedData value, as defined in Section 5.1 ๐ŸŽ

a recipient independently computes the message digest ๐Ÿงฎ this message digest and the signer's public key are used to verify the signature value. the signer's public key can be referenced in one of two ways ๐Ÿ—๏ธ first, by an issuer distinguished name along with an issuer-specific serial number, which uniquely identifies the certificate containing the public key. or second, by a subject key identifier, which accommodates both certified and uncertified public keys. while it's not required, the signer's certificate can be included in the SignedData certificates field ๐Ÿชช

when more than one signature is present, successfully validating one signature associated with a given signer is usually treated as a successful signature by that signer ๐Ÿ™Œ however, some application environments need other rules ๐Ÿค” an application that uses a rule other than one valid signature for each signer must specify those rules. also, if simply matching the signer identifier isn't enough to tell whether the signatures came from the same signer, the application specification must describe how to figure out which signatures were generated by the same signer ๐Ÿ•ต๏ธโ€โ™€๏ธ supporting different communities of recipients is the primary reason signers choose to include more than one signature ๐ŸŒˆ for example, signed-data might include signatures generated with the RSA signature algorithm and with the Elliptic Curve Digital Signature Algorithm (ECDSA) signature algorithm. that way, recipients can verify the signature associated with one algorithm or the other ๐Ÿฆ‹

this section is divided into six parts ๐ŸŽ€ the first part describes the top-level type SignedData, the second describes EncapsulatedContentInfo, and the third describes the per-signer information type SignerInfo. the fourth, fifth, and sixth parts describe the message digest calculation, signature generation, and signature verification processes, respectively ๐Ÿ’œ

5.1. SignedData Type โœจ

okay so the signed-data content type gets its own object identifier, bestie ๐Ÿ†” and here it is:

   id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
      us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }

its ASN.1 type is SignedData, and it's built like this ๐ŸŽ€:

   SignedData ::= SEQUENCE {
     version CMSVersion,
     digestAlgorithms DigestAlgorithmIdentifiers,
     encapContentInfo EncapsulatedContentInfo,
     certificates [0] IMPLICIT CertificateSet OPTIONAL,
     crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
     signerInfos SignerInfos }

   DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier

   SignerInfos ::= SET OF SignerInfo

okay now let's go through what each field means ๐Ÿงš

version is the syntax version number, and the right value depends on certificates, eContentType, and SignerInfo. The version MUST be assigned using this logic ๐Ÿ‘‘:

      IF ((certificates is present) AND
         (any certificates with a type of other are present)) OR
         ((crls is present) AND
         (any crls with a type of other are present))
      THEN version MUST be 5
      ELSE
         IF (certificates is present) AND
            (any version 2 attribute certificates are present)
         THEN version MUST be 4
         ELSE
            IF ((certificates is present) AND
               (any version 1 attribute certificates are present)) OR
               (any SignerInfo structures are version 3) OR
               (encapContentInfo eContentType is other than id-data)
            THEN version MUST be 3
            ELSE version MUST be 1

digestAlgorithms is a collection of message digest algorithm identifiers ๐Ÿฆ‹. There MAY be any number of elements in it, including zero. Each element names the message digest algorithm (plus any parameters) that one or more signers used. The whole point is to list every digest algorithm used by all the signers, in any order, so one-pass signature verification is possible โœจ. Implementations MAY fail to validate signatures that use a digest algorithm not included in this set, so keep that in mind ๐Ÿ™ˆ. The message digesting process is covered in Section 5.4.

encapContentInfo is the signed content itself, made of a content type identifier and the content ๐Ÿ’–. We get into the EncapsulatedContentInfo type in Section 5.2.

certificates is a collection of certificates ๐Ÿ“œ. The goal is for this set to be enough to build certification paths from a recognized "root" or "top-level certification authority" down to every signer in the signerInfos field. There can be more certificates than needed, and there can be certificates that build paths from two or more independent top-level certification authorities. It's also possible to have fewer certificates than needed, if recipients are expected to get the missing ones another way (for example, from an earlier set of certificates) ๐ŸŒธ. The signer's certificate MAY be included. Using version 1 attribute certificates is strongly discouraged, so please don't ๐Ÿ˜ญ.

crls is a collection of revocation status information ๐Ÿšซ. The goal is for it to hold enough info to tell whether the certificates in certificates are still valid, though that matching doesn't have to be perfect. Certificate revocation lists (CRLs) are the main source of revocation status information. There MAY be more CRLs than necessary, and there MAY also be fewer CRLs than necessary.

signerInfos is a collection of per-signer information ๐Ÿ’…. There MAY be any number of elements in it, including zero. When the collection represents more than one signature, a successful validation of one signature from a given signer should be treated as a successful signature by that signer. Some application environments need other rules though, so don't assume it's always that simple ๐ŸŒˆ. The details of the SignerInfo type are in Section 5.3. Each signer can use a different digital signature technique, and future specs might update the syntax, so all implementations MUST gracefully handle unimplemented versions of SignerInfo ๐Ÿซถ. And since not every implementation will support every signature algorithm, all implementations MUST gracefully handle unimplemented signature algorithms when they run into them too โœจ.

5.2. EncapsulatedContentInfo Type ๐ŸŽ

the content lives in the EncapsulatedContentInfo type, which looks like this:

   EncapsulatedContentInfo ::= SEQUENCE {
     eContentType ContentType,
     eContent [0] EXPLICIT OCTET STRING OPTIONAL }

   ContentType ::= OBJECT IDENTIFIER

the fields have these meanings ๐Ÿง:

eContentType is an object identifier, and it uniquely specifies the content type ๐Ÿ”–.

eContent is the content itself, carried as an octet string. The eContent does not need to be DER encoded ๐Ÿ’œ.

okay this next bit is so cool ๐Ÿ˜ญ. Making eContent optional is what lets you build "external signatures". With external signatures, the content being signed is NOT in the EncapsulatedContentInfo value inside the signed-data content type, so it's just missing ๐Ÿ™ˆ. If eContent is absent, the signatureValue is still calculated, and eContentType is assigned as though eContent had been present ๐ŸŒŸ.

In the degenerate case where there are no signers, the EncapsulatedContentInfo value being "signed" doesn't really matter ๐ŸŒ™. Here the content type in that value MUST be id-data (as defined in Section 4), and the content field of that value MUST be omitted ๐Ÿซง.

5.2.1. Compatibility with PKCS #7 ๐Ÿงท

this part is a little word of warning for anyone who wants to support both the CMS and PKCS #7 [PKCS#7] SignedData content types โš ๏ธ. Both identify the type of the encapsulated content with an object identifier, but in PKCS #7 SignedData the ASN.1 type of the content itself can vary ๐ŸŒ€.

PKCS #7 defines content as:

   content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL

and the CMS defines eContent as:

   eContent [0] EXPLICIT OCTET STRING OPTIONAL

the CMS version is way easier to work with in most apps ๐Ÿ’–, and it's compatible with both S/MIME v2 and S/MIME v3. Signed S/MIME messages made with CMS and PKCS #7 are compatible because they use identical signed message formats, specified in RFC 2311 for S/MIME v2 [MSG2], RFC 2633 for S/MIME v3 [MSG3], and RFC 3851 for S/MIME v3.1 [MSG3.1] โœจ. S/MIME v2 wraps the MIME content in a Data type (an OCTET STRING) that sits in the SignedData contentInfo content ANY field, while S/MIME v3 puts the MIME content in the SignedData encapContentInfo eContent OCTET STRING ๐Ÿฆ‹. So in S/MIME v2, v3, and v3.1, the MIME content goes in an OCTET STRING and the message digest is computed over the same portions of content. To be specific, the digest covers just the octets that make up the OCTET STRING's value, and the tag and length octets are NOT included ๐ŸŽ€.

there are incompatibilities between the CMS and PKCS #7 SignedData types when the encapsulated content isn't formatted using the Data type ๐Ÿ™ƒ. For example, say an RFC 2634 signed receipt [ESS] is encapsulated in the CMS SignedData type. Then the Receipt SEQUENCE is encoded in the SignedData encapContentInfo eContent OCTET STRING, and the message digest is computed over the entire Receipt SEQUENCE encoding (tag, length, and value octets included) ๐Ÿง . But if an RFC 2634 signed receipt is encapsulated in the PKCS #7 SignedData type, the Receipt SEQUENCE is DER encoded [X.509-88] in the SignedData contentInfo content ANY field (a SEQUENCE, not an OCTET STRING), so the digest is computed using only the value octets of the Receipt SEQUENCE encoding ๐Ÿ‘‘.

here's a strategy for backward compatibility with PKCS #7 when processing SignedData content types ๐Ÿ› ๏ธ. If the implementation can't ASN.1 decode the SignedData type using the CMS SignedData encapContentInfo eContent OCTET STRING syntax, then it MAY try decoding the SignedData type using the PKCS #7 SignedData contentInfo content ANY syntax, and compute the message digest to match ๐ŸŒธ.

and here's one for creating SignedData content types with backward compatibility in mind, when the encapsulated content isn't formatted using the Data type ๐ŸŒˆ. Implementations MAY look at the value of eContentType, and then adjust the expected DER encoding of eContent based on that object identifier. For example, to support Microsoft Authenticode [MSAC], the following information MAY be included ๐Ÿ’œ:

   eContentType Object Identifier is set to { 1 3 6 1 4 1 311 2 1 4 }

   eContent contains DER-encoded Authenticode signing information

5.3. SignerInfo Type ๐Ÿ’–

okay so per-signer info lives in this type called SignerInfo ๐ŸŽ€ it's giving very organized, bestie

SignerInfo ::= SEQUENCE {
  version CMSVersion,
  sid SignerIdentifier,
  digestAlgorithm DigestAlgorithmIdentifier,
  signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
  signatureAlgorithm SignatureAlgorithmIdentifier,
  signature SignatureValue,
  unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }

SignerIdentifier ::= CHOICE {
  issuerAndSerialNumber IssuerAndSerialNumber,
  subjectKeyIdentifier [0] SubjectKeyIdentifier }

SignedAttributes ::= SET SIZE (1..MAX) OF Attribute

UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute

Attribute ::= SEQUENCE {
  attrType OBJECT IDENTIFIER,
  attrValues SET OF AttributeValue }

AttributeValue ::= ANY

SignatureValue ::= OCTET STRING

okay now the fields, and they're all so iconic ๐ŸŒธ

  • version is the syntax version number ๐Ÿ”ข If the SignerIdentifier is the CHOICE issuerAndSerialNumber, then the version MUST be 1. If the SignerIdentifier is subjectKeyIdentifier, then the version MUST be 3. no cap, that's a hard rule ๐Ÿ’…
  • sid specifies the signer's certificate, and therefore the signer's public key ๐Ÿ”‘ The recipient needs that public key to verify the signature, so this field is literally the key to the whole thing ๐Ÿซถ SignerIdentifier gives you two options for pointing at the public key. The issuerAndSerialNumber option identifies the certificate by the issuer's distinguished name and the certificate serial number. The subjectKeyIdentifier option identifies the certificate by a key identifier. When an X.509 certificate is referenced, the key identifier matches the X.509 subjectKeyIdentifier extension value ๐Ÿฆ‹ When other certificate formats are referenced, the documents that specify that format and how it's used with the CMS must include details on matching the key identifier to the right certificate field ๐Ÿ“œ Implementations MUST support the reception of both the issuerAndSerialNumber and subjectKeyIdentifier forms of SignerIdentifier ๐Ÿ™Œ When generating a SignerIdentifier, implementations MAY support one of the forms (either issuerAndSerialNumber or subjectKeyIdentifier) and always use it, or implementations MAY arbitrarily mix the two forms. However, subjectKeyIdentifier MUST be used to refer to a public key contained in a non-X.509 certificate โœจ
  • digestAlgorithm identifies the message digest algorithm, plus any associated parameters, that the signer used ๐Ÿง The message digest is computed on either the content being signed, or the content together with the signed attributes, using the process described in Section 5.4. The message digest algorithm SHOULD be among those listed in the digestAlgorithms field of the associated SignerData. Implementations MAY fail to validate signatures that use a digest algorithm that is not included in the SignedData digestAlgorithms set. oof, that one's a little risky, so keep an eye on it ๐Ÿ‘€
  • signedAttrs is a collection of attributes that get signed โœ๏ธ The field is optional, but it MUST be present if the content type of the EncapsulatedContentInfo value being signed is not id-data. SignedAttributes MUST be DER encoded, even if the rest of the structure is BER encoded ๐Ÿ’œ Useful attribute types, like signing time, are defined in Section 11. If the field is present, it MUST contain, at a minimum, the following two attributes:
    • A content-type attribute, whose value is the content type of the EncapsulatedContentInfo value being signed. Section 11.1 defines the content-type attribute. However, the content-type attribute MUST NOT be used as part of a countersignature unsigned attribute as defined in Section 11.4 ๐Ÿ™ˆ
    • A message-digest attribute, whose value is the message digest of the content. Section 11.2 defines the message-digest attribute ๐ŸŒˆ
  • signatureAlgorithm identifies the signature algorithm, plus any associated parameters, that the signer used to generate the digital signature ๐Ÿ–‹๏ธ
  • signature is the result of digital signature generation, using the message digest and the signer's private key ๐Ÿ” The details depend on the signature algorithm that's employed ๐Ÿงš
  • unsignedAttrs is a collection of attributes that are NOT signed ๐Ÿ‘€ The field is optional. Useful attribute types, like countersignatures, are defined in Section 11 ๐Ÿ“

okay and then the attribute fields, which are the last bit of this section, I promise ๐Ÿ’€

  • attrType tells you what type of attribute it is. It's an object identifier ๐Ÿ†”
  • attrValues is a set of values that make up the attribute. The type of each value in the set can be determined uniquely by attrType. The attrType can impose restrictions on the number of items in the set ๐Ÿ’…

5.4. Message Digest Calculation Process ๐Ÿงฎ

the message digest calculation process computes a digest on either the content being signed, or the content together with the signed attributes ๐Ÿ’ซ Either way, the starting input is the "value" of the encapsulated content being signed. Specifically, that starting input is the encapContentInfo eContent OCTET STRING that the signing process is applied to. Only the octets that make up the value of the eContent OCTET STRING go into the message digest algorithm. The tag and length octets do NOT ๐Ÿ™…โ€โ™€๏ธ

the result depends on whether the signedAttrs field is there. When the field is absent, the result is just the message digest of the content, as described above ๐ŸŒท When the field is present, though, the result is the message digest of the complete DER encoding of the SignedAttrs value in the signedAttrs field ๐ŸŽ€ Since the SignedAttrs value, when present, must contain the content-type and the message-digest attributes, those values are indirectly included in the result ๐Ÿซถ The content-type attribute MUST NOT be included in a countersignature unsigned attribute as defined in Section 11.4. The signedAttrs field gets encoded a second time just for the digest calculation ๐Ÿง  The IMPLICIT [0] tag in signedAttrs is NOT used for that DER encoding. Instead, an EXPLICIT SET OF tag is used. That means the DER encoding of the EXPLICIT SET OF tag, not the IMPLICIT [0] tag, MUST be included in the message digest calculation, along with the length and content octets of the SignedAttributes value ๐Ÿฆ‹

when signedAttrs is absent, only the octets making up the value of the SignedData encapContentInfo eContent OCTET STRING (like the contents of a file) go into the calculation ๐Ÿ“„ This is a big perk, because the length of the content being signed doesn't need to be known before the signature generation process starts ๐Ÿ™Œ

even though the eContent OCTET STRING tag and length octets aren't in the message digest calculation, they're still protected by other means ๐Ÿ”’ The length octets are protected by the nature of the message digest algorithm, since it's computationally infeasible to find any two distinct message contents, of any length, that have the same message digest ๐Ÿคฏ


5.5. Signature Generation Process โœ๏ธ

the input to the signature generation process includes the result of the message digest calculation process and the signer's private key ๐Ÿ”‘ The details depend on the signature algorithm employed. The object identifier, plus any parameters, that names the signature algorithm the signer used goes in the signatureAlgorithm field ๐Ÿ†” The signature value the signer generates MUST be encoded as an OCTET STRING and carried in the signature field ๐Ÿ’Œ


5.6. Signature Verification Process ๐Ÿ•ต๏ธโ€โ™€๏ธ

the input to the signature verification process includes the result of the message digest calculation process and the signer's public key ๐Ÿ—๏ธ The recipient MAY get the correct public key for the signer by any means, but the preferred method is from a certificate found in the SignedData certificates field ๐Ÿ† Picking and validating the signer's public key MAY be based on certification path validation (see [PROFILE]), plus other external context. That stuff is beyond the scope of this document, though ๐Ÿ—บ๏ธ The details depend on the signature algorithm employed ๐Ÿงฉ

the recipient MUST NOT rely on any message digest values computed by the originator. Real talk, don't trust those! ๐Ÿ™…โ€โ™€๏ธ If the SignedData signerInfo includes signedAttributes, then the content message digest MUST be calculated as described in Section 5.4. For the signature to be valid, the message digest value the recipient calculates MUST be the same as the value of the messageDigest attribute included in the signedAttributes of the SignedData signerInfo ๐ŸŽฏ

and last one for this bit: if the SignedData signerInfo includes signedAttributes, then the content-type attribute value MUST match the SignedData encapContentInfo eContentType value โœจ๐Ÿ’–

6. Enveloped-data Content Type โœจ

okay so the enveloped-data content type is basically a secret box ๐ŸŽ It holds encrypted content of any type, plus encrypted content-encryption keys for one or more recipients. Each recipient gets their own key inside the box, and the encrypted content together with one encrypted content-encryption key for a recipient is a "digital envelope" for that recipient ๐Ÿ’Œ Any type of content can be enveloped for any number of recipients, using whichever supported key management technique fits each one ๐Ÿซถ

The typical use is one or more recipients' digital envelopes wrapped around content of the data or signed-data content types ๐ŸŽ€

Enveloped-data gets built in these steps:

  1. A content-encryption key for a particular content-encryption algorithm is generated at random ๐ŸŽฒ
  2. The content-encryption key is encrypted for each recipient. The details depend on the key management algorithm used, and four general techniques are supported ๐Ÿ”
    • key transport: the content-encryption key is encrypted in the recipient's public key.
    • key agreement: the recipient's public key and the sender's private key are used to generate a pairwise symmetric key, and then the content-encryption key is encrypted in that pairwise symmetric key.
    • symmetric key-encryption keys: the content-encryption key is encrypted in a previously distributed symmetric key-encryption key.
    • passwords: the content-encryption key is encrypted in a key-encryption key that is derived from a password or other shared secret value.
  3. For each recipient, the encrypted content-encryption key and other recipient-specific information are collected into a RecipientInfo value, defined in Section 6.2 ๐Ÿงฉ
  4. The content is encrypted with the content-encryption key. Content encryption may require that the content be padded to a multiple of some block size. See Section 6.3 ๐Ÿ“
  5. The RecipientInfo values for all the recipients are collected together with the encrypted content to form an EnvelopedData value, as defined in Section 6.1 ๐Ÿ‘‘

A recipient opens the digital envelope by decrypting one of the encrypted content-encryption keys, and then decrypting the encrypted content with the recovered content-encryption key ๐Ÿ”“โœจ

This section is split into four parts ๐Ÿฆ‹ The first part describes the top-level type EnvelopedData, the second part describes the per-recipient information type RecipientInfo, and the third and fourth parts describe the content-encryption and key-encryption processes.

6.1. EnvelopedData Type

The following object identifier identifies the enveloped-data content type ๐Ÿ†”

   id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
       us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 }

The enveloped-data content type shall have ASN.1 type EnvelopedData:

   EnvelopedData ::= SEQUENCE {
     version CMSVersion,
     originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
     recipientInfos RecipientInfos,
     encryptedContentInfo EncryptedContentInfo,
     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }

   OriginatorInfo ::= SEQUENCE {
     certs [0] IMPLICIT CertificateSet OPTIONAL,
     crls [1] IMPLICIT RevocationInfoChoices OPTIONAL }

   RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo

   EncryptedContentInfo ::= SEQUENCE {
     contentType ContentType,
     contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
     encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }

   EncryptedContent ::= OCTET STRING

   UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute

Here's what the fields of EnvelopedData mean ๐Ÿ‘€

  • version is the syntax version number. The right value depends on originatorInfo, RecipientInfo, and unprotectedAttrs. The version MUST be assigned as follows:
   IF (originatorInfo is present) AND
      ((any certificates with a type of other are present) OR
      (any crls with a type of other are present))
   THEN version is 4
   ELSE
      IF ((originatorInfo is present) AND
         (any version 2 attribute certificates are present)) OR
         (any RecipientInfo structures include pwri) OR
         (any RecipientInfo structures include ori)
      THEN version is 3
      ELSE
         IF (originatorInfo is absent) AND
            (unprotectedAttrs is absent) AND
            (all RecipientInfo structures are version 0)
         THEN version is 0
         ELSE version is 2
  • originatorInfo optionally provides information about the originator ๐Ÿ•ต๏ธโ€โ™€๏ธ It is present only if the key management algorithm requires it, and it may contain certificates and CRLs:
    • certs is a collection of certificates. certs may contain originator certificates associated with several different key management algorithms. It may also contain attribute certificates associated with the originator. The certificates contained in certs are intended to be sufficient for all recipients to build certification paths from a recognized "root" or "top-level certification authority". However, certs may contain more certificates than necessary, and there may be certificates sufficient to make certification paths from two or more independent top-level certification authorities. Alternatively, certs may contain fewer certificates than necessary, if it is expected that recipients have an alternate means of obtaining necessary certificates (e.g., from a previous set of certificates).
    • crls is a collection of CRLs. The set is intended to contain information sufficient to determine whether or not the certificates in the certs field are valid, but such correspondence is not necessary. There MAY be more CRLs than necessary, and there MAY also be fewer CRLs than necessary.
  • recipientInfos is a collection of per-recipient information ๐Ÿ“ฌ There MUST be at least one element in the collection.
  • encryptedContentInfo is the encrypted content information ๐ŸŽ
  • unprotectedAttrs is a collection of attributes that are not encrypted. The field is optional. Useful attribute types are defined in Section 11 ๐Ÿง

Here's what the fields of EncryptedContentInfo mean ๐Ÿ’…

  • contentType indicates the type of content.
  • contentEncryptionAlgorithm identifies the content-encryption algorithm, and any associated parameters, used to encrypt the content. The content-encryption process is described in Section 6.3. The same content-encryption algorithm and content-encryption key are used for all recipients ๐Ÿค
  • encryptedContent is the result of encrypting the content. The field is optional. If the field is not present, its intended value must be supplied by other means.

The recipientInfos field comes before the encryptedContentInfo field, so that an EnvelopedData value may be processed in a single pass ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ


6.2. RecipientInfo Type

Per-recipient information is represented in the type RecipientInfo ๐Ÿงš RecipientInfo has a different format for each of the supported key management techniques. Any of the key management techniques can be used for each recipient of the same encrypted content. In all cases, the encrypted content-encryption key is transferred to one or more recipients.

Since not all implementations will support every possible key management algorithm, all implementations MUST gracefully handle unimplemented algorithms when they are encountered ๐Ÿ™ˆ For example, if a recipient receives a content-encryption key encrypted in their RSA public key using RSA-OAEP (Optimal Asymmetric Encryption Padding), and the implementation only supports RSA PKCS #1 v1.5, then a graceful failure must be implemented ๐Ÿ’€

Implementations MUST support key transport, key agreement, and previously distributed symmetric key-encryption keys, as represented by ktri, kari, and kekri, respectively. Implementations MAY support the password-based key management as represented by pwri. Implementations MAY support any other key management technique as represented by ori. Since each recipient can use a different key management technique, and future specifications could define additional key management techniques, all implementations MUST gracefully handle unimplemented alternatives within the RecipientInfo CHOICE. All implementations MUST also gracefully handle unimplemented versions of otherwise supported alternatives within the RecipientInfo CHOICE, and all implementations MUST gracefully handle unimplemented or unknown ori alternatives ๐ŸŒˆ

   RecipientInfo ::= CHOICE {
     ktri KeyTransRecipientInfo,
     kari [1] KeyAgreeRecipientInfo,
     kekri [2] KEKRecipientInfo,
     pwri [3] PasswordRecipientinfo,
     ori [4] OtherRecipientInfo }

   EncryptedKey ::= OCTET STRING

6.2.1. KeyTransRecipientInfo Type ๐Ÿ”‘

okay so this is where per-recipient info using key transport lives ๐Ÿ’Œ Each KeyTransRecipientInfo carries the content-encryption key to exactly one recipient, so it's basically a personal delivery envelope ๐Ÿฆ‹

KeyTransRecipientInfo ::= SEQUENCE {
  version CMSVersion,  -- always set to 0 or 2
  rid RecipientIdentifier,
  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
  encryptedKey EncryptedKey }

RecipientIdentifier ::= CHOICE {
  issuerAndSerialNumber IssuerAndSerialNumber,
  subjectKeyIdentifier [0] SubjectKeyIdentifier }

here's what each field means ๐Ÿ’…

version is the syntax version number ๐ŸŽ€ If rid is the issuerAndSerialNumber CHOICE, then the version MUST be 0. If rid is subjectKeyIdentifier, then the version MUST be 2. no wiggle room bestie ๐Ÿ’€

rid tells the recipient which certificate or key the sender used to protect the content-encryption key ๐Ÿ” The content-encryption key gets encrypted with the recipient's public key, so the recipient needs to be able to find that exact key. RecipientIdentifier gives you two ways to point at the recipient's certificate, and so at their public key ๐ŸŒธ The certificate has to contain a key transport public key, which means a recipient X.509 version 3 certificate with a key usage extension MUST assert the keyEncipherment bit ๐Ÿงš The issuerAndSerialNumber option finds the certificate by the issuer's distinguished name plus the certificate serial number โœจ The subjectKeyIdentifier option finds it by a key identifier, and for an X.509 certificate that identifier matches the X.509 subjectKeyIdentifier extension value ๐Ÿ’œ If some other certificate format is referenced, the documents that specify that format and how it's used with CMS must include details on matching the key identifier to the right certificate field, so go check those too ๐Ÿ™ˆ For recipient processing, implementations MUST support both of these alternatives for specifying the recipient's certificate ๐Ÿซถ For sender processing, implementations MUST support at least one of them ๐Ÿ“

keyEncryptionAlgorithm identifies the key-encryption algorithm, plus any associated parameters, used to encrypt the content-encryption key for the recipient ๐ŸŒˆ The key-encryption process is described in Section 6.4 ๐Ÿ’–

encryptedKey is the result of encrypting the content-encryption key for the recipient ๐Ÿ”ฅ


6.2.2. KeyAgreeRecipientInfo Type ๐Ÿค

this one is the key agreement version ๐Ÿ’ซ Each KeyAgreeRecipientInfo moves the content-encryption key to one or more recipients who all use the same key agreement algorithm and domain parameters for that algorithm ๐Ÿ‘‘

KeyAgreeRecipientInfo ::= SEQUENCE {
  version CMSVersion,  -- always set to 3
  originator [0] EXPLICIT OriginatorIdentifierOrKey,
  ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
  recipientEncryptedKeys RecipientEncryptedKeys }

OriginatorIdentifierOrKey ::= CHOICE {
  issuerAndSerialNumber IssuerAndSerialNumber,
  subjectKeyIdentifier [0] SubjectKeyIdentifier,
  originatorKey [1] OriginatorPublicKey }

OriginatorPublicKey ::= SEQUENCE {
  algorithm AlgorithmIdentifier,
  publicKey BIT STRING }

RecipientEncryptedKeys ::= SEQUENCE OF RecipientEncryptedKey

RecipientEncryptedKey ::= SEQUENCE {
  rid KeyAgreeRecipientIdentifier,
  encryptedKey EncryptedKey }

KeyAgreeRecipientIdentifier ::= CHOICE {
  issuerAndSerialNumber IssuerAndSerialNumber,
  rKeyId [0] IMPLICIT RecipientKeyIdentifier }

RecipientKeyIdentifier ::= SEQUENCE {
  subjectKeyIdentifier SubjectKeyIdentifier,
  date GeneralizedTime OPTIONAL,
  other OtherKeyAttribute OPTIONAL }

SubjectKeyIdentifier ::= OCTET STRING

the fields, bestie ๐Ÿ’…

version is the syntax version number ๐ŸŽ€ It MUST always be 3. no exceptions, we're on team 3 ๐Ÿฅฐ

originator is a CHOICE with three alternatives that specify the sender's key agreement public key ๐Ÿฆ‹ The sender takes the matching private key and the recipient's public key and uses them to generate a pairwise key, and then the content-encryption key gets encrypted in that pairwise key ๐ŸŒธ The issuerAndSerialNumber option identifies the sender's certificate, and therefore their public key, by the issuer's distinguished name plus the certificate serial number ๐Ÿงš The subjectKeyIdentifier option does the same thing with a key identifier, and for an X.509 certificate that identifier matches the X.509 subjectKeyIdentifier extension value ๐Ÿ’œ For other certificate formats, the documents that specify that format and its CMS use must cover matching the key identifier to the right certificate field โœจ The originatorKey option carries the algorithm identifier and the sender's key agreement public key directly ๐ŸŒˆ This one allows originator anonymity, because the public key isn't certified ๐Ÿ™ˆ Implementations MUST support all three alternatives for specifying the sender's public key ๐Ÿซถ

ukm is optional ๐Ÿ’– With some key agreement algorithms, the sender provides User Keying Material (UKM) so that a different key gets generated every time the same two parties generate a pairwise key ๐Ÿ”‘ Implementations MUST accept a KeyAgreeRecipientInfo SEQUENCE that includes a ukm field ๐Ÿ“ Implementations that don't support key agreement algorithms that use UKMs MUST gracefully handle the presence of UKMs ๐ŸŒผ

keyEncryptionAlgorithm identifies the key-encryption algorithm, plus any associated parameters, used to encrypt the content-encryption key with the key-encryption key ๐ŸŽ€ The key-encryption process is described in Section 6.4 โœจ

recipientEncryptedKeys holds a recipient identifier and an encrypted key for one or more recipients ๐Ÿ’Œ The KeyAgreeRecipientIdentifier is a CHOICE with two alternatives that specify the recipient's certificate, and therefore their public key, which the sender used to generate a pairwise key-encryption key ๐Ÿฆข The recipient's certificate must contain a key agreement public key, so a recipient X.509 version 3 certificate with a key usage extension MUST assert the keyAgreement bit ๐Ÿ” The content-encryption key gets encrypted in that pairwise key-encryption key ๐ŸŒŸ The issuerAndSerialNumber option finds the recipient's certificate by the issuer's distinguished name plus the certificate serial number, and RecipientKeyIdentifier is described below ๐Ÿซง The encryptedKey is the result of encrypting the content-encryption key in the pairwise key-encryption key generated by the key agreement algorithm ๐Ÿ’œ Implementations MUST support both alternatives for specifying the recipient's certificate ๐Ÿฅน

The fields of type RecipientKeyIdentifier mean this ๐Ÿงš

subjectKeyIdentifier identifies the recipient's certificate by a key identifier ๐ŸŒธ For an X.509 certificate, the key identifier matches the X.509 subjectKeyIdentifier extension value ๐Ÿ’– For other certificate formats, the documents that specify the format and its use with CMS must include details on matching the key identifier to the right certificate field ๐Ÿ™ˆ

date is optional ๐Ÿ•ฐ๏ธ When it's present, it tells you which of the recipient's previously distributed UKMs the sender used ๐Ÿ“

other is optional ๐ŸŽ When it's present, it holds extra information the recipient uses to locate the public keying material the sender used ๐Ÿ’…


6.2.3. KEKRecipientInfo Type ๐Ÿ—๏ธ

this is for recipients who already have a previously distributed symmetric key-encryption key (KEK) ๐ŸŒˆ Each KEKRecipientInfo transfers the content-encryption key to one or more recipients who hold that key-encryption key ๐Ÿ’ซ

KEKRecipientInfo ::= SEQUENCE {
  version CMSVersion,  -- always set to 4
  kekid KEKIdentifier,
  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
  encryptedKey EncryptedKey }

KEKIdentifier ::= SEQUENCE {
  keyIdentifier OCTET STRING,
  date GeneralizedTime OPTIONAL,
  other OtherKeyAttribute OPTIONAL }

the fields of KEKRecipientInfo ๐Ÿ’…

version is the syntax version number ๐ŸŽ€ It MUST always be 4 ๐Ÿ“

kekid specifies a symmetric key-encryption key that was previously distributed to the sender and to one or more recipients ๐Ÿ”‘

keyEncryptionAlgorithm identifies the key-encryption algorithm, plus any associated parameters, used to encrypt the content-encryption key with the key-encryption key ๐Ÿฆ‹ The key-encryption process is described in Section 6.4 โœจ

encryptedKey is the result of encrypting the content-encryption key in the key-encryption key ๐Ÿ’–

the fields of KEKIdentifier ๐Ÿงš

keyIdentifier identifies the key-encryption key that was previously distributed to the sender and one or more recipients ๐ŸŒธ

date is optional ๐Ÿ•ฐ๏ธ When it's present, it specifies a single key-encryption key from a set that was previously distributed ๐Ÿ’œ

other is optional ๐ŸŽ When it's present, it holds extra information the recipient uses to determine which key-encryption key the sender used ๐Ÿซถ


6.2.4. PasswordRecipientInfo Type ๐Ÿ”

last one in this batch ๐Ÿฅณ Recipient information using a password or shared secret value is represented in PasswordRecipientInfo. Each instance transfers the content-encryption key to one or more recipients who possess that password or shared secret value ๐ŸŒˆ

The PasswordRecipientInfo Type is specified in RFC 3211 [PWRI]. The PasswordRecipientInfo structure is repeated here for completeness, so you don't have to go hunting ๐Ÿ’Œ

PasswordRecipientInfo ::= SEQUENCE {
  version CMSVersion,   -- Always set to 0
  keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier
                               OPTIONAL,
  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
  encryptedKey EncryptedKey }

the fields of PasswordRecipientInfo ๐Ÿ’…

version is the syntax version number ๐ŸŽ€ It MUST always be 0 ๐Ÿ“

keyDerivationAlgorithm identifies the key-derivation algorithm, plus any associated parameters, used to derive the key-encryption key from the password or shared secret value ๐Ÿฆข If this field is absent, the key-encryption key comes from an external source instead, like a hardware crypto token such as a smart card ๐Ÿ’ณ

keyEncryptionAlgorithm identifies the encryption algorithm, plus any associated parameters, used to encrypt the content-encryption key with the key-encryption key ๐ŸŒธ

encryptedKey is the result of encrypting the content-encryption key with the key-encryption key โœจ

6.2.5. OtherRecipientInfo Type ๐ŸŽ€

okay so recipient info for extra key management techniques lives in OtherRecipientInfo ๐Ÿงš it's giving future-proof! This type lets future documents specify key management techniques beyond key transport, key agreement, previously distributed key-encryption keys, and password-based key management. Each technique gets its own object identifier so it's uniquely identified โœจ

OtherRecipientInfo ::= SEQUENCE {
  oriType OBJECT IDENTIFIER,
  oriValue ANY DEFINED BY oriType }

The fields of type OtherRecipientInfo have the following meanings:

  • oriType identifies the key management technique.
  • oriValue contains the protocol data elements needed by a recipient using the identified key management technique.

6.3. Content-encryption Process ๐Ÿ”

first up, the content-encryption key for your chosen content-encryption algorithm gets randomly generated ๐ŸŽฒ then the data gets padded (more on that in a sec), and the padded data is encrypted with that key. Encryption maps an arbitrary string of octets (the data) to another string of octets (the ciphertext) under control of a content-encryption key. The encrypted data goes into the EnvelopedData encryptedContentInfo encryptedContent OCTET STRING ๐Ÿ“ฆ

some content-encryption algorithms assume the input length is a multiple of k octets, where k is greater than one. for those algorithms, the input shall be padded at the trailing end with k-(lth mod k) octets all having value k-(lth mod k), where lth is the length of the input. in other words, the input gets padded at the trailing end with one of these strings:

                 01 -- if lth mod k = k-1
              02 02 -- if lth mod k = k-2
                  .
                  .
                  .
        k k ... k k -- if lth mod k = 0

the padding can be removed unambiguously since all input is padded, including input values that are already a multiple of the block size, and no padding string is a suffix of another. ngl this padding method is only well defined if and only if k is less than 256 ๐Ÿ™ˆ

6.4. Key-encryption Process ๐Ÿ—๏ธ

the input to the key-encryption process (the value handed to the recipient's key-encryption algorithm) is just the "value" of the content-encryption key ๐Ÿ’•

any of the aforementioned key management techniques can be used for each recipient of the same encrypted content. so many options, bestie ๐ŸŒˆ

7. Digested-data Content Type ๐Ÿง

the digested-data content type is basically content of any type paired with a message digest of that content ๐Ÿ“

typically, the digested-data content type is used to provide content integrity, and the result generally becomes an input to the enveloped-data content type โœจ

here's how you build digested-data:

  1. A message digest is computed on the content with a message-digest algorithm.
  2. The message-digest algorithm and the message digest are collected together with the content into a DigestedData value.

a recipient verifies the message digest by comparing it to an independently computed message digest. main character energy ๐Ÿ’…

the following object identifier identifies the digested-data content type:

id-digestedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs7(7) 5 }

the digested-data content type shall have ASN.1 type DigestedData:

DigestedData ::= SEQUENCE {
  version CMSVersion,
  digestAlgorithm DigestAlgorithmIdentifier,
  encapContentInfo EncapsulatedContentInfo,
  digest Digest }

Digest ::= OCTET STRING

The fields of type DigestedData have the following meanings:

  • version is the syntax version number. If the encapsulated content type is id-data, then the value of version MUST be 0. however, if the encapsulated content type is other than id-data, then the value of version MUST be 2.
  • digestAlgorithm identifies the message digest algorithm, and any associated parameters, under which the content is digested. the message-digesting process is the same as in Section 5.4 in the case when there are no signed attributes.
  • encapContentInfo is the content that is digested, as defined in Section 5.2.
  • digest is the result of the message-digesting process.

the ordering of the digestAlgorithm field, the encapContentInfo field, and the digest field makes it possible to process a DigestedData value in a single pass. efficient and iconic ๐Ÿฆ‹

8. Encrypted-data Content Type ๐Ÿ”’

the encrypted-data content type is encrypted content of any type. unlike the enveloped-data content type, it has neither recipients nor encrypted content-encryption keys. keys MUST be managed by other means ๐Ÿ™ˆ

the typical use case is encrypting the content of the data content type for local storage, perhaps where the encryption key is derived from a password ๐Ÿ’œ

the following object identifier identifies the encrypted-data content type:

id-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 }

the encrypted-data content type shall have ASN.1 type EncryptedData:

EncryptedData ::= SEQUENCE {
  version CMSVersion,
  encryptedContentInfo EncryptedContentInfo,
  unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }

The fields of type EncryptedData have the following meanings:

  • version is the syntax version number. If unprotectedAttrs is present, then the version MUST be 2. If unprotectedAttrs is absent, then version MUST be 0.
  • encryptedContentInfo is the encrypted content information, as defined in Section 6.1.
  • unprotectedAttrs is a collection of attributes that are not encrypted. The field is optional. Useful attribute types are defined in Section 11.

9. Authenticated-data Content Type ๐Ÿ›ก๏ธ

the authenticated-data content type is content of any type, a message authentication code (MAC), and encrypted authentication keys for one or more recipients. the MAC plus one encrypted authentication key for a recipient is what that recipient needs to verify the integrity of the content. any type of content can be integrity protected for an arbitrary number of recipients ๐Ÿซถ

here's how authenticated-data gets built:

  1. A message-authentication key for a particular message-authentication algorithm is generated at random.
  2. The message-authentication key is encrypted for each recipient. the details of this encryption depend on the key management algorithm used.
  3. For each recipient, the encrypted message-authentication key and other recipient-specific information are collected into a RecipientInfo value, defined in Section 6.2.
  4. Using the message-authentication key, the originator computes a MAC value on the content. If the originator is authenticating any information in addition to the content (see Section 9.2), a message digest is calculated on the content, the message digest of the content and the other information are authenticated using the message-authentication key, and the result becomes the "MAC value".

9.1. AuthenticatedData Type ๐Ÿ’–

the following object identifier identifies the authenticated-data content type:

id-ct-authData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
   us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16)
   ct(1) 2 }

the authenticated-data content type shall have ASN.1 type AuthenticatedData:

AuthenticatedData ::= SEQUENCE {
  version CMSVersion,
  originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
  recipientInfos RecipientInfos,
  macAlgorithm MessageAuthenticationCodeAlgorithm,
  digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
  encapContentInfo EncapsulatedContentInfo,
  authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
  mac MessageAuthenticationCode,
  unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }

  AuthAttributes ::= SET SIZE (1..MAX) OF Attribute

  UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute

  MessageAuthenticationCode ::= OCTET STRING

The fields of type AuthenticatedData have the following meanings:

  • version is the syntax version number. The version MUST be assigned as follows:
IF (originatorInfo is present) AND
   ((any certificates with a type of other are present) OR
   (any crls with a type of other are present))
THEN version is 3
ELSE
   IF ((originatorInfo is present) AND
      (any version 2 attribute certificates are present))
   THEN version is 1
   ELSE version is 0
  • originatorInfo optionally provides information about the originator. It is present only if required by the key management algorithm. It MAY contain certificates, attribute certificates, and CRLs, as defined in Section 6.1.
  • recipientInfos is a collection of per-recipient information, as defined in Section 6.1. There MUST be at least one element in the collection.
  • macAlgorithm is a message authentication code (MAC) algorithm identifier. It identifies the MAC algorithm, along with any associated parameters, used by the originator. Placement of the macAlgorithm field facilitates one-pass processing by the recipient.
  • digestAlgorithm identifies the message digest algorithm, and any associated parameters, used to compute a message digest on the encapsulated content if authenticated attributes are present. The message digesting process is described in Section 9.2. Placement of the digestAlgorithm field facilitates one-pass processing by the recipient. If the digestAlgorithm field is present, then the authAttrs field MUST also be present.
  • encapContentInfo is the content that is authenticated, as defined in Section 5.2.
  • authAttrs is a collection of authenticated attributes. The authAttrs structure is optional, but it MUST be present if the content type of the EncapsulatedContentInfo value being authenticated is not id-data. If the authAttrs field is present, then the digestAlgorithm field MUST also be present. The AuthAttributes structure MUST be DER encoded, even if the rest of the structure is BER encoded. Useful attribute types are defined in Section 11. If the authAttrs field is present, it MUST contain, at a minimum, the following two attributes:
    • A content-type attribute having as its value the content type of the EncapsulatedContentInfo value being authenticated. Section 11.1 defines the content-type attribute.
    • A message-digest attribute, having as its value the message digest of the content. Section 11.2 defines the message-digest attribute.
  • mac is the message authentication code.
  • unauthAttrs is a collection of attributes that are not authenticated. The field is optional. To date, no attributes have been defined for use as unauthenticated attributes, but other useful attribute types are defined in Section 11.

9.2. MAC Generation ๐Ÿ”

okay so the MAC calculation process computes a message authentication code (MAC) on either the content being authenticated or a message digest of that content, together with the originator's authenticated attributes. the vibe is basically "lock it up tight" ๐Ÿ’…

if the authAttrs field is absent, the input to the MAC calculation process is the value of the encapContentInfo eContent OCTET STRING. only the octets that make up the value of that OCTET STRING go into the MAC algorithm. the tag and the length octets get left out ๐Ÿซถ this is a total win because the length of the content doesn't need to be known before the MAC generation process starts, bestie โœจ

if the authAttrs field is present, the content-type attribute (as described in Section 11.1) and the message-digest attribute (as described in Section 11.2) MUST be included. the input to the MAC calculation process is the DER encoding of authAttrs. the authAttrs field gets encoded a second time for the message digest calculation ๐Ÿฆ‹ here's the tricky part: the IMPLICIT [2] tag in the authAttrs field is NOT used for the DER encoding. an EXPLICIT SET OF tag is used instead. so for the message digest calculation, what goes in is the DER encoding of the SET OF tag (not the IMPLICIT [2] tag), along with the length and content octets of the authAttrs value. it's giving very precise, and it's so important to get right ๐Ÿ˜ญ

the message digest calculation process computes a message digest on the content being authenticated. the initial input is the "value" of the encapsulated content being authenticated. specifically, that's the encapContentInfo eContent OCTET STRING that the authentication process is applied to. only the octets making up the value of the encapContentInfo eContent OCTET STRING go into the message digest algorithm, and the tag and length octets stay out ๐ŸŒธ again, the upside is that the length of the content doesn't need to be known ahead of time. even though the tag and length octets of the eContent OCTET STRING aren't part of the message digest calculation, they're still protected by other means. the length octets are protected by the nature of the message digest algorithm, since it is computationally infeasible to find any two distinct contents of any length that have the same message digest. iconic protection, honestly ๐Ÿ‘‘

the input to the MAC calculation process includes the MAC input data (defined above) and an authentication key conveyed in a recipientInfo structure. the details of the MAC calculation depend on the MAC algorithm being used (for example, Hashed Message Authentication Code (HMAC)). the object identifier, along with any parameters, that specifies the MAC algorithm the originator used is carried in the macAlgorithm field. the MAC value the originator generates is encoded as an OCTET STRING and carried in the mac field ๐Ÿงš


9.3. MAC Verification ๐Ÿ•ต๏ธโ€โ™€๏ธ

the input to the MAC verification process includes the input data (which depends on whether the authAttrs field is present or absent, as defined in 9.2) and the authentication key conveyed in recipientInfo. the details of MAC verification depend on the MAC algorithm in use ๐Ÿ’œ

the recipient MUST NOT rely on any MAC values or message digest values computed by the originator. that's a HARD rule, bestie. recompute everything yourself ๐Ÿ™…โ€โ™€๏ธ

the content is authenticated as described in Section 9.2. if the originator includes authenticated attributes, then the content of the authAttrs is authenticated as described in Section 9.2 too. for authentication to succeed, the MAC value calculated by the recipient MUST be the same as the value of the mac field. similarly, for authentication to succeed when the authAttrs field is present, the content message digest value calculated by the recipient MUST be the same as the message digest value included in the authAttrs message-digest attribute. it's all about matching, matching, matching ๐ŸŽ€

if the AuthenticatedData includes authAttrs, then the content-type attribute value MUST match the AuthenticatedData encapContentInfo eContentType value ๐Ÿ“


10. Useful Types โœจ

this section is split into two parts. the first part defines the algorithm identifiers, and the second part defines other useful types ๐Ÿฆ‹

10.1. Algorithm Identifier Types ๐Ÿง

all of the algorithm identifiers have the same type: AlgorithmIdentifier. the definition of AlgorithmIdentifier is taken from X.509 [X.509-88]. so there are a LOT of alternatives for each algorithm type, which is very on-brand for cryptography ๐Ÿ’–

10.1.1. DigestAlgorithmIdentifier

the DigestAlgorithmIdentifier type identifies a message-digest algorithm. examples include SHA-1, MD2, and MD5. a message-digest algorithm maps an octet string (the content) to another octet string (the message digest) ๐ŸŒˆ

   DigestAlgorithmIdentifier ::= AlgorithmIdentifier

10.1.2. SignatureAlgorithmIdentifier

the SignatureAlgorithmIdentifier type identifies a signature algorithm, and it can also identify a message digest algorithm. examples include RSA, DSA, DSA with SHA-1, ECDSA, and ECDSA with SHA-256. a signature algorithm supports signature generation and verification operations. the signature generation operation uses the message digest and the signer's private key to generate a signature value. the signature verification operation uses the message digest and the signer's public key to determine whether or not a signature value is valid. context determines which operation is intended ๐Ÿ”ฅ

   SignatureAlgorithmIdentifier ::= AlgorithmIdentifier

10.1.3. KeyEncryptionAlgorithmIdentifier

the KeyEncryptionAlgorithmIdentifier type identifies a key-encryption algorithm used to encrypt a content-encryption key. the encryption operation maps an octet string (the key) to another octet string (the encrypted key) under control of a key-encryption key. the decryption operation is the inverse of the encryption operation. context determines which operation is intended ๐Ÿ”

the details of encryption and decryption depend on the key management algorithm being used. key transport, key agreement, previously distributed symmetric key-encrypting keys, and symmetric key-encrypting keys derived from passwords are all supported ๐Ÿซถ

   KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier

10.1.4. ContentEncryptionAlgorithmIdentifier

the ContentEncryptionAlgorithmIdentifier type identifies a content-encryption algorithm. examples include Triple-DES and RC2. a content-encryption algorithm supports encryption and decryption operations. the encryption operation maps an octet string (the plaintext) to another octet string (the ciphertext) under control of a content-encryption key. the decryption operation is the inverse of the encryption operation. context determines which operation is intended ๐Ÿ’…

   ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier

10.1.5. MessageAuthenticationCodeAlgorithm

the MessageAuthenticationCodeAlgorithm type identifies a message authentication code (MAC) algorithm. examples include DES-MAC and HMAC-SHA-1. a MAC algorithm supports generation and verification operations. the MAC generation and verification operations use the same symmetric key. context determines which operation is intended ๐ŸŒธ

   MessageAuthenticationCodeAlgorithm ::= AlgorithmIdentifier

10.1.6. KeyDerivationAlgorithmIdentifier

the KeyDerivationAlgorithmIdentifier type is specified in RFC 3211 [PWRI]. the KeyDerivationAlgorithmIdentifier definition gets repeated here for completeness ๐Ÿ“

key derivation algorithms convert a password or shared secret value into a key-encryption key ๐Ÿ”‘

   KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier

10.2. Other Useful Types ๐Ÿงฉ

this section defines types that are used in other places in the document. the types are not listed in any particular order, so no drama ๐Ÿ™ƒ

10.2.1. RevocationInfoChoices

the RevocationInfoChoices type gives a set of revocation status information alternatives. the set is meant to contain information that's sufficient to determine whether the certificates and attribute certificates it's associated with are revoked. however, there MAY be more revocation status information than necessary, or there MAY be less revocation status information than necessary. X.509 Certificate revocation lists (CRLs) [X.509-97] are the primary source of revocation status information, but any other revocation information format can be supported. the OtherRevocationInfoFormat alternative is there to support any other revocation information format without further modifications to the CMS. for example, Online Certificate Status Protocol (OCSP) Responses [OCSP] can be supported using the OtherRevocationInfoFormat ๐Ÿ›ก๏ธ

the CertificateList may contain a CRL, an Authority Revocation List (ARL), a Delta CRL, or an Attribute Certificate Revocation List. all of these lists share a common syntax ๐Ÿ’œ

the CertificateList type gives a certificate revocation list (CRL). CRLs are specified in X.509 [X.509-97], and they're profiled for use on the Internet in RFC 5280 [PROFILE] โœจ

the definition of CertificateList is taken from X.509 ๐ŸŽ€

   RevocationInfoChoices ::= SET OF RevocationInfoChoice

   RevocationInfoChoice ::= CHOICE {
     crl CertificateList,
     other [1] IMPLICIT OtherRevocationInfoFormat }

   OtherRevocationInfoFormat ::= SEQUENCE {
     otherRevInfoFormat OBJECT IDENTIFIER,
     otherRevInfo ANY DEFINED BY otherRevInfoFormat }

10.2.2. CertificateChoices

the CertificateChoices type gives either a PKCS #6 extended certificate [PKCS#6], an X.509 certificate, a version 1 X.509 attribute certificate (ACv1) [X.509-97], a version 2 X.509 attribute certificate (ACv2) [X.509-00], or any other certificate format. the PKCS #6 extended certificate is obsolete. the PKCS #6 certificate is included for backward compatibility, and PKCS #6 certificates SHOULD NOT be used. the ACv1 is also obsolete. ACv1 is included for backward compatibility, and ACv1 SHOULD NOT be used. the Internet profile of X.509 certificates is specified in the "Internet X.509 Public Key Infrastructure: Certificate and CRL Profile" [PROFILE]. the Internet profile of ACv2 is specified in the "An Internet Attribute Certificate Profile for Authorization" [ACPROFILE]. the OtherCertificateFormat alternative is there to support any other certificate format without further modifications to the CMS ๐ŸŒท

the definition of Certificate is taken from X.509 ๐Ÿ“œ

the definitions of AttributeCertificate are taken from X.509-1997 and X.509-2000. the definition from X.509-1997 is assigned to AttributeCertificateV1 (see Section 12.2), and the definition from X.509-2000 is assigned to AttributeCertificateV2 ๐Ÿฆ‹

   CertificateChoices ::= CHOICE {
    certificate Certificate,
    extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete
    v1AttrCert [1] IMPLICIT AttributeCertificateV1,       -- Obsolete
    v2AttrCert [2] IMPLICIT AttributeCertificateV2,
    other [3] IMPLICIT OtherCertificateFormat }

   OtherCertificateFormat ::= SEQUENCE {
     otherCertFormat OBJECT IDENTIFIER,
     otherCert ANY DEFINED BY otherCertFormat }

10.2.3. CertificateSet

Okay, so CertificateSet is basically a bag of certs ๐ŸŽ’โœจ. The whole point is that the set should have enough certificates to build certification paths from a recognized "root" or "top-level certification authority" down to every sender certificate it's linked to. It's totally fine to have extra certs, or there MAY be fewer than necessary ๐Ÿ™ˆ

What a "certification path" actually means is outside the scope of this doc, but [PROFILE] defines it for X.509 certificates. Some apps put a cap on how long a path can be, and others enforce certain relationships between the subjects and issuers in a path. Very picky, very iconic ๐Ÿ’…

CertificateSet ::= SET OF CertificateChoices

10.2.4. IssuerAndSerialNumber

IssuerAndSerialNumber is how you identify a certificate, and through it an entity and a public key ๐Ÿ”‘. You do it with the distinguished name of the certificate issuer plus a serial number that the issuer picked for that cert.

The Name definition comes from X.501 [X.501-88], and CertificateSerialNumber comes from X.509 [X.509-97] ๐Ÿ“š

IssuerAndSerialNumber ::= SEQUENCE {
  issuer Name,
  serialNumber CertificateSerialNumber }

CertificateSerialNumber ::= INTEGER

10.2.5. CMSVersion

CMSVersion is the syntax version number. It's there so things stay compatible with future revisions of this spec ๐Ÿฆ‹

CMSVersion ::= INTEGER
               { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }

10.2.6. UserKeyingMaterial

UserKeyingMaterial (UKM) gives us a syntax for user keying material ๐ŸŒธ Some key agreement algorithms need a UKM so that a different key gets generated every time the same two parties make a pairwise key. The sender supplies a UKM for use with one specific key agreement algorithm.

UserKeyingMaterial ::= OCTET STRING

10.2.7. OtherKeyAttribute

OtherKeyAttribute is the syntax for including other key attributes that let the recipient pick the key the sender used ๐Ÿงš The attribute object identifier must be registered along with the syntax of the attribute itself. Use of this structure should be avoided since it might impede interoperability. Be careful with this one, bestie ๐Ÿ’€

OtherKeyAttribute ::= SEQUENCE {
  keyAttrId OBJECT IDENTIFIER,
  keyAttr ANY DEFINED BY keyAttrId OPTIONAL }

11. Useful Attributes

This section defines attributes that can be used with signed-data, enveloped-data, encrypted-data, or authenticated-data ๐Ÿ’– The syntax of Attribute is compatible with X.501 [X.501-88] and RFC 5280 [PROFILE]. Some of these attributes were originally defined in PKCS #9 [PKCS#9], and others came from an earlier version of this spec [CMS1]. The attributes aren't listed in any particular order, so no drama ๐Ÿ™ƒ

Loads more attributes are defined elsewhere, especially the S/MIME Version 3.1 Message Specification [MSG3.1] and the Enhanced Security Services for S/MIME [ESS]. Those also have recommendations on where these attributes should go โœจ

11.1. Content Type

The content-type attribute type says what content type the ContentInfo is within signed-data or authenticated-data ๐ŸŽ€ The content-type attribute type MUST be present whenever signed attributes are present in signed-data, or authenticated attributes are present in authenticated-data. The content-type attribute value MUST match the encapContentInfo eContentType value in the signed-data or authenticated-data.

The content-type attribute MUST be a signed attribute or an authenticated attribute. It MUST NOT be an unsigned attribute, unauthenticated attribute, or unprotected attribute. Non-negotiable, periodt ๐Ÿ’…

Here's the object identifier for the content-type attribute:

id-contentType OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) 3 }

Content-type attribute values have the ASN.1 type ContentType:

ContentType ::= OBJECT IDENTIFIER

Even though the syntax is defined as a SET OF AttributeValue, a content-type attribute MUST have a single attribute value. Zero or multiple instances of AttributeValue are not permitted ๐Ÿ™…โ€โ™€๏ธ

SignedAttributes and AuthAttributes are each defined as a SET OF Attributes. The SignedAttributes in a signerInfo MUST NOT include multiple instances of the content-type attribute. Same goes for the AuthAttributes in an AuthenticatedData: they MUST NOT include multiple instances of the content-type attribute ๐Ÿซถ

11.2. Message Digest

The message-digest attribute type says what the message digest is of the encapContentInfo eContent OCTET STRING being signed in signed-data (see Section 5.4) or authenticated in authenticated-data (see Section 9.2) ๐ŸŒˆ For signed-data, the digest is computed with the signer's message digest algorithm. For authenticated-data, it's computed with the originator's message digest algorithm.

Within signed-data, the message-digest signed attribute type MUST be present when there are any signed attributes present. Within authenticated-data, the message-digest authenticated attribute type MUST be present when there are any authenticated attributes present.

The message-digest attribute MUST be a signed attribute or an authenticated attribute. It MUST NOT be an unsigned attribute, unauthenticated attribute, or unprotected attribute. Lowkey the most protected attribute in the whole group chat ๐Ÿ”ฅ

Here's the object identifier for the message-digest attribute:

id-messageDigest OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) 4 }

Message-digest attribute values have the ASN.1 type MessageDigest:

MessageDigest ::= OCTET STRING

A message-digest attribute MUST have a single attribute value, even though the syntax is defined as a SET OF AttributeValue. There MUST NOT be zero or multiple instances of AttributeValue present.

SignedAttributes and AuthAttributes are each defined as a SET OF Attributes. The SignedAttributes in a signerInfo MUST include only one instance of the message-digest attribute. Similarly, the AuthAttributes in an AuthenticatedData MUST include only one instance of the message-digest attribute ๐Ÿ‘‘

11.3. Signing Time

The signing-time attribute type says when the signer (allegedly) did the signing process โฐ It's meant for use in signed-data.

The signing-time attribute MUST be a signed attribute or an authenticated attribute. It MUST NOT be an unsigned attribute, unauthenticated attribute, or unprotected attribute.

Here's the object identifier for the signing-time attribute:

id-signingTime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) 5 }

Signing-time attribute values have the ASN.1 type SigningTime:

SigningTime ::= Time

Time ::= CHOICE {
  utcTime UTCTime,
  generalizedTime GeneralizedTime }

Note: The definition of Time matches the one in the 1997 version of X.509 [X.509-97] ๐Ÿ“–

Dates between 1 January 1950 and 31 December 2049 (inclusive) MUST be encoded as UTCTime. Any dates with year values before 1950 or after 2049 MUST be encoded as GeneralizedTime ๐Ÿ•ฐ๏ธ

UTCTime values MUST be expressed in Coordinated Universal Time (formerly known as Greenwich Mean Time (GMT) and Zulu clock time). They MUST include seconds, even where the number of seconds is zero, so times look like YYMMDDHHMMSSZ. Midnight MUST be represented as "YYMMDD000000Z". Century info is implicit, and the century MUST be worked out like this:

Where YY is greater than or equal to 50, the year MUST be interpreted as 19YY; and Where YY is less than 50, the year MUST be interpreted as 20YY.

GeneralizedTime values MUST be expressed in Coordinated Universal Time and MUST include seconds, even where the number of seconds is zero. Times look like YYYYMMDDHHMMSSZ. GeneralizedTime values MUST NOT include fractional seconds ๐Ÿšซโฑ๏ธ

A signing-time attribute MUST have a single attribute value, even though the syntax is defined as a SET OF AttributeValue. There MUST NOT be zero or multiple instances of AttributeValue present.

SignedAttributes and AuthAttributes are each defined as a SET OF Attributes. The SignedAttributes in a signerInfo MUST NOT include multiple instances of the signing-time attribute. Similarly, the AuthAttributes in an AuthenticatedData MUST NOT include multiple instances of the signing-time attribute.

No requirement is imposed on whether the signing time is correct, and whether a claimed signing time gets accepted is up to the recipient's discretion ๐Ÿคทโ€โ™€๏ธ It is expected, though, that some signers, like time-stamp servers, will be trusted implicitly โญ

11.4. Countersignature

The countersignature attribute type specifies one or more signatures over the contents octets of the signature OCTET STRING in a SignerInfo value of the signed-data ๐Ÿชž So the message digest is computed over the octets making up the value of that OCTET STRING, and the tag and length octets are NOT included. That means the countersignature attribute type countersigns (signs in serial) another signature. Signature inception, literally ๐ŸŒ€

The countersignature attribute MUST be an unsigned attribute. It MUST NOT be a signed attribute, an authenticated attribute, an unauthenticated attribute, or an unprotected attribute.

Here's the object identifier for the countersignature attribute:

id-countersignature OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) 6 }

Countersignature attribute values have the ASN.1 type Countersignature:

Countersignature ::= SignerInfo

Countersignature values mean the same thing as SignerInfo values for ordinary signatures, with a few differences:

  1. The signedAttributes field MUST NOT contain a content-type attribute. There is no content type for countersignatures.
  2. The signedAttributes field MUST contain a message-digest attribute if it contains any other attributes.
  3. The input to the message-digesting process is the contents octets of the DER encoding of the signatureValue field of the SignerInfo value that the attribute is associated with.

A countersignature attribute can have multiple attribute values. The syntax is defined as a SET OF AttributeValue, and there MUST be one or more instances of AttributeValue present.

UnsignedAttributes is defined as a SET OF Attributes. The UnsignedAttributes in a signerInfo may include multiple instances of the countersignature attribute ๐Ÿ’œ

A countersignature, since it has type SignerInfo, can have a countersignature attribute of its own. So it's possible to build an arbitrarily long series of countersignatures ๐ŸŒ€๐ŸŒ€๐ŸŒ€

12. ASN.1 Modules

Section 12.1 has the ASN.1 module for the CMS, and Section 12.2 has the ASN.1 module for the Version 1 Attribute Certificate ๐ŸŽ€

12.1. CMS ASN.1 Module ๐ŸŽ€

okay bestie, this is the big one: the whole CMS ASN.1 module, the typed blueprint every message is built from ๐Ÿงš it's all in one block, exactly as written, so you can copy it straight into your parser ๐Ÿ’…

CryptographicMessageSyntax2004
  { iso(1) member-body(2) us(840) rsadsi(113549)
    pkcs(1) pkcs-9(9) smime(16) modules(0) cms-2004(24) }

DEFINITIONS IMPLICIT TAGS ::=
BEGIN

-- EXPORTS All
-- The types and values defined in this module are exported for use
-- in the other ASN.1 modules.  Other applications may use them for
-- their own purposes.

IMPORTS

  -- Imports from RFC 5280 [PROFILE], Appendix A.1
        AlgorithmIdentifier, Certificate, CertificateList,
        CertificateSerialNumber, Name
           FROM PKIX1Explicit88
                { iso(1) identified-organization(3) dod(6)
                  internet(1) security(5) mechanisms(5) pkix(7)
                  mod(0) pkix1-explicit(18) }

  -- Imports from RFC 3281 [ACPROFILE], Appendix B
        AttributeCertificate
           FROM PKIXAttributeCertificate
                { iso(1) identified-organization(3) dod(6)
                  internet(1) security(5) mechanisms(5) pkix(7)
                  mod(0) attribute-cert(12) }

  -- Imports from Appendix B of this document
        AttributeCertificateV1
           FROM AttributeCertificateVersion1
                { iso(1) member-body(2) us(840) rsadsi(113549)
                  pkcs(1) pkcs-9(9) smime(16) modules(0)
                  v1AttrCert(15) } ;

-- Cryptographic Message Syntax

ContentInfo ::= SEQUENCE {
  contentType ContentType,
  content [0] EXPLICIT ANY DEFINED BY contentType }

ContentType ::= OBJECT IDENTIFIER
SignedData ::= SEQUENCE {
  version CMSVersion,
  digestAlgorithms DigestAlgorithmIdentifiers,
  encapContentInfo EncapsulatedContentInfo,
  certificates [0] IMPLICIT CertificateSet OPTIONAL,
  crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
  signerInfos SignerInfos }

DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier

SignerInfos ::= SET OF SignerInfo

EncapsulatedContentInfo ::= SEQUENCE {
  eContentType ContentType,
  eContent [0] EXPLICIT OCTET STRING OPTIONAL }

SignerInfo ::= SEQUENCE {
  version CMSVersion,
  sid SignerIdentifier,
  digestAlgorithm DigestAlgorithmIdentifier,
  signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
  signatureAlgorithm SignatureAlgorithmIdentifier,
  signature SignatureValue,
  unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }

SignerIdentifier ::= CHOICE {
  issuerAndSerialNumber IssuerAndSerialNumber,
  subjectKeyIdentifier [0] SubjectKeyIdentifier }

SignedAttributes ::= SET SIZE (1..MAX) OF Attribute

UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute

Attribute ::= SEQUENCE {
  attrType OBJECT IDENTIFIER,
  attrValues SET OF AttributeValue }

AttributeValue ::= ANY

SignatureValue ::= OCTET STRING

EnvelopedData ::= SEQUENCE {
  version CMSVersion,
  originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
  recipientInfos RecipientInfos,
  encryptedContentInfo EncryptedContentInfo,
  unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }

OriginatorInfo ::= SEQUENCE {
  certs [0] IMPLICIT CertificateSet OPTIONAL,
  crls [1] IMPLICIT RevocationInfoChoices OPTIONAL }

RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo

EncryptedContentInfo ::= SEQUENCE {
  contentType ContentType,
  contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
  encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }

EncryptedContent ::= OCTET STRING

UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute

RecipientInfo ::= CHOICE {
  ktri KeyTransRecipientInfo,
  kari [1] KeyAgreeRecipientInfo,
  kekri [2] KEKRecipientInfo,
  pwri [3] PasswordRecipientInfo,
  ori [4] OtherRecipientInfo }

EncryptedKey ::= OCTET STRING

KeyTransRecipientInfo ::= SEQUENCE {
  version CMSVersion,  -- always set to 0 or 2
  rid RecipientIdentifier,
  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
  encryptedKey EncryptedKey }

RecipientIdentifier ::= CHOICE {
  issuerAndSerialNumber IssuerAndSerialNumber,
  subjectKeyIdentifier [0] SubjectKeyIdentifier }

KeyAgreeRecipientInfo ::= SEQUENCE {
  version CMSVersion,  -- always set to 3
  originator [0] EXPLICIT OriginatorIdentifierOrKey,
  ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
  recipientEncryptedKeys RecipientEncryptedKeys }

OriginatorIdentifierOrKey ::= CHOICE {
  issuerAndSerialNumber IssuerAndSerialNumber,
  subjectKeyIdentifier [0] SubjectKeyIdentifier,
  originatorKey [1] OriginatorPublicKey }

OriginatorPublicKey ::= SEQUENCE {
  algorithm AlgorithmIdentifier,
  publicKey BIT STRING }

RecipientEncryptedKeys ::= SEQUENCE OF RecipientEncryptedKey

RecipientEncryptedKey ::= SEQUENCE {
  rid KeyAgreeRecipientIdentifier,
  encryptedKey EncryptedKey }

KeyAgreeRecipientIdentifier ::= CHOICE {
  issuerAndSerialNumber IssuerAndSerialNumber,
  rKeyId [0] IMPLICIT RecipientKeyIdentifier }

RecipientKeyIdentifier ::= SEQUENCE {
  subjectKeyIdentifier SubjectKeyIdentifier,
  date GeneralizedTime OPTIONAL,
  other OtherKeyAttribute OPTIONAL }

SubjectKeyIdentifier ::= OCTET STRING

KEKRecipientInfo ::= SEQUENCE {
  version CMSVersion,  -- always set to 4
  kekid KEKIdentifier,
  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
  encryptedKey EncryptedKey }

KEKIdentifier ::= SEQUENCE {
  keyIdentifier OCTET STRING,
  date GeneralizedTime OPTIONAL,
  other OtherKeyAttribute OPTIONAL }

PasswordRecipientInfo ::= SEQUENCE {
  version CMSVersion,   -- always set to 0
  keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier
                             OPTIONAL,
  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
  encryptedKey EncryptedKey }

OtherRecipientInfo ::= SEQUENCE {
  oriType OBJECT IDENTIFIER,
  oriValue ANY DEFINED BY oriType }

DigestedData ::= SEQUENCE {
  version CMSVersion,
  digestAlgorithm DigestAlgorithmIdentifier,
  encapContentInfo EncapsulatedContentInfo,
  digest Digest }

Digest ::= OCTET STRING

EncryptedData ::= SEQUENCE {
  version CMSVersion,
  encryptedContentInfo EncryptedContentInfo,
  unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }

AuthenticatedData ::= SEQUENCE {
  version CMSVersion,
  originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
  recipientInfos RecipientInfos,
  macAlgorithm MessageAuthenticationCodeAlgorithm,
  digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
  encapContentInfo EncapsulatedContentInfo,
  authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
  mac MessageAuthenticationCode,
  unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }

AuthAttributes ::= SET SIZE (1..MAX) OF Attribute

UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute

MessageAuthenticationCode ::= OCTET STRING

DigestAlgorithmIdentifier ::= AlgorithmIdentifier

SignatureAlgorithmIdentifier ::= AlgorithmIdentifier

KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier

ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier

MessageAuthenticationCodeAlgorithm ::= AlgorithmIdentifier

KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier

RevocationInfoChoices ::= SET OF RevocationInfoChoice

RevocationInfoChoice ::= CHOICE {
  crl CertificateList,
  other [1] IMPLICIT OtherRevocationInfoFormat }

OtherRevocationInfoFormat ::= SEQUENCE {
  otherRevInfoFormat OBJECT IDENTIFIER,
  otherRevInfo ANY DEFINED BY otherRevInfoFormat }

CertificateChoices ::= CHOICE {
  certificate Certificate,
  extendedCertificate [0] IMPLICIT ExtendedCertificate,  -- Obsolete
  v1AttrCert [1] IMPLICIT AttributeCertificateV1,        -- Obsolete
  v2AttrCert [2] IMPLICIT AttributeCertificateV2,
  other [3] IMPLICIT OtherCertificateFormat }

AttributeCertificateV2 ::= AttributeCertificate

OtherCertificateFormat ::= SEQUENCE {
  otherCertFormat OBJECT IDENTIFIER,
  otherCert ANY DEFINED BY otherCertFormat }

CertificateSet ::= SET OF CertificateChoices

IssuerAndSerialNumber ::= SEQUENCE {
  issuer Name,
  serialNumber CertificateSerialNumber }

CMSVersion ::= INTEGER  { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }

UserKeyingMaterial ::= OCTET STRING

OtherKeyAttribute ::= SEQUENCE {
  keyAttrId OBJECT IDENTIFIER,
  keyAttr ANY DEFINED BY keyAttrId OPTIONAL }

-- Content Type Object Identifiers

id-ct-contentInfo OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) smime(16) ct(1) 6 }

id-data OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 }

id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }

id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 }

id-digestedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs7(7) 5 }

id-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 }

id-ct-authData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1) 2 }

-- The CMS Attributes

MessageDigest ::= OCTET STRING

SigningTime  ::= Time

Time ::= CHOICE {
  utcTime UTCTime,
  generalTime GeneralizedTime }

Countersignature ::= SignerInfo

-- Attribute Object Identifiers

id-contentType OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) 3 }

id-messageDigest OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) 4 }

id-signingTime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) 5 }

id-countersignature OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs9(9) 6 }

-- Obsolete Extended Certificate syntax from PKCS #6

ExtendedCertificateOrCertificate ::= CHOICE {
  certificate Certificate,
  extendedCertificate [0] IMPLICIT ExtendedCertificate }

ExtendedCertificate ::= SEQUENCE {
  extendedCertificateInfo ExtendedCertificateInfo,
  signatureAlgorithm SignatureAlgorithmIdentifier,
  signature Signature }

ExtendedCertificateInfo ::= SEQUENCE {
  version CMSVersion,
  certificate Certificate,
  attributes UnauthAttributes }

Signature ::= BIT STRING

END -- of CryptographicMessageSyntax2004

12.2. Version 1 Attribute Certificate ASN.1 Module โœจ

Okay bestie, this is the old-school version 1 attribute certificate module ๐ŸŽ€ It's written in ASN.1, so the code block below has to stay exactly as it is. Every character matters ๐Ÿ’…

AttributeCertificateVersion1
    { iso(1) member-body(2) us(840) rsadsi(113549)
      pkcs(1) pkcs-9(9) smime(16) modules(0) v1AttrCert(15) }

DEFINITIONS EXPLICIT TAGS ::=
BEGIN

-- EXPORTS All

IMPORTS

  -- Imports from RFC 5280 [PROFILE], Appendix A.1
        AlgorithmIdentifier, Attribute, CertificateSerialNumber,
        Extensions, UniqueIdentifier
           FROM PKIX1Explicit88
                { iso(1) identified-organization(3) dod(6)
                  internet(1) security(5) mechanisms(5) pkix(7)
                  mod(0) pkix1-explicit(18) }

  -- Imports from RFC 5280 [PROFILE], Appendix A.2
        GeneralNames
           FROM PKIX1Implicit88
                { iso(1) identified-organization(3) dod(6)
                  internet(1) security(5) mechanisms(5) pkix(7)
                  mod(0) pkix1-implicit(19) }

  -- Imports from RFC 3281 [ACPROFILE], Appendix B
        AttCertValidityPeriod, IssuerSerial
           FROM PKIXAttributeCertificate
                { iso(1) identified-organization(3) dod(6)
                  internet(1) security(5) mechanisms(5) pkix(7)
                  mod(0) attribute-cert(12) } ;

-- Definition extracted from X.509-1997 [X.509-97], but
-- different type names are used to avoid collisions.

AttributeCertificateV1 ::= SEQUENCE {
  acInfo AttributeCertificateInfoV1,
  signatureAlgorithm AlgorithmIdentifier,
  signature BIT STRING }

AttributeCertificateInfoV1 ::= SEQUENCE {
  version AttCertVersionV1 DEFAULT v1,
  subject CHOICE {
    baseCertificateID [0] IssuerSerial,
      -- associated with a Public Key Certificate
    subjectName [1] GeneralNames },
      -- associated with a name
  issuer GeneralNames,
  signature AlgorithmIdentifier,
  serialNumber CertificateSerialNumber,
  attCertValidityPeriod AttCertValidityPeriod,
  attributes SEQUENCE OF Attribute,
  issuerUniqueID UniqueIdentifier OPTIONAL,
  extensions Extensions OPTIONAL }

AttCertVersionV1 ::= INTEGER { v1(0) }

END -- of AttributeCertificateVersion1

13. References ๐Ÿ“š

13.1. Normative References ๐Ÿ”’

These are the must-read ones, the ones the spec leans on ๐Ÿซถ

  • [ACPROFILE] Farrell, S. and R. Housley, "An Internet Attribute Certificate Profile for Authorization", RFC 3281, April 2002.
  • [PROFILE] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and W. Polk, "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile", RFC 5280, May 2008.
  • [STDWORDS] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
  • [X.208-88] CCITT. Recommendation X.208: Specification of Abstract Syntax Notation One (ASN.1), 1988.
  • [X.209-88] CCITT. Recommendation X.209: Specification of Basic Encoding Rules for Abstract Syntax Notation One (ASN.1), 1988.
  • [X.501-88] CCITT. Recommendation X.501: The Directory - Models, 1988.
  • [X.509-88] CCITT. Recommendation X.509: The Directory - Authentication Framework, 1988.
  • [X.509-97] ITU-T. Recommendation X.509: The Directory - Authentication Framework, 1997.
  • [X.509-00] ITU-T. Recommendation X.509: The Directory - Authentication Framework, 2000.

13.2. Informative References ๐Ÿ’œ

These are the extra reading ones. Super helpful, not strictly required ๐ŸŒธ

  • [CMS1] Housley, R., "Cryptographic Message Syntax", RFC 2630, June 1999.
  • [CMS2] Housley, R., "Cryptographic Message Syntax (CMS)", RFC 3369, August 2002.
  • [CMS3] Housley, R., "Cryptographic Message Syntax (CMS)", RFC 3852, July 2004.
  • [CMSALG] Housley, R., "Cryptographic Message Syntax (CMS) Algorithms", RFC 3370, August 2002.
  • [CMSMSIG] Housley, R., "Cryptographic Message Syntax (CMS) Multiple Signer Clarification", RFC 4853, April 2007.
  • [DH-X9.42] Rescorla, E., "Diffie-Hellman Key Agreement Method", RFC 2631, June 1999.
  • [ESS] Hoffman, P., Ed., "Enhanced Security Services for S/MIME", RFC 2634, June 1999.
  • [MSAC] Microsoft Development Network (MSDN) Library, "Authenticode", April 2004 Release.
  • [MSG2] Dusse, S., Hoffman, P., Ramsdell, B., Lundblade, L., and L. Repka, "S/MIME Version 2 Message Specification", RFC 2311, March 1998.
  • [MSG3] Ramsdell, B., Ed., "S/MIME Version 3 Message Specification", RFC 2633, June 1999.
  • [MSG3.1] Ramsdell, B., Ed., "Secure/Multipurpose Internet Mail Extensions (S/MIME) Version 3.1 Message Specification", RFC 3851, July 2004.
  • [NEWPKCS#1] Kaliski, B. and J. Staddon, "PKCS #1: RSA Cryptography Specifications Version 2.0", RFC 2437, October 1998.
  • [OCSP] Myers, M., Ankney, R., Malpani, A., Galperin, S., and C. Adams, "X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP", RFC 2560, June 1999.
  • [PKCS#1] Kaliski, B., "PKCS #1: RSA Encryption Version 1.5", RFC 2313, March 1998.
  • [PKCS#6] RSA Laboratories. PKCS #6: Extended-Certificate Syntax Standard, Version 1.5. November 1993.
  • [PKCS#7] Kaliski, B., "PKCS #7: Cryptographic Message Syntax Version 1.5", RFC 2315, March 1998.
  • [PKCS#9] RSA Laboratories. PKCS #9: Selected Attribute Types, Version 1.1. November 1993.
  • [PWRI] Gutmann, P., "Password-based Encryption for CMS", RFC 3211, December 2001.
  • [RANDOM] Eastlake, D., 3rd, Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, June 2005.

14. Security Considerations ๐Ÿšจ

Okay, this part is seriously important, so please actually read it ๐Ÿ˜ญ CMS gives you a way to digitally sign, digest, encrypt, and authenticate data. Lots of power, lots of responsibility ๐Ÿ‘‘

Implementations must protect the signer's private key. If someone gets the signer's private key, they can totally pretend to be that signer. Masquerade central ๐Ÿ’€

Implementations must protect the key management private key, the key-encryption key, and the content-encryption key. If the key management private key or the key-encryption key leaks, everything protected with that key can be disclosed. Same vibe for the content-encryption key: if it leaks, the encrypted content it covers can be exposed ๐Ÿ™ˆ

Implementations must protect the key management private key and the message-authentication key. If the key management private key leaks, authenticated data can be masqueraded. And if the message-authentication key leaks, someone can change the authenticated content without anyone noticing โœจ

The key management technique used to hand out message-authentication keys has to provide data origin authentication too. Otherwise the content shows up with integrity but from an unknown source, which is not it ๐Ÿฅด RSA [PKCS#1] [NEWPKCS#1] doesn't provide the needed data origin authentication, and neither does Ephemeral-Static Diffie-Hellman [DH-X9.42]. Static-Static Diffie-Hellman [DH-X9.42] does provide it, but only when both the originator and recipient public keys are bound to appropriate identities in X.509 certificates ๐Ÿฆ‹

When more than two parties share the same message-authentication key, data origin authentication is not provided. Anyone who knows that key can compute a valid MAC, so the content could have come from any one of them. Sharing with a whole group is a vibe-check fail ๐Ÿ˜ฌ

Implementations must randomly generate content-encryption keys, message-authentication keys, initialization vectors (IVs), and padding. Key pair generation also depends on random numbers. If you use weak pseudo-random number generators (PRNGs) to make keys, you can end up with little or no security. An attacker may find it way easier to recreate the PRNG environment that made the keys and search that small set of possibilities, instead of brute forcing the whole key space. Making quality random numbers is genuinely hard. RFC 4086 [RANDOM] has important guidance on this ๐ŸŽฒ

When you use key-agreement algorithms or previously distributed symmetric key-encryption keys, a key-encryption key is used to encrypt the content-encryption key. If the key-encryption algorithm and the content-encryption algorithm are different, your effective security is set by the weaker of the two. For example, say content is encrypted with Triple-DES using a 168-bit Triple-DES content-encryption key, but that key is wrapped with RC2 using a 40-bit RC2 key-encryption key. Then you only get at most 40 bits of protection. A trivial search can find the 40-bit RC2 key, which recovers the Triple-DES key, and then the Triple-DES key decrypts the content. Womp womp ๐Ÿ˜ญ So implementers must make sure key-encryption algorithms are as strong as or stronger than content-encryption algorithms.

Implementers should know that cryptographic algorithms get weaker over time. As new cryptanalysis techniques show up and computers get faster, the work factor to break an algorithm goes down. So algorithm implementations should be modular, letting new algorithms get dropped in easily. In other words, implementers should be ready for the set of algorithms they must support to change over time ๐Ÿ”„

The countersignature unsigned attribute includes a digital signature computed over the content signature value. That means the countersigning process doesn't need to know the original signed content, which is great for efficiency. But it also means someone could countersign an inappropriate signature value. So implementations that perform countersignatures should either verify the original signature value before countersigning it (this verification needs the original content to be processed), or they should countersign in a context that makes sure only appropriate signature values get countersigned ๐Ÿง


15. Acknowledgments ๐Ÿซถ

This document is the result of a bunch of professionals putting in the work ๐Ÿ’– The author is grateful for the hard work of every member of the IETF S/MIME Working Group, and gives a special thanks to Rich Ankney, Simon Blake-Wilson, Tim Dean, Steve Dusse, Carl Ellison, Peter Gutmann, Bob Jueneman, Stephen Henson, Paul Hoffman, Scott Hollenbeck, Don Johnson, Burt Kaliski, John Linn, John Pawling, Blake Ramsdell, Francois Rousseau, Jim Schaad, Dave Solo, Paul Timmel, and Sean Turner for their efforts and support ๐ŸŒˆ

The author also thanks Tim Polk for his encouragement in moving this specification along the standards maturity ladder โญ And thanks to Jan Vilhuber for the careful reading that resulted in RFC Errata 1744 ๐Ÿ”ฅ

Author's Address ๐Ÿ–‹๏ธ

Russell Housley
Vigil Security, LLC
918 Spring Knoll Drive
Herndon, VA 20170
USA
EMail: housley@vigilsec.com
๐Ÿ“ฆ where this page comes from