RFC 5652
Cryptographic Message Syntax (CMS)
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 ๐
Copyright and License Notice ๐ง
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 ๐
contentTypetells 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 ๐ขcontentis the associated content. its type can be figured out uniquely fromcontentType. 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 ๐ช
- 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."
- for each signer, the message digest gets digitally signed using the signer's private key ๐
- 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 ๐
- 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 ๐ธ
versionis the syntax version number ๐ข If theSignerIdentifieris the CHOICEissuerAndSerialNumber, then the version MUST be 1. If theSignerIdentifierissubjectKeyIdentifier, then the version MUST be 3. no cap, that's a hard rule ๐sidspecifies 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 ๐ซถSignerIdentifiergives you two options for pointing at the public key. TheissuerAndSerialNumberoption identifies the certificate by the issuer's distinguished name and the certificate serial number. ThesubjectKeyIdentifieroption identifies the certificate by a key identifier. When an X.509 certificate is referenced, the key identifier matches the X.509subjectKeyIdentifierextension 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 theissuerAndSerialNumberandsubjectKeyIdentifierforms ofSignerIdentifier๐ When generating aSignerIdentifier, implementations MAY support one of the forms (eitherissuerAndSerialNumberorsubjectKeyIdentifier) and always use it, or implementations MAY arbitrarily mix the two forms. However,subjectKeyIdentifierMUST be used to refer to a public key contained in a non-X.509 certificate โจdigestAlgorithmidentifies 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 thedigestAlgorithmsfield of the associated SignerData. Implementations MAY fail to validate signatures that use a digest algorithm that is not included in the SignedDatadigestAlgorithmsset. oof, that one's a little risky, so keep an eye on it ๐signedAttrsis 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 notid-data.SignedAttributesMUST 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 ๐
signatureAlgorithmidentifies the signature algorithm, plus any associated parameters, that the signer used to generate the digital signature ๐๏ธsignatureis 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 ๐งunsignedAttrsis 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 ๐
attrTypetells you what type of attribute it is. It's an object identifier ๐attrValuesis a set of values that make up the attribute. The type of each value in the set can be determined uniquely byattrType. TheattrTypecan 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:
- A content-encryption key for a particular content-encryption algorithm is generated at random ๐ฒ
- 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.
- For each recipient, the encrypted content-encryption key and other recipient-specific information are collected into a RecipientInfo value, defined in Section 6.2 ๐งฉ
- 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 ๐
- 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 ๐
versionis the syntax version number. The right value depends onoriginatorInfo,RecipientInfo, andunprotectedAttrs. 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
originatorInfooptionally provides information about the originator ๐ต๏ธโโ๏ธ It is present only if the key management algorithm requires it, and it may contain certificates and CRLs:certsis a collection of certificates.certsmay contain originator certificates associated with several different key management algorithms. It may also contain attribute certificates associated with the originator. The certificates contained incertsare intended to be sufficient for all recipients to build certification paths from a recognized "root" or "top-level certification authority". However,certsmay 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,certsmay 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).crlsis a collection of CRLs. The set is intended to contain information sufficient to determine whether or not the certificates in thecertsfield are valid, but such correspondence is not necessary. There MAY be more CRLs than necessary, and there MAY also be fewer CRLs than necessary.
recipientInfosis a collection of per-recipient information ๐ฌ There MUST be at least one element in the collection.encryptedContentInfois the encrypted content information ๐unprotectedAttrsis 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 ๐
contentTypeindicates the type of content.contentEncryptionAlgorithmidentifies 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 ๐คencryptedContentis 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:
oriTypeidentifies the key management technique.oriValuecontains 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:
- A message digest is computed on the content with a message-digest algorithm.
- 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:
versionis 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.digestAlgorithmidentifies 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.encapContentInfois the content that is digested, as defined in Section 5.2.digestis 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:
versionis the syntax version number. If unprotectedAttrs is present, then the version MUST be 2. If unprotectedAttrs is absent, then version MUST be 0.encryptedContentInfois the encrypted content information, as defined in Section 6.1.unprotectedAttrsis 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:
- A message-authentication key for a particular message-authentication algorithm is generated at random.
- The message-authentication key is encrypted for each recipient. the details of this encryption depend on the key management algorithm used.
- For each recipient, the encrypted message-authentication key and other recipient-specific information are collected into a RecipientInfo value, defined in Section 6.2.
- 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:
versionis 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
originatorInfooptionally 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.recipientInfosis a collection of per-recipient information, as defined in Section 6.1. There MUST be at least one element in the collection.macAlgorithmis 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.digestAlgorithmidentifies 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.encapContentInfois the content that is authenticated, as defined in Section 5.2.authAttrsis 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.
macis the message authentication code.unauthAttrsis 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:
- The signedAttributes field MUST NOT contain a content-type attribute. There is no content type for countersignatures.
- The signedAttributes field MUST contain a message-digest attribute if it contains any other attributes.
- 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
- ietf: rfc5652 2009-09
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms