RFC 8894

Simple Certificate Enrolment Protocol

๐Ÿ“… published September 2020, from ietf

Simple Certificate Enrolment Protocol ๐ŸŽ€

Okay bestie, let's get into RFC 8894, and yes, the title block comes first because it's giving official paperwork energy ๐Ÿ’…. This one is by P. Gutmann from the University of Auckland, published in September 2020. It's an Informational document, which means it's published for info and is not an Internet Standards Track spec ๐ŸŒธ. It's an IETF product with IETF consensus, and it got the IESG's approval, but it's not a candidate for any level of Internet Standard. Check Section 2 of RFC 7841 if you want the full tea on that โœจ. It also shares the ISSN 2070-1721 ๐Ÿฆ‹.

This RFC doesn't obsolete or update anything, so it's its own little main character ๐Ÿ‘‘. The info page and errata live at https://www.rfc-editor.org/info/rfc8894.


Abstract

Okay, so the big idea: SCEP is a PKI protocol that is genuinely clever because it doesn't reinvent the wheel ๐Ÿ”ฅ. It uses Cryptographic Message Syntax (CMS, which used to be called PKCS #7) and PKCS #10, and it runs all of that over HTTP ๐Ÿ’–. SCEP is the evolved version of an enrolment protocol that Cisco Systems sponsored. It has really wide support across both client and server implementations, and loads of other industry standards that work with certificates rely on it ๐Ÿซถ. Literally iconic.


Status of This Memo

Let me break down the status stuff, because it's actually important ๐Ÿ’€.

This document is not an Internet Standards Track specification. It's published for informational purposes ๐Ÿงš.

It's an IETF product, so it represents the consensus of the IETF community. It went through public review and got approved for publication by the Internet Engineering Steering Group (IESG) โœจ. Not every IESG-approved document is a candidate for any level of Internet Standard, so see Section 2 of RFC 7841 for that.

For info on the current status of this document, errata, and how to give feedback, go to https://www.rfc-editor.org/info/rfc8894 ๐ŸŒˆ.


Copyright (c) 2020 IETF Trust and the persons identified as the document authors. All rights reserved ๐Ÿ’œ.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stood on the publication date. Please read these carefully, because they spell out your rights and restrictions ๐Ÿ™ˆ. Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty as described in that license.

This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of that material may not have granted the IETF Trust the right to allow modifications outside the IETF Standards Process. So unless you get an adequate license from whoever controls that copyright, this document may not be modified outside the IETF Standards Process, and derivative works may not be created outside it either. The exceptions are formatting it for publication as an RFC or translating it into languages other than English ๐ŸŽ€.


Table of Contents

   1.  Introduction
     1.1.  Conventions Used in This Document
   2.  SCEP Overview
     2.1.  SCEP Entities
       2.1.1.  Client
       2.1.2.  Certificate Authority
     2.2.  CA Certificate Distribution
     2.3.  Client Authentication
     2.4.  Enrolment Authorisation
     2.5.  Certificate Enrolment/Renewal
       2.5.1.  Client State Transitions
     2.6.  Certificate Access
     2.7.  CRL Access
     2.8.  Certificate Revocation
     2.9.  Mandatory-to-Implement Functionality
   3.  SCEP Secure Message Objects
     3.1.  SCEP Message Object Processing
     3.2.  SCEP pkiMessage
       3.2.1.  Signed Transaction Attributes
         3.2.1.1.  transactionID
         3.2.1.2.  messageType
         3.2.1.3.  pkiStatus
         3.2.1.4.  failInfo and failInfoText
         3.2.1.5.  senderNonce and recipientNonce
       3.2.2.  SCEP pkcsPKIEnvelope
     3.3.  SCEP pkiMessage types
       3.3.1.  PKCSReq/RenewalReq
       3.3.2.  CertRep
         3.3.2.1.  CertRep SUCCESS
         3.3.2.2.  CertRep FAILURE
         3.3.2.3.  CertRep PENDING
       3.3.3.  CertPoll (GetCertInitial)
       3.3.4.  GetCert and GetCRL
     3.4.  Degenerate certificates-only CMS SignedData
     3.5.  CA Capabilities
       3.5.1.  GetCACaps HTTP Message Format
       3.5.2.  CA Capabilities Response Format
   4.  SCEP Transactions
     4.1.  HTTP POST and GET Message Formats
     4.2.  Get CA Certificate
       4.2.1.  Get CA Certificate Response Message Format
         4.2.1.1.  CA Certificate Response Message Format
         4.2.1.2.  CA Certificate Chain Response Message Format
     4.3.  Certificate Enrolment/Renewal
       4.3.1.  Certificate Enrolment/Renewal Response Message
     4.4.  Poll for Client Initial Certificate
       4.4.1.  Polling Response Message Format
     4.5.  Certificate Access
       4.5.1.  Certificate Access Response Message Format
     4.6.  CRL Access
       4.6.1.  CRL Access Response Message Format
     4.7.  Get Next Certificate Authority Certificate
       4.7.1.  Get Next CA Response Message Format
   5.  SCEP Transaction Examples
     5.1.  Successful Transactions
     5.2.  Transactions with Errors
   6.  IANA Considerations
     6.1.  Registration of the application/x-x509-ca-cert Media Type
     6.2.  Registration of the application/x-x509-ca-ra-cert Media
           Type
     6.3.  Registration of the application/x-x509-next-ca-cert Media
           Type
     6.4.  Registration of the application/x-pki-message Media Type
   7.  Security Considerations
     7.1.  General Security
     7.2.  Use of the CA Private Key
     7.3.  ChallengePassword Shared Secret Value
     7.4.  Lack of Certificate Issue Confirmation
     7.5.  GetCACaps Issues
     7.6.  Lack of PoP in Renewal Requests
     7.7.  Traffic Monitoring
     7.8.  Unnecessary Cryptography
     7.9.  Use of SHA-1
     7.10. Use of HTTP
   8.  References
     8.1.  Normative References
     8.2.  Informative References
   Appendix A.  Background Notes
   Acknowledgements
   Author's Address

1. Introduction โœจ

Okay so X.509 certificates are the foundation for a bunch of security protocols, like TLS [RFC8446], S/MIME [RFC8551], and IKE/IPsec [RFC7296] ๐Ÿ’–. Whenever an X.509 certificate gets issued, a PKI client usually needs a certificate management protocol so it can request or renew a certificate from a Certificate Authority (CA). This spec defines SCEP, the Simple Certificate Enrolment Protocol, for certificate management and for certificate and CRL queries ๐Ÿฆ‹.

SCEP supports these general operations:

  • CA public key distribution ๐Ÿ”‘
  • Certificate enrolment and issue ๐ŸŽ€
  • Certificate renewal ๐ŸŒธ
  • Certificate query ๐Ÿงš
  • CRL query โญ

SCEP makes heavy use of CMS [RFC5652] and PKCS #10 [RFC2986] ๐Ÿ’….

1.1. Conventions Used in This Document

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here ๐Ÿ™ˆ.

This document uses the Augmented Backus-Naur Form (ABNF) notation as specified in [RFC5234] to define the formal syntax of commands. Non-terminals that aren't defined in [RFC5234] are defined in Section 4.1 ๐Ÿงš.


2. SCEP Overview ๐ŸŒˆ

This section gives you the overview of what SCEP can do ๐Ÿ’œ.

2.1. SCEP Entities

There are two entity types in SCEP: a client that requests a certificate, and a Certificate Authority (CA) that issues it. The following sections cover each one ๐ŸŽ€.

2.1.1. Client

A client MUST have the following information configured locally:

  1. The CA's fully qualified domain name or IP address.
  2. Any identification and/or authorisation information the CA requires before it will issue a certificate, as described in Section 3.3.1.
  3. The identifying information used to authenticate the CA in Section 4.2.1, which is typically a certificate fingerprint ๐Ÿซถ.

2.1.2. Certificate Authority

A SCEP CA is the entity that signs client certificates. A CA may enforce policies and apply them to certificate requests, and it may reject a request for any reason ๐Ÿ’….

Since the client is expected to do signature verification and, optionally, encryption using the CA certificate, the keyUsage extension in the CA certificate MUST indicate that it is valid for digitalSignature and keyEncipherment (if the key is to be used for en/decryption), alongside the usual CA usages of keyCertSign and/or cRLSign ๐Ÿ”ฅ.

2.2. CA Certificate Distribution

If the client hasn't already got the CA certificate(s) through some other means, the client MUST retrieve them before any PKI operation (Section 3) can start. Since no public key has been exchanged between the client and the CA yet, the messages can't be secured using CMS, so the CA certificate request and response data gets transferred in the clear ๐Ÿ˜ญ.

If there's an intermediate CA in use, a certificates-only CMS SignedData message with a certificate chain made of all the CA certificates gets returned. Otherwise, the CA certificate itself is returned ๐ŸŒธ.

The CA certificate MAY be provided to the client out of band. Alternatively, the CA certificate fingerprint MAY be used to authenticate a CA certificate distributed by the GetCACert response (Section 4.2) or via HTTP certificate-store access [RFC4387]. The fingerprint is made by calculating a SHA-256 hash over the whole CA certificate. (For legacy reasons, some implementations may use a SHA-1 hash.) ๐Ÿฆ‹

After the client gets the CA certificate, it SHOULD authenticate it somehow, unless that's deemed unnecessary, for example because the device is being provisioned inside a trusted environment โœจ. For instance, the client could compare the certificate's fingerprint with locally configured, out-of-band distributed identifying information, or do some equivalent thing like a direct comparison with a locally stored copy of the certificate ๐ŸŽ€.

Intermediate CA certificates, if there are any, are signed by a higher-level CA, so there's no need to authenticate them against the out-of-band data ๐Ÿงš. But intermediate CA certificates get rolled over more often than long-lived top-level CA certificates, so clients MUST verify intermediate-level CA certificates before using them during protocol exchanges, in case the intermediate CA certificate has expired or otherwise been invalidated ๐Ÿ˜ญ.

When a CA certificate expires, certificates that were signed by it may no longer be regarded as valid. CA key rollover gives the CA a way to distribute a new CA certificate that will be valid in the future, once the current one has expired ๐Ÿ’–. This is done with the GetNextCACert message (Section 4.7) ๐Ÿ‘‘.


That's part 1 done, bestie! More parts are coming, so stay tuned ๐Ÿ’œ๐Ÿซถ

2.3. Client Authentication ๐Ÿ”

Okay so, like with every protocol that uses public-key cryptography, the link between the public keys being used and the identities they belong to has to be authenticated in a cryptographically secure way. The client and the CA chat through SCEP Secure Message Objects, which Section 3 explains. That's where CMS comes in to encrypt and sign the data ๐ŸŽ€. To do the signing, the client needs an appropriate local certificate, and there are three possible situations:

  1. If the client does not have an appropriate existing certificate, then a locally generated self-signed certificate MUST be used. The keyUsage extension in the certificate MUST indicate that it is valid for digitalSignature and keyEncipherment (if available). The self-signed certificate SHOULD use the same subject name and key as in the PKCS #10 request. In this case, the messageType is PKCSReq (see Section 3.2.1.2) ๐Ÿ’–.
  2. If the client already has a certificate issued by the SCEP CA, and the CA supports renewal (see Section 2.5), that certificate SHOULD be used. In this case, the messageType is RenewalReq (see Section 3.2.1.2) โœจ.
  3. Alternatively, if the client has no certificate issued by the SCEP CA but has credentials from an alternate CA, then the certificate issued by the alternate CA MAY be used in a renewal request as described above. The SCEP CA's policy will determine whether the request can be accepted or not ๐Ÿคทโ€โ™€๏ธ.

Quick heads-up ๐Ÿ‘€: although the text above describes several different types of operations, for historical reasons most implementations always apply the first one, even if an existing certificate already exists. So support for the first case is mandatory, while support for the latter ones is optional (see Section 2.9). Main character energy for case one, periodt ๐Ÿ’…

During certificate enrolment, the client MUST use the selected certificate's key when signing the CMS envelope (see Section 3). That certificate will be either the self-signed one matching the PKCS #10 request, or the CA-issued one used to authorise a renewal. Either way, it MUST be included in the signedData certificates field (possibly as part of a full certificate chain). If the key being certified allows encryption, then the CA's CertResp will use that same certificate's public key when encrypting the response ๐Ÿ”’.

Now, for renewals, the request will be signed and authenticated with the key in the previously issued certificate, not the key in the PKCS #10 request. The response may similarly come back encrypted with the key in that previously issued certificate.

This one has real security implications, so go read Section 7.6 ๐Ÿ’€

2.4. Enrolment Authorisation ๐Ÿ—๏ธ

PKCS #10 [RFC2986] specifies a PKCS #9 [RFC2985] challengePassword attribute to be sent as part of the enrolment request. When using the challengePassword, the CA hands a shared secret to the client, and that secret is used to authenticate the request from the client. It is RECOMMENDED that the challengePassword be a one-time authenticator value. That way, an attacker who captures the authenticator from the client or CA can't just reuse it to request more certificates ๐Ÿ™ˆ.

Including the challengePassword from the SCEP client is RECOMMENDED. However, leaving it out allows for unauthenticated authorisation of enrolment requests. That might still need manual approval for each certificate issued, if other security measures controlling issuance aren't in place (see below). Inclusion is OPTIONAL for renewal requests that are authenticated by being signed with an existing certificate. And the CMS envelope protects the privacy of the challengePassword ๐ŸŽ€.

A client that is performing certificate renewal as per Section 2.5 SHOULD omit the challengePassword, but MAY send the originally distributed shared secret in the challengePassword attribute. The SCEP CA MAY authenticate the request using the challengePassword in addition to the previously issued certificate that signs the request. The SCEP CA MUST NOT attempt to authenticate a client based on a self-signed certificate unless it has been verified through out-of-band means, such as a certificate fingerprint. Not me trusting random self-signed certs ๐Ÿ™…โ€โ™€๏ธ

For manual mode, the client's request is placed in the PENDING state until the CA operator authorises or rejects it. Manual authorisation is used when the client has only a self-signed certificate that hasn't been previously authenticated by the CA, and/or a challengePassword is not available. The SCEP CA MAY either reject unauthorised requests or mark them for manual authorisation according to CA policy ๐Ÿง.

2.5. Certificate Enrolment/Renewal ๐Ÿ”„

A client kicks off an enrolment transaction (Section 3.3.1) by creating a certificate request using PKCS #10, then sending it to the CA enveloped using CMS (Section 3) ๐Ÿ“จ.

If the CA supports certificate renewal and CA policy permits it, a new certificate with new validity dates can be issued, even though the old one is still valid โœจ. To renew an existing certificate, the client uses the RenewalReq message (see Section 3.3) and signs it with the existing client certificate. The client SHOULD use a new keypair when requesting a new certificate, but MAY request a new certificate using the old keypair.

If the CA returns a CertRep message (Section 3.3.2) with status set to PENDING, the client enters polling mode. That means it periodically sends a CertPoll message (Section 3.3.3) to the CA until the CA operator completes the manual authentication (approving or denying the request) ๐Ÿ”. How often it polls is a CA/client configuration issue. It might be every few seconds or minutes when issuing is automatic but not instant, or every few hours or days if issuing needs manual approval โณ.

If polling mode is being used, the client sends a single PKCSReq/RenewalReq message (Section 3.3.1), followed by 0 or more CertPoll messages (Section 3.3.3). The CA will, in return, send 0 or more CertRep messages (Section 3.3.2) with status set to PENDING in response to the CertPolls. Then comes a single CertRep message (Section 3.3.2) with status set to either SUCCESS or FAILURE ๐ŸŽ‰.

2.5.1. Client State Transitions ๐Ÿ—บ๏ธ

The client state transitions during the SCEP process are shown in Figure 1 ๐Ÿ‘‡

                                   CertPoll
                                 +-----<----+
                                 |          |
                                 |          | CertRep(PENDING)
                                 |          |
   [CERT-NONEXISTENT] ------> [CERT-REQ-PENDING] --------> [CERT-ISSUED]
         ^            PKCSReq    |           CertRep(SUCCESS)
         |          RenewalReq   |
         |                       |
         +-----------------------+
         CertRep(FAILURE) or
         Max-time/max-polls exceeded

                     Figure 1: State Transition Diagram

The certificate-issue process starts at state CERT-NONEXISTENT. Sending a PKCSReq/RenewalReq message changes the state to CERT-REQ-PENDING ๐Ÿš€.

If the CA returns a CertRep message with pkiStatus set to SUCCESS, then the state changes to CERT-ISSUED ๐Ÿ’–.

If the CA returns a CertRep message with pkiStatus set to FAILURE, or there is no response, then the state reverts back to CERT-NONEXISTENT ๐Ÿ’€.

If the CA returns a CertRep message with pkiStatus set to PENDING, then the client keeps polling by sending a CertPoll message until one of three things happens: a CertRep message with status set to SUCCESS or FAILURE is received, a timeout occurs, or the maximum number of polls has been exceeded ๐Ÿ”.

Figure 2 shows a successful transaction in automatic mode โšก

       CLIENT                              CA SERVER

   PKCSReq: PKI cert. enrolment message
   --------------------------------> CertRep: pkiStatus = SUCCESS
                                     Certificate attached
                                     <------------------------------
   Receive issued certificate.

                          Figure 2: Automatic Mode

Figure 3 shows a successful transaction in manual mode:

       CLIENT                              CA SERVER

   PKCSReq: PKI cert. enrolment message
   --------------------------------> CertRep: pkiStatus = PENDING
                                     <------------------------------
   CertPoll: Polling message
   --------------------------------> CertRep: pkiStatus = PENDING
                                     <------------------------------
   ................ <Manual identity authentication> ...............

   CertPoll: Polling message
   --------------------------------> CertRep: pkiStatus = SUCCESS
                                     Certificate attached
                                     <------------------------------
   Receive issued certificate.

                           Figure 3: Manual Mode

2.6. Certificate Access ๐Ÿ“ฅ

A certificate query message is defined so clients can retrieve a copy of their own certificate from the CA. It's for clients that don't store their certificates locally, so they can grab a copy whenever they need one ๐Ÿงš. This isn't meant to be a general-purpose certificate-access service. That job may be handled instead by HTTP certificate-store access [RFC4387] or the Lightweight Directory Access Protocol (LDAP).

To retrieve a certificate from the CA, a client sends a request made up of the certificate's issuer name and serial number. This assumes the client saved the issuer name and serial number of the issued certificate from its previous enrolment transaction ๐Ÿ“. The transaction to retrieve a certificate consists of one GetCert (Section 3.3.4) message and one CertRep (Section 3.3.2) message, as shown in Figure 4.

      CLIENT                               CA SERVER

   GetCert: PKI certificate query message
   -------------------------------> CertRep: pkiStatus = SUCCESS
                                    Certificate attached
                                    <-----------------------------
   Receive the certificate.

                     Figure 4: Retrieving a Certificate

2.7. CRL Access ๐Ÿ“œ

SCEP clients MAY request a CRL via one of three methods:

  1. If the CA supports the CRL Distribution Points (CRLDPs) extension [RFC5280] in issued certificates, then the CRL MAY be retrieved via the mechanism specified in the CRLDP ๐ŸŒธ.
  2. If the CA supports HTTP certificate-store access [RFC4387], then the CRL MAY be retrieved via the AuthorityInfoAcces [RFC5280] location specified in the certificate ๐Ÿฆ‹.
  3. Only if the CA does not support CRLDPs or HTTP access should a CRL query be composed, by creating a GetCRL message consisting of the issuer name and serial number from the certificate whose revocation status is being queried ๐Ÿซถ.

The message is sent to the SCEP CA the same way as the other SCEP requests. The transaction to retrieve a CRL consists of one GetCRL PKI message and one CertRep PKI message. That CertRep contains only the CRL (no certificates), in a degenerate certificates-only CMS SignedData message (Section 3.4), as shown in Figure 5 โœจ.

          CLIENT                           CA SERVER

      GetCRL: PKI CRL query message
   ---------------------------------->
                                     CertRep: CRL attached
                                     <-----------------------------
   Receive the CRL

                         Figure 5: Retrieving a CRL

Okay so, picking up right where we left off, bestie ๐Ÿ’…๐ŸŽ€ Section 2.8 is about revocation, and SCEP literally doesn't have its own way to do it, so the client has to go talk to the CA through some other mechanism ๐Ÿ™ˆ

2.8. Certificate Revocation

SCEP does not specify a method to request certificate revocation. In order to revoke a certificate, the client must contact the CA using a non-SCEP-defined mechanism. ๐Ÿฆ‹

2.9. Mandatory-to-Implement Functionality

Okay this one is the checklist of must-haves ๐Ÿ“โœจ At a minimum, all SCEP implementations compliant with this specification MUST support GetCACaps (Section 3.5.1), GetCACert (Section 4.2), PKCSReq (Section 3.3.1) (and its associated response messages), communication of binary data via HTTP POST (Section 4.1), and the AES128-CBC [AES] and SHA-256 [SHA2] algorithms to secure pkiMessages (Section 3.2). No skipping these, periodt ๐Ÿ’–

For historical reasons, implementations MAY support communications of binary data via HTTP GET (Section 4.1), and the triple DES-CBC and SHA-1 algorithms to secure pkiMessages (Section 3.2). So those are optional, totally your call ๐ŸŒธ Implementations MUST NOT support the obsolete and/or insecure single DES and MD5 algorithms used in earlier versions of this specification, since the unsecured nature of GetCACaps means that an in-path attacker can trivially roll back the encryption used to these insecure algorithms; see Section 7.5. ๐Ÿšซ Not me going back to single DES and MD5 just because an attacker can trick the handshake, absolutely not ๐Ÿ˜ญ๐Ÿ”ฅ

3. SCEP Secure Message Objects

Okay now we're in the good stuff ๐Ÿงš CMS is a general enveloping mechanism that enables both signed and encrypted transmission of arbitrary data. SCEP messages that require confidentiality use two layers of CMS, as shown using ASN.1-like pseudocode in Figure 6. By applying both enveloping and signing transformations, the SCEP message is protected both for the integrity of its end-to-end transaction information and the confidentiality of its information portion. Two layers, both doing a job, iconic ๐Ÿ‘‘

pkiMessage {
  contentType = signedData { pkcs-7 2 },
  content {
    digestAlgorithms,
    encapsulatedContentInfo {
      eContentType = data { pkcs-7 1 },
      eContent {           -- pkcsPKIEnvelope, optional
        contentType = envelopedData { pkcs-7 3 },
        content {
          recipientInfo,
          encryptedContentInfo {
            contentType = data { pkcs-7 1 },
            contentEncrAlgorithm,
            encryptedContent {
              messageData  -- Typically PKCS #10 request
              }
            }
          }
        }
      },
    certificates,          -- Optional
    crls,                  -- Optional
    signerInfo {
      signedAttrs {
        transactionID,
        messageType,
        pkiStatus,
        failInfo,          -- Optional
        senderNonce / recipientNonce,
        },
      signature
      }
    }
  }

Figure 6: CMS Layering ๐ŸŽ€

When a particular SCEP message carries data, this data is carried in the messageData. CertRep messages will lack any signed content and consist only of a pkcsPKIEnvelope (Section 3.2.2). Those CertRep messages are giving minimalism ๐ŸŒˆ

The remainder of this document will refer only to "messageData", but it is understood to always be encapsulated in the pkcsPKIEnvelope (Section 3.2.2). The format of the data in the messageData is defined by the messageType attribute (see Section 3.2) of the SignedData. If there is no messageData to be transmitted, the entire pkcsPKIEnvelope MUST be omitted. So if there's nothing to send, the whole envelope gets dropped, no exceptions ๐Ÿ™…โ€โ™€๏ธ

Samples of SCEP messages are available through the JSCEP project [JSCEP] in the src/samples directory. Go check those out, they're a great peek ๐Ÿ‘€

3.1. SCEP Message Object Processing

Creating a SCEP message consists of several stages. The content to be conveyed (in other words, the messageData) is first encrypted, and the encrypted content is then signed. Encrypt first, then sign, that's the order ๐Ÿ”

The form of encryption to be applied depends on the capabilities of the recipient's public key. If the key is encryption capable (for example, RSA), then the messageData is encrypted using the recipient's public key with the CMS KeyTransRecipientInfo mechanism. If the key is not encryption capable (for example, DSA or ECDSA), then the messageData is encrypted using the challengePassword with the CMS PasswordRecipientInfo mechanism. So it depends on what kind of key they've got, RSA gets the direct treatment and DSA or ECDSA fall back to the challengePassword ๐Ÿฆ‹

Once the messageData has been encrypted, it is signed with the sender's public key. This completes the SCEP message, which is then sent to the recipient. Signed, sent, done โœจ

Note that some early implementations of this specification dealt with keys that were not encryption capable by omitting the encryption stage, based on the text in Section 3 that indicated that "the EnvelopedData is omitted". This alternative processing mechanism SHOULD NOT be used since it exposes in cleartext the challengePassword used to authorise the certificate issue. ๐Ÿ™ˆ Lowkey a bad idea, because it leaks the challengePassword in plain text, so SHOULD NOT be the vibe here ๐Ÿ’€

3.2. SCEP pkiMessage

The basic building block of all secured SCEP messages is the SCEP pkiMessage. It consists of a CMS SignedData content type. The following restrictions apply:

  • The eContentType in encapsulatedContentInfo MUST be data ({pkcs-7 1}).
  • The signed content, if present (FAILURE and PENDING CertRep messages will lack any signed content), MUST be a pkcsPKIEnvelope (Section 3.2.2) and MUST match the messageType attribute.
  • The SignerInfo MUST contain a set of authenticatedAttributes (Section 3.2.1).

3.2.1. Signed Transaction Attributes

At a minimum, all messages MUST contain the following authenticatedAttributes:

  • A transactionID attribute (see Section 3.2.1.1).
  • A messageType attribute (see Section 3.2.1.2).
  • A fresh senderNonce attribute (see Section 3.2.1.5). However, note the comment about senderNonces and polling in Section 3.3.2
  • Any attributes required by CMS.

If the message is a CertRep, it MUST also include the following authenticatedAttributes:

  • A pkiStatus attribute (see Section 3.2.1.3).
  • failInfo and optional failInfoText attributes (see Section 3.2.1.4) if pkiStatus = FAILURE.
  • A recipientNonce attribute (see Section 3.2.1.5) copied from the senderNonce in the request that this is a response to.

The following transaction attributes are encoded as authenticated attributes and carried in the SignerInfo for this SignedData. Here's the attribute cheat sheet ๐Ÿ“

+================+=================+==============================+
| Attribute      | Encoding        | Comment                      |
+================+=================+==============================+
| transactionID  | PrintableString | Unique ID for this           |
|                |                 | transaction as a text string |
+----------------+-----------------+------------------------------+
| messageType    | PrintableString | Decimal value as a numeric   |
|                |                 | text string                  |
+----------------+-----------------+------------------------------+
| pkiStatus      | PrintableString | Decimal value as a numeric   |
|                |                 | text string                  |
+----------------+-----------------+------------------------------+
| failInfo       | PrintableString | Decimal value as a numeric   |
|                |                 | text string                  |
+----------------+-----------------+------------------------------+
| failInfoText   | UTF8String      | Descriptive text for the     |
|                |                 | failInfo value               |
+----------------+-----------------+------------------------------+
| senderNonce    | OCTET STRING    | Random nonce as a 16-byte    |
|                |                 | binary data string           |
+----------------+-----------------+------------------------------+
| recipientNonce | OCTET STRING    | Random nonce as a 16-byte    |
|                |                 | binary data string           |
+----------------+-----------------+------------------------------+

Table 1: SCEP Attributes

The OIDs used for these attributes are as follows. Numbers, numbers everywhere ๐Ÿ”ข

+======================+===============================+
| Name                 | ASN.1 Definition              |
+======================+===============================+
| id-VeriSign          | OBJECT_IDENTIFIER ::= {2 16   |
|                      | US(840) 1 VeriSign(113733)}   |
+----------------------+-------------------------------+
| id-pki               | OBJECT_IDENTIFIER ::= {id-    |
|                      | VeriSign pki(1)}              |
+----------------------+-------------------------------+
| id-attributes        | OBJECT_IDENTIFIER ::= {id-pki |
|                      | attributes(9)}                |
+----------------------+-------------------------------+
| id-transactionID     | OBJECT_IDENTIFIER ::= {id-    |
|                      | attributes transactionID(7)}  |
+----------------------+-------------------------------+
| id-messageType       | OBJECT_IDENTIFIER ::= {id-    |
|                      | attributes messageType(2)}    |
+----------------------+-------------------------------+
| id-pkiStatus         | OBJECT_IDENTIFIER ::= {id-    |
|                      | attributes pkiStatus(3)}      |
+----------------------+-------------------------------+
| id-failInfo          | OBJECT_IDENTIFIER ::= {id-    |
|                      | attributes failInfo(4)}       |
+----------------------+-------------------------------+
| id-senderNonce       | OBJECT_IDENTIFIER ::= {id-    |
|                      | attributes senderNonce(5)}    |
+----------------------+-------------------------------+
| id-recipientNonce    | OBJECT_IDENTIFIER ::= {id-    |
|                      | attributes recipientNonce(6)} |
+----------------------+-------------------------------+
| id-scep              | OBJECT IDENTIFIER ::= {id-    |
|                      | pkix 24}                      |
+----------------------+-------------------------------+
| id-scep-failInfoText | OBJECT IDENTIFIER ::= {id-    |
|                      | scep 1}                       |
+----------------------+-------------------------------+

Table 2: SCEP Attribute OIDs

The attributes are detailed in the following sections. Let's get into it ๐Ÿ’œ

3.2.1.1. transactionID

A PKI operation is a transaction consisting of the messages exchanged between a client and the CA. The transactionID is a text string provided by the client when starting a transaction. The client MUST use a unique string as the transaction identifier, encoded as a PrintableString, which MUST be used for all PKI messages exchanged for a given operation, such as a certificate issue. Same ID for every message in the operation, no mixing ๐Ÿฆ‹

Note that the transactionID must be unique, but not necessarily randomly generated. For example, it may be a value assigned by the CA to allow the client to be identified by their transactionID, using a value such as the client device's Extended Unique Identifier (EUI), Remote Terminal Unit (RTU) ID, or a similar unique identifier. This can be useful when the client doesn't have a preassigned Distinguished Name through which the CA can identify their request -- for example, when enrolling Supervisory Control and Data Acquisition (SCADA) devices. โšก Unique doesn't have to mean random, so devices like SCADA gear can use their EUI or RTU ID and the CA can still recognise them, iykyk ๐ŸŒธ

3.2.1.2. messageType โœจ

okay so the messageType attribute tells you what kind of operation the transaction is doing ๐Ÿ’… and it MUST be included in all PKI messages ๐Ÿซถ no exceptions bestie. here's the lineup of message types:

+=======+============+============================================+
| Value | Name       | Description                                |
+=======+============+============================================+
| 0     | Reserved   |                                            |
+-------+------------+--------------------------------------------+
| 3     | CertRep    | Response to certificate or CRL request.    |
+-------+------------+--------------------------------------------+
| 17    | RenewalReq | PKCS #10 certificate request authenticated |
|       |            | with an existing certificate.              |
+-------+------------+--------------------------------------------+
| 19    | PKCSReq    | PKCS #10 certificate request authenticated |
|       |            | with a shared secret.                      |
+-------+------------+--------------------------------------------+
| 20    | CertPoll   | Certificate polling in manual enrolment.   |
+-------+------------+--------------------------------------------+
| 21    | GetCert    | Retrieve a certificate.                    |
+-------+------------+--------------------------------------------+
| 22    | GetCRL     | Retrieve a CRL.                            |
+-------+------------+--------------------------------------------+

                    Table 3: SCEP Message Types

any message type that's NOT in that table MUST be treated as an error ๐Ÿ™ˆ, unless its use got negotiated through GetCACaps (Section 3.5.1) โœจ. so don't just wing it, babe.

3.2.1.3. pkiStatus ๐ŸŒธ

every response message MUST include transaction status info ๐Ÿ’–, and that's the pkiStatus attribute:

   +=======+=========+========================================+
   | Value | Name    | Description                            |
   +=======+=========+========================================+
   | 0     | SUCCESS | Request granted.                       |
   +-------+---------+----------------------------------------+
   | 2     | FAILURE | Request rejected.  In this case, the   |
   |       |         | failInfo attribute, as defined in      |
   |       |         | Section 3.2.1.4, MUST also be present. |
   +-------+---------+----------------------------------------+
   | 3     | PENDING | Request pending for manual approval.   |
   +-------+---------+----------------------------------------+

                  Table 4: pkiStatus Attributes

pkiStatus values that aren't listed up there MUST be treated as errors too ๐Ÿ”ฅ, unless they were negotiated through GetCACaps (Section 3.5.1) ๐Ÿ‘‘.

3.2.1.4. failInfo and failInfoText ๐Ÿฆ‹

the failInfo attribute MUST contain one of these failure reasons:

  +=======+=================+==================================+
  | Value | Name            | Description                      |
  +=======+=================+==================================+
  | 0     | badAlg          | Unrecognised or unsupported      |
  |       |                 | algorithm.                       |
  +-------+-----------------+----------------------------------+
  | 1     | badMessageCheck | Integrity check (meaning         |
  |       |                 | signature verification of the    |
  |       |                 | CMS message) failed.             |
  +-------+-----------------+----------------------------------+
  | 2     | badRequest      | Transaction not permitted or     |
  |       |                 | supported.                       |
  +-------+-----------------+----------------------------------+
  | 3     | badTime         | The signingTime attribute from   |
  |       |                 | the CMS authenticatedAttributes  |
  |       |                 | was not sufficiently close to    |
  |       |                 | the system time.  This condition |
  |       |                 | may occur if the CA is concerned |
  |       |                 | about replays of old messages.   |
  +-------+-----------------+----------------------------------+
  | 4     | badCertId       | No certificate could be          |
  |       |                 | identified matching the provided |
  |       |                 | criteria.                        |
  +-------+-----------------+----------------------------------+

                   Table 5: failInfo Attributes

failure reasons that aren't defined above MUST be treated as errors as well, unless they got negotiated through GetCACaps (Section 3.5.1) โœจ.

the failInfoText is a free-form UTF-8 text string that gives extra info when pkiStatus = FAILURE ๐Ÿ’…. it can explain why a cert request wasn't granted in more detail than the super-generic failInfo = badRequest status ๐Ÿ™ˆ. because it's free-form and meant for humans to read, implementations SHOULD NOT assume it has any machine-processable content ๐Ÿค–๐Ÿ’€.

3.2.1.5. senderNonce and recipientNonce ๐ŸŽ€

the senderNonce and recipientNonce attributes are each a 16-byte random number generated for each transaction ๐Ÿงš. they exist to prevent replay attacks ๐Ÿ”’.

when a sender sends a PKI message to a recipient, a fresh senderNonce MUST be included in the message ๐Ÿ’œ. the recipient MUST copy the senderNonce into the recipientNonce of the reply as proof of liveliness โญ. the original sender MUST verify that the recipientNonce of the reply matches the senderNonce it sent in the request ๐Ÿ‘€. if the nonce doesn't match, then the message MUST be rejected ๐Ÿšซ.

note that since SCEP exchanges are a single request followed by a single response, having distinct sender and recipient nonces is kinda redundant ๐Ÿ™ƒ. the client sends a nonce in its request and the CA responds with the same nonce in its reply. so in effect there's just one nonce: it's called senderNonce in the client's request and recipientNonce in the CA's reply ๐ŸŒˆ.

3.2.2. SCEP pkcsPKIEnvelope ๐Ÿ’Œ

the info portion of a SCEP message is carried inside an EnvelopedData content type, as defined in CMS, with these restrictions:

  • contentType in encryptedContentInfo MUST be data ({pkcs-7 1}).
  • encryptedContent MUST be the SCEP message being transported (see Section 4) and MUST match the messageType authenticated Attribute in the pkiMessage. ๐Ÿซถ

3.3. SCEP pkiMessage types ๐Ÿฆ‹

all of the messages in this section are pkiMessages (Section 3.2), and the type of the message MUST be specified in the "messageType" authenticated Attribute ๐Ÿ’…. each section defines a valid message type, the matching messageData formats, and the mandatory authenticated attributes for that type โœจ.

3.3.1. PKCSReq/RenewalReq ๐ŸŒธ

the messageData for this type is a PKCS #10 Certificate Request. the certificate request MUST contain at least these items:

  • The subject Distinguished Name.
  • The subject public key.
  • For a PKCSReq, if authorisation based on a shared secret is being used, a challengePassword attribute.

on top of that, the message must contain the authenticatedAttributes specified in Section 3.2.1 ๐Ÿ’–.

3.3.2. CertRep ๐Ÿ“

the messageData for this type is a degenerate certificates-only CMS SignedData message (Section 3.4) ๐Ÿงš. the exact content needed in the reply depends on what kind of request this is responding to. the request types are detailed in Sections 3.3.2.1 and 4 ๐Ÿ‘‘. again, the message must contain the authenticatedAttributes specified in Section 3.2.1 โœจ.

earlier draft versions of this spec required this message to include a senderNonce alongside the recipientNonce, which was supposed to chain into later polling operations ๐Ÿ™ˆ. but if a single message got lost during a long polling window (see Section 5 for an example), enforcing that would make the whole transaction fail even though nothing actually went wrong ๐Ÿ˜ญ. so implementations mostly ignored the requirement, either carrying the nonce over or checking for it ๐Ÿ’€. newer versions of the spec no longer require chaining nonces across polling operations ๐ŸŒˆ.

3.3.2.1. CertRep SUCCESS ๐ŸŽ€

when pkiStatus is set to SUCCESS, the messageData is a degenerate certificates-only CMS SignedData message (Section 3.4) ๐Ÿ’…. what goes in that degenerate certificates-only SignedData depends on the original request, as laid out in Table 6 ๐Ÿฆ‹.

+==============+===============================================+
| Request-type | Reply-contents                                |
+==============+===============================================+
| PKCSReq      | The reply MUST contain at least the issued    |
|              | certificate in the certificates field of the  |
|              | SignedData.  The reply MAY contain additional |
|              | certificates, but the issued certificate MUST |
|              | be the leaf certificate.                      |
+--------------+-----------------------------------------------+
| RenewalReq   | Same as PKCSReq                               |
+--------------+-----------------------------------------------+
| CertPoll     | Same as PKCSReq                               |
+--------------+-----------------------------------------------+
| GetCert      | The reply MUST contain at least the requested |
|              | certificate in the certificates field of the  |
|              | SignedData.  The reply MAY contain additional |
|              | certificates, but the requested certificate   |
|              | MUST be the leaf certificate.                 |
+--------------+-----------------------------------------------+
| GetCRL       | The reply MUST contain the CRL in the crls    |
|              | field of the SignedData.                      |
+--------------+-----------------------------------------------+

                 Table 6: CertRep Response Types

3.3.2.2. CertRep FAILURE ๐Ÿ’€

when pkiStatus is set to FAILURE, the reply MUST also contain a failInfo (Section 3.2.1.4) attribute set to the right error condition describing the failure ๐Ÿšจ. the reply MAY also contain a failInfoText attribute with extended details on why the operation failed, usually to expand on the catchall failInfo = badRequest status ๐Ÿซถ. the pkcsPKIEnvelope (Section 3.2.2) MUST be omitted ๐Ÿ™…โ€โ™€๏ธ.

3.3.2.3. CertRep PENDING โณ

when pkiStatus is set to PENDING, the pkcsPKIEnvelope (Section 3.2.2) MUST be omitted ๐ŸŒท.

3.3.3. CertPoll (GetCertInitial) ๐Ÿฆ‹

okay so this message is used for certificate polling ๐Ÿงš for reasons nobody can fully explain, it was called "GetCertInitial" in earlier draft versions ๐Ÿ’€ the messageData for this type is an IssuerAndSubject:

issuerAndSubject ::= SEQUENCE {
    issuer     Name,
    subject    Name
    }

the issuer gets set to the subjectName of the CA (aka the intended issuerName of the certificate being requested) ๐Ÿ‘‘ and the subject gets set to the subjectName used when requesting the certificate ๐Ÿ’–

ngl both of these fields are kinda redundant ๐Ÿ™ˆ the CA is identified by the recipientInfo in the pkcsPKIEnvelope (or, most of the time, just by the server the message is sent to), and the client/transaction being polled is identified by the transactionID โœจ the CA can handle both of those without going through the cryptographically expensive process of unwrapping and processing the issuerAndSubject ๐Ÿ’… so implementations SHOULD assume that the polling operation is controlled by the recipientInfo and transactionID rather than the contents of the messageData ๐Ÿซถ and the message must contain the authenticatedAttributes specified in Section 3.2.1 ๐Ÿ”ฅ

3.3.4. GetCert and GetCRL ๐ŸŒธ

the messageData for these types is an IssuerAndSerialNumber, as defined in CMS ๐ŸŽ€ it uniquely identifies the certificate being requested, either the certificate itself for GetCert or its revocation status via a CRL for GetCRL ๐Ÿง and again, the message must contain the authenticatedAttributes specified in Section 3.2.1 โœจ

these message types are included here for completeness, but honestly they add unnecessary cryptography and messaging overhead just to move a certificate or CRL around ๐Ÿ˜ญ (see Section 7.8) so implementations SHOULD prefer HTTP certificate-store access [RFC4387] or LDAP over using these messages ๐Ÿ’œ

3.4. Degenerate certificates-only CMS SignedData ๐Ÿ“

CMS has a degenerate case of the SignedData content type where there are no signers ๐Ÿงš the whole point of it is to disseminate certificates and CRLs ๐Ÿ’… for SCEP, the content field of the ContentInfo value of a degenerate certificates-only SignedData MUST be omitted ๐Ÿ™…โ€โ™€๏ธ when it's carrying certificates, they go in the certificates field of the SignedData ๐ŸŽ€ and when it's carrying a CRL, the CRL goes in the crls field of the SignedData ๐Ÿฆ‹

3.5. CA Capabilities ๐Ÿ‘‘

so SCEP needs to be able to grow ๐ŸŒˆ to support future enhancements, CAs MUST implement the GetCACaps message so clients can ask which functionality the CA actually has ๐Ÿ”ฅ

3.5.1. GetCACaps HTTP Message Format ๐Ÿ’–

this message requests capabilities from a CA, following the format from Section 4.1:

"GET" SP SCEPPATH "?operation=GetCACaps" SP HTTP-version CRLF

3.5.2. CA Capabilities Response Format โœจ

the response to a GetCACaps message is a list of CA capabilities ๐Ÿ’… it's plain text, in any order, separated by <CR><LF> or <LF> characters ๐Ÿซถ this spec defines the following keywords (quotation marks are not sent) ๐ŸŒธ

+==================+========================================+
| Keyword          | Description                            |
+==================+========================================+
| AES              | CA supports the AES128-CBC encryption  |
|                  | algorithm.                             |
+------------------+----------------------------------------+
| DES3             | CA supports the triple DES-CBC         |
|                  | encryption algorithm.                  |
+------------------+----------------------------------------+
| GetNextCACert    | CA supports the GetNextCACert message. |
+------------------+----------------------------------------+
| POSTPKIOperation | CA supports PKIOPeration messages sent |
|                  | via HTTP POST.                         |
+------------------+----------------------------------------+
| Renewal          | CA supports the Renewal CA operation.  |
+------------------+----------------------------------------+
| SHA-1            | CA supports the SHA-1 hashing          |
|                  | algorithm.                             |
+------------------+----------------------------------------+
| SHA-256          | CA supports the SHA-256 hashing        |
|                  | algorithm.                             |
+------------------+----------------------------------------+
| SHA-512          | CA supports the SHA-512 hashing        |
|                  | algorithm.                             |
+------------------+----------------------------------------+
| SCEPStandard     | CA supports all mandatory-to-implement |
|                  | sections of the SCEP standard.  This   |
|                  | keyword implies "AES",                 |
|                  | "POSTPKIOperation", and "SHA-256", as  |
|                  | well as the provisions of Section 2.9. |
+------------------+----------------------------------------+

Table 7: GetCACaps Response Keywords ๐Ÿ“‹

table 7 is the full list of keywords defined in this spec ๐Ÿ’œ a CA MAY provide additional keywords to advertise more capabilities and functionality ๐ŸŒˆ and a client MUST be able to accept and ignore any unknown keywords a CA sends ๐Ÿซถ

the CA MUST use the text case specified here ๐Ÿ’… but clients SHOULD ignore the text case when processing this message ๐Ÿฆ‹ clients MUST accept the standard HTTP-style text delimited by <CR><LF>, as well as the text delimited by <LF> that was specified in an earlier draft version of this spec ๐ŸŽ€

and the client SHOULD use SHA-256 over SHA-1 for hashing, and AES128-CBC over triple DES-CBC for encryption, if the CA supports them ๐Ÿ”ฅ the CMS format allows any form of AES and SHA-2, but for interoperability's sake, the de facto universal standards of AES128-CBC and SHA-256 SHOULD be used ๐ŸŒธ

announcing some of these capabilities one by one is redundant ๐Ÿ™„ they're already required as mandatory-to-implement functionality (see Section 2.9), and that whole set gets signalled by the "SCEPStandard" capability ๐Ÿ’– still, it can be useful to announce them anyway, to help older implementations that would otherwise fall back to obsolete, insecure algorithms and mechanisms ๐Ÿงš

if the CA supports none of the above capabilities, it SHOULD return an empty message ๐Ÿ“ a CA MAY also just return an HTTP error ๐Ÿ’€ and a client that gets an empty message or an HTTP error SHOULD interpret the response as if none of the listed capabilities are supported by the CA ๐Ÿ™ˆ

โš ๏ธ bestie, this one's a real plot twist: at least one widely deployed server implementation supports several of the above operations but doesn't support the GetCACaps message to say so, and it will close the connection if sent a GetCACaps message ๐Ÿ˜ญ so the equivalent of GetCACaps has to be done through server fingerprinting, which can use the ID string "Microsoft-IIS" ๐Ÿ•ต๏ธโ€โ™€๏ธ newer versions of that same server, if sent a SCEP request using AES and SHA-2, respond with an invalid response that can't be decrypted, so you have to use 3DES and SHA-1 to get a response you can actually process, even if AES and/or SHA-2 are supposedly supported ๐Ÿ’€ and the server generates CA certificates that only have one, but not both, of the keyEncipherment and digitalSignature keyUsage flags set, which means the client has to ignore the keyUsage flags to use those certificates for SCEP ๐Ÿซ 

the Content-type of the reply SHOULD be "text/plain" ๐Ÿ’œ clients SHOULD ignore the Content-type anyway, since older SCEP implementations may send various Content-types ๐ŸŒˆ

example time ๐Ÿง

GET /cgi-bin/pkiclient.exe?operation=GetCACaps HTTP/1.1

might return:

AES
GetNextCACert
POSTPKIOperation
SCEPStandard
SHA-256

this means the CA supports modern crypto algorithms ๐Ÿ”ฅ and the GetNextCACert message lets PKIOperation messages (PKCSReq/RenewalReq, GetCert, CertPoll, ...) be sent using HTTP POST โœจ plus it's compliant with the final version of the SCEP standard ๐Ÿ‘‘


4. SCEP Transactions โœจ

this section covers the SCEP Transactions and their HTTP [RFC7230] transport mechanism ๐Ÿฆ‹

heads up though, SCEP doesn't follow best current practices on HTTP usage ๐Ÿ˜ฌ in particular, it recommends ignoring some media types and hard-codes specific URI paths ๐Ÿ’€ if you want guidance on applying HTTP properly in these situations, check out [HTTP] ๐Ÿซถ

4.1. HTTP POST and GET Message Formats ๐ŸŒธ

okay so SCEP talks to the CA over HTTP, using both POST and GET [RFC7230] ๐Ÿ’Œ Here's the ABNF for what a client sends to a CA:

POSTREQUEST = "POST" SP SCEPPATH "?operation=" OPERATION
              SP HTTP-version CRLF

GETREQUEST = "GET" SP SCEPPATH "?operation=" OPERATION
             "&message=" MESSAGE SP HTTP-version CRLF

Here's what each piece means ๐Ÿงš

  • SCEPPATH is the HTTP URL path for accessing the CA. Clients SHOULD set SCEPPATH to the fixed string "/cgi-bin/pkiclient.exe" unless directed to do otherwise by the CA.
  • OPERATION depends on the SCEP transaction and is defined in the following sections.
  • HTTP-version is the HTTP version string, which is "HTTP/1.1" for [RFC7230].
  • SP and CRLF are space and carriage return/linefeed, as defined in [RFC5234].

The CA will usually ignore SCEPPATH, because it's not likely to be handing out certificates through a web server anyway ๐Ÿ™ˆ Clients SHOULD set SCEPPATH to "/cgi-bin/pkiclient.exe" unless the CA says otherwise. The CA SHOULD ignore the SCEPPATH unless its exact format is critical to how the CA works.

Early SCEP drafts sent everything through GET, even the non-idempotent stuff that should've gone through POST. See [HTTP] for the details ๐Ÿ˜ญ This caused real problems. A supposedly idempotent GET gets messed up by caches and proxies, and the super-long GET requests you get from encoding CMS messages can get cut off in transit. These issues usually don't show up when you test on a LAN, but they crop up once you deploy over WANs. So, if the remote CA supports POST, the CMS-encoded SCEP messages MUST be sent via HTTP POST instead of HTTP GET. This applies to every SCEP message except GetCACert, GetNextCACert, and GetCACaps, and it skips the base64 and URL encoding that GET needs โœจ A client can check whether the CA supports SCEP messages over POST by looking for the "SCEPStandard" or "POSTPKIOperation" capability (see Section 3.5.2).

If a client or CA uses HTTP GET and runs into HTTP-related trouble, like messages getting truncated, errors such as HTTP 414 ("Request-URI too long"), or messages just never arriving even though normal requests to the server (like from a web browser) work fine, that's a sign GET is being used in a problematic way ๐Ÿšฉ The fix is to switch the implementation over to HTTP POST. Also, when you're using GET, it's recommended to test from as many different network locations as possible to see whether GET will cause communication problems.

When you're using GET to send binary data, base64 encoding as specified in Section 4 of [RFC4648] MUST be used. Base64 output is different from "base64url" and can contain URI reserved characters, so the encoded data MUST also be escaped as specified in [RFC3986]. Once that's done, the encoded data goes into the MESSAGE portion of the HTTP GET request ๐Ÿ’…

4.2. Get CA Certificate ๐ŸŽ€

To grab the CA certificate(s), the client sends a GetCACert message to the CA. The OPERATION MUST be set to "GetCACert". There's no request data attached to this message.

4.2.1. Get CA Certificate Response Message Format ๐Ÿฆ‹

The response for GetCACert is different depending on whether the CA talks to the client directly during enrolment, or whether an intermediate CA sits in between and the client talks to that one instead.

4.2.1.1. CA Certificate Response Message Format

If the CA doesn't have any intermediate CA certificates, the response is a single X.509 CA certificate. The response will have a Content-Type of "application/x-x509-ca-cert".

Content-Type: application/x-x509-ca-cert

<binary X.509>

4.2.1.2. CA Certificate Chain Response Message Format

If the CA does have intermediate CA certificates, the response is a degenerate certificates-only CMS SignedData message (Section 3.4) holding those certificates, with the intermediate CA certificate(s) as the leaf certificate(s). The response will have a Content-Type of "application/x-x509-ca-ra-cert". This label sticks around for historical reasons, since older versions of this specification used it. No special meaning should be attached to the label ๐Ÿ™ƒ

Content-Type: application/x-x509-ca-ra-cert

<binary CMS>

4.3. Certificate Enrolment/Renewal ๐Ÿ’–

A PKCSReq/RenewalReq (Section 3.3.1) message is used for a certificate enrolment or renewal transaction. The OPERATION MUST be set to "PKIOperation". When you're using HTTP POST, "PKIOperation" is the only OPERATION that's possible, so a lot of CAs don't bother checking that value or even notice when it's missing ๐Ÿ’€ When it's implemented with HTTP POST, the message is sent with a Content-Type of "application/x-pki-message" and looks something like this:

POST /cgi-bin/pkiclient.exe?operation=PKIOperation HTTP/1.1
Content-Length: <length of data>
Content-Type: application/x-pki-message

<binary CMS data>

With HTTP GET, it might look like this:

GET /cgi-bin/pkiclient.exe?operation=PKIOperation& \
message=MIAGCSqGSIb3DQEHA6CAMIACAQAxgDCBzAIBADB2MG \
IxETAPBgNVBAcTCE......AAAAAA== HTTP/1.1

4.3.1. Certificate Enrolment/Renewal Response Message ๐ŸŒˆ

If the request is granted, the CA sends back a CertRep SUCCESS message (Section 3.3.2.1) โœ… If it's rejected, you get a CertRep FAILURE message (Section 3.3.2.2) instead ๐Ÿ’” If the CA is set up to manually authenticate the client, a CertRep PENDING message (Section 3.3.2.3) MAY be returned. The CA MAY return a PENDING for other reasons too.

The response will have a Content-Type of "application/x-pki-message".

Content-Type: application/x-pki-message

<binary CertRep message>

4.4. Poll for Client Initial Certificate โณ

When the client gets a CertRep message with pkiStatus set to PENDING, it enters the polling state and keeps sending CertPoll messages to the CA. It keeps this up until one of three things happens: the request is granted and the certificate comes back, the request gets rejected, or a preconfigured polling time limit or maximum number of polls is exceeded. The OPERATION MUST be set to "PKIOperation".

CertPoll messages sent during the polling period MUST carry the same transactionID attribute as the previous PKCSReq/RenewalReq. If the CA gets a CertPoll that doesn't match any PKCSReq/RenewalReq it has, it MUST reject that request ๐Ÿšซ

Since the certificate hasn't been issued yet, the client can only use its own subject name (which was in the original PKCS# 10 sent with PKCSReq/RenewalReq) to identify the polled certificate request. (But check out the note on identification during polling in Section 3.3.3 too ๐Ÿ‘€) In theory, one client could have several requests outstanding, like if different keys and different key usages were used to request multiple certificates. That's why the transactionID also has to be included, so the requests can be told apart. In practice, though, the client SHOULD NOT have multiple requests outstanding at any one time, because it tends to confuse some CAs ๐Ÿ˜ตโ€๐Ÿ’ซ

4.4.1. Polling Response Message Format ๐Ÿง

The response messages for CertPoll are the same as the ones in Section 4.3.1.

4.5. Certificate Access ๐Ÿ”

A client can ask the SCEP CA for an issued certificate, as long as it knows the issuer name and the issuer-assigned certificate serial number.

This transaction is one GetCert (Section 3.3.4) message sent from the client to the CA, followed by one CertRep (Section 3.3.2) message sent back from the CA. The OPERATION MUST be set to "PKIOperation".

4.5.1. Certificate Access Response Message Format ๐Ÿ“œ

In this case the CertRep from the CA is the same as the one in Section 4.3.1, except the CA will either grant the request (SUCCESS) or reject it (FAILURE).

4.6. CRL Access ๐Ÿ“‹

Clients can request a CRL from the SCEP CA, as described in Section 2.7. The OPERATION MUST be set to "PKIOperation".

4.6.1. CRL Access Response Message Format ๐Ÿ—‚๏ธ

The CRL gets sent back to the client in a CertRep (Section 3.3.2) message. The information portion of that message is a degenerate certificates-only SignedData (Section 3.4) that contains only the most recent CRL, which sits in the crls field of the SignedData.

4.7. Get Next Certificate Authority Certificate ๐Ÿ”ฎ

When a CA certificate is about to expire, clients need to fetch the CA's next CA certificate, which is the rollover certificate. This happens with the GetNextCACert message. The OPERATION MUST be set to "GetNextCACert". There's no request data attached to this message.

4.7.1. Get Next CA Response Message Format ๐ŸŒท

The response is a SignedData CMS message, signed with the current CA signing key. Clients MUST validate the signature on the message before they trust any of its contents ๐Ÿ” The response will have a Content-Type of "application/x-x509-next-ca-cert".

Content-Type: application/x-x509-next-ca-cert

<binary CMS>

The content of the SignedData message is a degenerate certificates-only SignedData message (Section 3.4) with the new CA certificate(s) that get used once the current CA certificate expires.

5. SCEP Transaction Examples ๐ŸŽฌ

This section walks through a few examples of client-to-CA transactions. Client actions are on the left, CA actions are on the right, and the transactionID is in parentheses. Small integers are used to keep things easy to read, but in real life you'd use full transaction IDs. Here's what the first transaction looks like in words:

|  Client Sends PKCSReq message with transactionID 1 to the CA.  The
|  CA signs the certificate and constructs a CertRep Message
|  containing the signed certificate with a transaction ID 1.  The
|  client receives the message and installs the certificate locally.

5.1. Successful Transactions ๐Ÿฅณ

PKCSReq (1)             ----------> CA issues certificate
                        <---------- CertRep (1) SUCCESS
Client installs certificate

      Figure 7: Successful Enrolment Case: Automatic Processing
PKCSReq (2)             ----------> Cert request goes into queue
                        <---------- CertRep (2) PENDING
CertPoll (2)            ----------> Still pending
                        <---------- CertRep (2) PENDING
CertPoll (2)            ----------> CA issues certificate
                        <---------- CertRep (2) SUCCESS
Client installs certificate

 Figure 8: Successful Enrolment Case: Manual Authentication Required
GetNextCACert         ---------->
                      <---------- New CA certificate

PKCSReq*              ----------> CA issues certificate with
                                  new key
                      <---------- CertRep SUCCESS
Client stores certificate
for installation when
existing certificate expires.

               Figure 9: CA Certificate Rollover Case

* Enveloped for the new CA certificate.  The CA will use the envelope
to determine which key to use to issue the client certificate.

5.2. Transactions with Errors

okay so, polled transactions that don't finish automatically can get interrupted by network or software/hardware issues ๐Ÿ˜ญ. There are two ways to recover. Option one: the client saves its transaction state and resumes the CertPoll polling once service is back ๐Ÿ”„. Option two: the client starts a whole new transaction by sending a new PKCSReq/RenewalReq instead of continuing the old CertPoll ๐ŸŒธ. Both have pros and cons, so let's break it down bestie ๐Ÿซถ

Resuming the CertPoll means the client has to hang onto its transaction state until it starts polling again. That's pretty chill if it was just a quick network blip, but it gets messy if the client crashed and restarted ๐Ÿ’€. Also, the CA might treat a dropped connection as the end of the transaction, so a new connection followed by a CertPoll would count as an error ๐Ÿ™ˆ

The PKCSReq/RenewalReq route is the simpler one, since it doesn't need any state at all. It's a brand-new transaction, full stop ๐Ÿฆ‹ BUT, and this is a big but ๐Ÿšจ, if the certificate was already issued and the client never got it, the retry looks like a request for a duplicate certificate (see Section 7.4 for why that's a problem). The CA might refuse the transaction or need a human to remove or revoke the old certificate before the client can ask for another one ๐Ÿ˜ฉ

Since the new-transaction resume is sturdier when things go wrong and doesn't need any special handling from either the client or the CA, clients SHOULD use the new-transaction option instead of the resumed-CertPoll option to recover from errors ๐Ÿ‘‘

Resync Case 1: Client resyncs via new PKCSReq (recommended):

PKCSReq (3)           ----------> Cert request goes into queue
                      <---------- CertRep (3) PENDING
CertPoll (3)          ----------> Still pending
                        X-------- CertRep(3) PENDING
(Network outage)
(Client reconnects)
PKCSReq (4)           ---------->
                      <---------- CertRep (4) PENDING
etc...

Figure 10: Resync Case 1

Resync Case 2: Client resyncs via resumed CertPoll after a network outage (not recommended; use PKCSReq to resync):

PKCSReq (5)           ----------> Cert request goes into queue
                      <---------- CertRep (5) PENDING
CertPoll (5)          ----------> Still pending
                        X-------- CertRep(5) PENDING
(Network outage)
(Client reconnects)
CertPoll (5)          ----------> CA issues certificate
                      <---------- CertRep (5) SUCCESS
Client installs certificate

Figure 11: Resync Case 2

Resync Case 3: Special-case variation of Case 2 where the CertRep SUCCESS rather than the CertRep PENDING is lost (recommended):

PKCSReq (6)           ----------> Cert request goes into queue
                      <---------- CertRep (6) PENDING
CertPoll (6)          ----------> Still pending
                      <---------- CertRep (6) PENDING
CertPoll (6)          ----------> CA issues certificate
                        X-------- CertRep(6) SUCCESS
(Network outage)
(Client reconnects)
PKCSReq (7)           ----------> There is already a valid
                                  certificate with this
                                  Distinguished Name (DN).
                      <---------- CertRep (7) FAILURE
                                  Admin revokes certificate
PKCSReq (8)           ----------> CA issues new certificate
                      <---------- CertRep (8) SUCCESS
Client installs certificate

Figure 12: Resync Case 3

Resync Case 4: Special-case variation of Case 1 where the CertRep SUCCESS rather than the CertRep PENDING is lost (not recommended; use PKCSReq to resync):

PKCSReq (9)           ----------> Cert request goes into queue
                      <---------- CertRep (9) PENDING
CertPoll (9)          ----------> Still pending
                      <---------- CertRep (9) PENDING
CertPoll (9)          ----------> CA issues certificate
                        X-------- CertRep(9) SIGNED CERT
(Network outage)
(Client reconnects)
CertPoll (9)          ----------> Certificate already issued
                      <---------- CertRep (9) SUCCESS
Client installs certificate

Figure 13: Resync Case 4

As these examples show, recovering with a resumed CertPoll is tricky, because both the client and/or the CA have to hold onto state ๐Ÿงฉ A PKCSReq/RenewalReq resume is the easiest to implement. It's stateless and works the same way for polled and nonpolled transactions, whereas a CertPoll resume treats them differently. (A nonpolled transaction is resumed with a PKCSReq/RenewalReq, and a polled transaction is resumed with a CertPoll.) For that reason, error recovery SHOULD be handled via a new PKCSReq rather than a resumed CertPoll ๐Ÿ’–


6. IANA Considerations โœจ

IANA got an object identifier arc for SCEP Attribute Identifiers in the "SMI Security for PKIX" registry (1.3.6.1.5.5.7) ๐ŸŽ€ This identifier, Simple Certificate Enrollment Protocol Attributes, is called id-scep:

id-scep OBJECT IDENTIFIER ::= { id-pkix 24 }

IANA then made the "SMI Security for SCEP Attribute Identifiers" registry (1.3.6.1.5.5.7.24) and added the following entry, which points back to this document ๐ŸŒˆ

id-scep-failInfoText OBJECT IDENTIFIER ::= { id-scep 1 }

New entries in this registry are assigned under the "Specification Required" policy defined in [RFC8126] ๐Ÿ“‹

Section 3.2.1.2 describes the "SCEP Message Type" registry, and Section 3.5 describes the "SCEP CA Capabilities" registry ๐Ÿ“ IANA maintains both, and they hold a bunch of code-point identifiers. New entries are also assigned under the "Specification Required" policy defined in [RFC8126] ๐Ÿ’…

The "SCEP Message Types" registry has "Value", "Name", "Description", and "Reference" columns. Values are small positive integers, and the value "0" is reserved ๐Ÿ”ข

The "SCEP CA Capabilities" registry has "Keyword", "Description", and "Reference" columns. Implementations SHOULD use this registry, but SCEP often gets deployed in places where that isn't possible ๐Ÿฅบ In that case, private-use CA capabilities can be named with a unique prefix, like an organisation identifier or a domain name the defining entity controls. For example, the prefix would be "Example.com-", so the full capability would be "Example.com-CapabilityName" ๐Ÿงš

IANA registered four media types as defined in this document ๐Ÿฆ‹

  • application/x-x509-ca-cert
  • application/x-x509-ca-ra-cert
  • application/x-x509-next-ca-cert
  • application/x-pki-message

These are grandfathered media types, registered under Appendix A of [RFC6838] ๐Ÿ“œ The registration templates are below.

6.1. Registration of the application/x-x509-ca-cert Media Type

  • Type name: application
  • Subtype name: x-x509-ca-cert
  • Required parameters: none
  • Optional parameters: none
  • Encoding considerations: binary
  • Security considerations: heads up bestie, this one carries a certificate, so check the Security Considerations section of [RFC5280] for the full picture ๐Ÿ” Otherwise there's no executable content, so it's safe vibes โœจ
  • Interoperability considerations: this is a grandfathered registration of an alias to application/pkix-cert, which is basically a single DER-encoded Certification Authority certificate. It's only used in SCEP ๐ŸŒธ
  • Published specification: RFC 8894
  • Applications that use this media type: SCEP uses this media type when returning a CA certificate ๐Ÿ’–
  • Fragment identifier considerations: N/A
  • Additional information:
    • Deprecated alias names for this type: N/A
    • Magic number(s): none
    • File extension(s): N/A
    • Macintosh file type code(s): N/A
  • Person and email address to contact for further information: see the Authors' Addresses section of RFC 8894 ๐Ÿ“ฌ
  • Intended usage: LIMITED USE
  • Restrictions on usage: SCEP protocol
  • Author: see the Authors' Addresses section of RFC 8894
  • Change controller: IETF
  • Provisional registration? No

6.2. Registration of the application/x-x509-ca-ra-cert Media Type

  • Type name: application
  • Subtype name: x-x509-ca-ra-cert
  • Required parameters: none
  • Optional parameters: none
  • Encoding considerations: binary
  • Security considerations: this one is a degenerate certificates-only CMS SignedData message (Section 3.4) holding the certificates, with the intermediate CA certificate(s) as the leaf certificate(s) ๐ŸŒฟ No executable content here either ๐Ÿ’…
  • Interoperability considerations: this is a grandfathered registration that's only used in SCEP ๐Ÿงก
  • Published specification: RFC 8894
  • Applications that use this media type: SCEP uses this media type when returning CA Certificate Chain Response ๐Ÿ”—
  • Fragment identifier considerations: N/A
  • Additional information:
    • Deprecated alias names for this type: N/A
    • Magic number(s): none
    • File extension(s): N/A
    • Macintosh file type code(s): N/A
  • Person and email address to contact for further information: see the Authors' Addresses section of RFC 8894 ๐Ÿ“ฌ
  • Intended usage: LIMITED USE
  • Restrictions on usage: SCEP protocol
  • Author: see the Authors' Addresses section of RFC 8894.
  • Change controller: IETF
  • Provisional registration? no

6.3. Registration of the application/x-x509-next-ca-cert Media Type

  • Type name: application
  • Subtype name: x-x509-next-ca-cert
  • Required parameters: none
  • Optional parameters: none
  • Encoding considerations: binary
  • Security considerations: this one is a SignedData CMS message, signed with the current CA signing key ๐Ÿ” No executable content, promise ๐ŸŽ€
  • Interoperability considerations: this is a grandfathered registration that's only used in SCEP ๐ŸŒท
  • Published specification: RFC 8894
  • Applications that use this media type: SCEP uses this media type when returning a Get Next CA response โญ
  • Fragment identifier considerations: N/A
  • Additional information:
    • Deprecated alias names for this type: N/A
    • Magic number(s): none
    • File extension(s): N/A
    • Macintosh file type code(s): N/A
  • Person and email address to contact for further information: see the Authors' Addresses section of RFC 8894 ๐Ÿ“ฌ
  • Intended usage: LIMITED USE
  • Restrictions on usage: SCEP protocol
  • Author: see the Authors' Addresses section of RFC 8894.
  • Change controller: IETF
  • Provisional registration? no

6.4. Registration of the application/x-pki-message Media Type ๐ŸŽ€

okay so this one is the media type for the Certificate Enrolment/Renewal Response, and it's honestly the most grandfathered little guy ever ๐Ÿงš Here's the registration template, copied exactly:

   Type name:  application

   Subtype name:  x-pki-message

   Required parameters:  none

   Optional parameters:  none

   Encoding considerations:  binary

   Security considerations:  This media type consists of a degenerate
      certificates-only CMS SignedData message.  There is no executable
      content.

   Interoperability considerations:  This is a grandfathered
      registration that is only used in SCEP.

   Published specification:  RFC 8894

   Applications that use this media type:  SCEP uses this media type
      when returning a Certificate Enrolment/Renewal Response.

   Fragment identifier considerations:  N/A

   Additional information:

      Deprecated alias names for this type:  N/A

      Magic number(s):  none

      File extension(s):  N/A

      Macintosh file type code(s):  N/A

   Person and email address to contact for further information:  See the
      Authors' Addresses section of RFC 8894.

   Intended usage:  LIMITED USE

   Restrictions on usage:  SCEP protocol

   Author:  See the Authors' Addresses section of RFC 8894.

   Change controller:  IETF

   Provisional registration?  no

7. Security Considerations ๐Ÿ›ก๏ธ

okay the big security goal here is that no adversary can mess with the public key/identity binding, and that's literally the whole point ๐Ÿ˜ค An adversary is any entity other than the client and the CA taking part in the protocol. Anyone else is the villain ๐Ÿ’€

this goal gets met using CMS and PKCS #10 encryption and digital signatures with authenticated public keys ๐Ÿ” The CA's public key is authenticated out-of-band, like by checking the CA fingerprint, and the SCEP client's public key is authenticated through manual or preshared secret authentication.

7.1. General Security ๐Ÿงฑ

basic key-management stuff matters so much here ngl: keep private keys truly private, and use adequate lengths for symmetric and asymmetric keys. That's how this protocol stays secure. CA keys are the most important ones of all, because if one gets compromised, the security of every relying party goes down with it ๐Ÿ˜ญ

7.2. Use of the CA Private Key ๐Ÿ”‘

a CA private key is usually meant for, and flagged as, certificate (and CRL) signing only. It's not for data signing or encryption, periodt. SCEP, though, uses the CA private key to sign and optionally encrypt CMS transport messages, and that's generally considered undesirable ๐Ÿ™ƒ It widens the chance of an implementation weakness, gives the private key one more place where it has to be used (so it's a bit more exposed), and opens the door to a side-channel attack.

7.3. challengePassword Shared Secret Value ๐Ÿง

the measures for the challengePassword shared secret depend on how SCEP is being used. In the simplest case, SCEP is used to provision devices with certificates in the manufacturing facility, and the physical security of the facility might be enough by itself, with no extra measures explicitly required. In general though, the security of the issue process depends on the security around the challengePassword shared secret ๐Ÿซถ It's not possible to list every situation where SCEP might show up, so these are the measures to think about:

  • The challengePassword, despite its name, shouldn't be a conventional password. It should be a high-entropy shared-secret authentication string. Using the base64 encoding of a keying value generated or exchanged as part of standard device authentication protocols like the Extensible Authentication Protocol (EAP) or DNP3 Secure Authentication (DNP3-SA) makes for a good challengePassword โœจ High-entropy shared secrets are especially important when the PasswordRecipientInfo option is used to encrypt SCEP messages; see Section 3.1.
  • If it's feasible, the challengePassword should be a one-time value used to authenticate the issue of a single certificate. Later certificate requests get authenticated by being signed with the initial certificate, so it's not a big deal. If the challengePassword is single use, then seeing later requests that use the same challengePassword can be used to indicate a security breach ๐Ÿšจ
  • The lifetime of a challengePassword can be limited, so it works during initial device provisioning but has expired by a later date if an attacker manages to compromise the challengePassword value. For example, that could happen by compromising the device it's stored in ๐Ÿงš
  • The CA should take appropriate measures to protect the challengePassword. Examples of possible measures include physical security measures, storing it as a salted iterated hash or equivalent memory-hard function, storing it as a keyed MAC value if it's not being used for encryption, and storing it in encrypted form if it is being used for encryption ๐Ÿ’…

7.4. Lack of Certificate Issue Confirmation ๐Ÿ“ญ

SCEP gives no confirmation that the issued certificate was successfully received and processed by the client ๐Ÿ˜ฌ So if the CertRep message gets lost or the client can't process it, the CA will consider the certificate successfully issued while the client won't. If that's a concern, the correct issuance needs to be verified out-of-band. One option is the client sending a message signed by the newly issued certificate to the CA. That also gives the proof of possession that's missing in a renewal operation; see Section 7.6.

7.5. GetCACaps Issues ๐Ÿšฉ

the GetCACaps response isn't authenticated by the CA, and that opens the door for an attacker to perform downgrade attacks on the cryptographic capabilities of the client/CA exchange ๐Ÿ˜ค Like, if the server supported MD5 and single DES, an in-path attacker could totally roll back the encryption to those insecure algorithms. There's a lot of static known plaintext in SCEP messages, and as of 2017, a DES rainbow table attack could recover most encryption keys in under a minute. MD5 chosen-prefix collisions can be calculated for a few tens of cents of computing time using tools like HashClash ๐Ÿ’€ That's why this specification makes single DES and MD5 a MUST NOT feature. Also, all known servers support at least triple DES and SHA-1 (whether or not "DES3" and "SHA-1" are indicated in GetCACaps), so there should never be a reason to fall all the way back to single DES and MD5.

one simple countermeasure to a GetCACaps downgrade attack: clients working in an environment where on-path attacks are possible, which expect the "SCEPStandard" capability from the CA but don't see it in the GetCACaps response, can treat that absence as a security issue. Then they can either discontinue the exchange or continue as if "SCEPStandard" had been returned ๐Ÿง  This takes a certain trade-off between compatibility with old servers and security against active attacks.

7.6. Lack of PoP in Renewal Requests ๐Ÿ”„

renewal operations (but not standard certificate-issue operations) are processed via a previously issued certificate and its associated private key, not the key in the PKCS #10 request. So a client doesn't demonstrate proof of possession (PoP) of the private key for the public key in the PKCS #10 request anymore ๐Ÿ™ˆ That means a client could recertify an existing key that a third party uses, so two or more certificates exist for the same key. By swapping out the certificate in a signature, an attacker can make it look like a piece of data was signed by their certificate instead of the original signer's. This and other attacks are described in S/MIME ESS [RFC2634].

avoiding these attacks needs situation-specific measures. For example, CMS/SMIME implementations may use the ESSCertID attribute from S/MIME ESS [RFC2634] or its successor, S/MIME ESSv2 [RFC5035], to unambiguously identify the signing certificate. But other mechanisms and protocols that the certificates get used with usually don't defend against this problem, so it's unclear whether this is an actual issue with SCEP ๐Ÿคทโ€โ™€๏ธ

7.7. Traffic Monitoring ๐Ÿ‘€

SCEP messages are signed with certificates that may contain identifying information. If those go over the public Internet, and the signing certificate data includes real identity information (instead of placeholder values or arbitrary device IDs), an attacker may be able to monitor the identities of the entities submitting certificate requests ๐Ÿ˜ฑ If this is an issue, [RFC7258] is the place to look for guidance.

7.8. Unnecessary Cryptography ๐Ÿ™„

some of the SCEP exchanges use signing and encryption operations they don't really need ๐Ÿ’… In particular, the GetCert and GetCRL exchanges are encrypted and signed in both directions. The information requested is public, so encrypting the requests is of questionable value. CRLs and certificates sent in responses are already signed by the CA and can be verified by the recipient without extra signing and encryption. For that reason, more lightweight ways of retrieving certificates and CRLs are recommended, such as HTTP certificate-store access [RFC4387] and LDAP ๐Ÿฆ‹

7.9. Use of SHA-1 โœจ

okay so the vast majority of the SCEP devices out there today default to SHA-1 ๐Ÿฅน, and a lot of them only support that hash with zero way to upgrade to something newer. SHA-1 is no longer regarded as secure in all situations, but as used in SCEP, it's still safe ๐Ÿ’…. There are three reasons for this ๐ŸŽ€.

The first reason is that attacking SCEP would need a fully general SHA-1 collision made in close to real time, and a broken AES too ๐Ÿ˜ญ. More specifically, the attacker would need to make a fully general SHA-1 collision for the PKCS #10 request, break the AES encryption around that PKCS #10 request, and then make a second SHA-1 collision for the signature on the encrypted data. That's giving "not happening for a long time" energy ๐Ÿ™ˆ.

The second reason is that the signature over the message doesn't do any critical crypto work ๐Ÿง. In other words, the SHA-1 hash that isn't protected by encryption isn't carrying the weight here. The PKCS #10 data is already authenticated by its own signature and protected by encryption, and the whole request is authorised by the (encrypted) shared secret ๐Ÿ”. The one sole exception is the small number of implementations that support the Renewal operation. Those can be authorised purely through a signature, but presumably any implementation recent enough to support Renewal also supports SHA-2 ๐ŸŒˆ. Any legacy implementation that supports the historic core SCEP protocol wouldn't be affected at all ๐Ÿฆ‹.

The third reason is the iconic one ๐Ÿ‘‘. SCEP uses the same key for encryption and signing. So even if an attacker managed to capture an outgoing renewal request that didn't include a shared secret (so one that was only authorised through a signature), break the AES encryption, forge the SHA-1 hash in real time, and forward the forged request to the CA, they still couldn't decrypt the returned certificate. That's because it's protected with the same key that was used to generate the signature ๐Ÿ”‘. Section 7.8 points out that SCEP uses unnecessary cryptography in places, and honestly? The extra level of security that the extra crypto provides is what makes it immune to any issues with SHA-1 ๐Ÿ’–.

This doesn't mean SCEP implementations should keep using SHA-1 forever. It just means there's no need for a panicked switch to SHA-2 ๐Ÿ˜Œ.

7.10. Use of HTTP โœจ

SCEP is an encrypted, authenticated certificate enrollment protocol that uses HTTP as a simple transport mechanism ๐Ÿšš. Since SCEP messages are already cryptographically secured, it doesn't need transport layer security. Where HTTPS is elected, a performance hit may result from the TLS overhead, operational problems may result from the more complex configuration, and a potential security vulnerability may result from adding an entire TLS protocol stack alongside the basic SCEP protocol ๐Ÿซ .

In particular, experience has shown that configuring certificates, CAs, and trust for both TLS and SCEP often leads to interoperability problems, because different certificates and trust models get used in each one ๐Ÿ˜ตโ€๐Ÿ’ซ. Using HTTPS to authenticate the server does not let you leave out the ChallengePassword or a similar authenticator from the SCEP message. Nope, not on the assumption that switching from HTTP to HTTPS will somehow make this insecure usage secure again ๐Ÿ™…โ€โ™€๏ธ. HTTPS is not soy sauce for security ๐Ÿฃ, and it's unnecessary for SCEP, which uses cryptographically secured messages and doesn't require transport layer security. Respect the message-level crypto, bestie ๐Ÿ’œ.


8. References ๐Ÿ“š

8.1. Normative References

8.2. Informative References

Appendix A. Background Notes โœจ

okay so this spec has been cooking for over twenty years ๐Ÿ˜ญ It started out as a way to get certificates onto IPsec routers, but now it's basically the go-to for general device, embedded system, and IoT stuff ๐Ÿ”Œ To make it fit that role, people kept bolting on extra features through a growing pile of appendices and by sticking in extra, often conflicting, paragraphs all over the body text ๐Ÿ’€ Existing features never got updated when newer ones showed up, so the spec built up a ton of historical baggage. If OpenPGP was "a museum of 1990s crypto," then SCEP was its graveyard ๐Ÿชฆ

About five years ago, the original sponsors more or less dropped it, even though it had only ever gotten sporadic reposts. But it's used in big chunks of the industry, so it got a glow-up in 2015 ๐Ÿฆ‹ That cleaned up fifteen years of accumulated cruft, fixed errors, cleared up ambiguities, and moved the algorithms into the current century. Before that, the de facto lowest-common-denominator algorithms for interop were the insecure forty-year-old single DES and the broken MD5 hash algorithms ๐Ÿ™ˆ

Here's the tea: even though the text changed a lot from folding features and appendices into the main doc, the protocol is still identical on the wire to the original. The only unavoidable exception is the switch from single DES and MD5 to AES and SHA-2 โœจ The only two new changes, the "SCEPStandard" indicator in GetCACaps and the failInfoText attribute, are both optional values. Older implementations that don't support them would just ignore them, or they can be left out of messages if they cause problems ๐ŸŒธ

Other changes include:

  • Resolved contradictions in the text -- for example, a requirement given as a MUST in one paragraph and a SHOULD in the next, a MUST NOT in one paragraph and a MAY a few paragraphs later, a SHOULD NOT contradicted later by a MAY, and so on. ๐Ÿ’…
  • Merged several later fragmentary addenda placed in appendices (for example, the handling of certificate renewal) with the body of the text.
  • Merged the "SCEP Transactions" and "SCEP Transport" sections, since the latter mostly duplicated (with occasional inconsistencies) the former.
  • Updated the algorithms to ones dating from at least this century.
  • Did the same for normative references to other standards.
  • Updated the text to use consistent terminology for the client and CA rather than a mixture of client, requester, requesting system, end entity, server, certificate authority, certification authority, and CA. ๐ŸŽ€
  • Corrected incorrect references to other standards, e.g., IssuerAndSerial -> IssuerAndSerialNumber.
  • Corrected errors such as a statement that when both signature and encryption certificates existed, the signature certificate was used for encryption.
  • Condensed redundant discussions of the same topic spread across multiple sections into a single location. For example, the description of intermediate CA handling previously existed in three different locations, with slightly different requirements in each one. ๐Ÿงš
  • Added a description of how pkiMessages were processed, which was never made explicit in the original specification. This led to creative interpretations that had security problems but were employed anyway due to the lack of specific guidance on what to do. ๐Ÿ˜ญ
  • Relaxed some requirements that didn't serve any obvious purpose and that major implementations didn't seem to be enforcing. For example, the requirement that the self-signed certificate used with a request MUST contain a subject name that matched the one in the PKCS #10 request was relaxed to a SHOULD, because a number of implementations either ignored the issue entirely or at worst performed some minor action like creating a log entry, after which they continued anyway. ๐Ÿ™ˆ
  • Removed discussion of the transactionID from the security considerations, since the instructions there were directly contradicted by the discussion of the use of the transactionID in Section 5.
  • Added a requirement that the signed message include the signing certificate(s) in the signedData certificates field. This was implicit in the original specification (without it, the message couldn't be verified by the CA) and was handled by the fact that most PKCS #7/CMS libraries do this by default, but was never explicitly mentioned. ๐ŸŒˆ
  • Clarified sections that were unclear or even made no sense -- for example, the requirement for a "hash on the public key" [sic] encoded as a PrintableString. ๐Ÿค”
  • Renamed "RA certificates" to "intermediate CA certificates". The original document at some point added mention of RA certificates without specifying how the client was to determine that an RA was in use, how the RA operations were identified in the protocol, or how it was used. It's unclear whether what was meant was a true RA or merely an intermediate CA, as opposed to the default practice of having certificates issued directly from a single root CA certificate. This update uses the term "intermediate CA certificates", since this seems to have been the original intent of the text. ๐Ÿฆ‹
  • Redid the PKIMessage diagram to match what was specified in CMS; the original diagram omitted a number of fields and nested data structures, which meant that the diagram didn't match either the text or the CMS specification. ๐ŸŒธ
  • Removed the requirement for a CertPoll to contain a recipientNonce, since CertPoll is a client message and will never be sent in response to a message containing a senderNonce. See also the note in Section 3.3.2. ๐Ÿ’œ
  • Clarified certificate renewal. This represents a capability that was bolted onto the original protocol with (at best) vaguely defined semantics, including a requirement by the CA to guess whether a particular request was a renewal or not. In response to developer feedback that they either avoided renewal entirely because of this uncertainty or hard-coded in particular behaviour on a per-CA basis, this specification explicitly identifies renewal requests as such and provides proper semantics for them. ๐Ÿ‘‘
  • Corrected the requirement that "undefined message types are treated as an error", since this negates the effect of GetCACaps, which is used to define new message types. In particular, operations such as GetCACaps "Renewal" would be impossible if enforced as written, because the Renewal operation was an undefined message type at the time. โญ
  • In line with the above, added IANA registries for several entries that had previously been defined in an ad hoc manner in different locations in the text. ๐Ÿ“
  • Added the "SCEPStandard" keyword to GetCACaps to indicate that the CA complies with the final version of the SCEP standard, since the definition of what constitutes SCEP standards compliance has changed significantly over the years. โœจ
  • Added the optional failInfoText attribute to deal with the fact that failInfo was incapable of adequately communicating to clients why a certificate request operation had been rejected. ๐Ÿ’–
  • Removed the discussion in the security considerations of revocation issues, since SCEP doesn't support revocation as part of the protocol. ๐Ÿงš
  • Clarified the use of nonces, which if applied as originally specified would have made the use of polling in the presence of a lost message impossible. ๐Ÿ”ฅ
  • Removed the discussion of generating a given transactionID by hashing the public key, since this implied that there was some special significance in the value generated this way. Since it was neither a MUST nor a MAY, it was unsound to imply that servers could rely on the value being generated a certain way. In addition, it wouldn't work if multiple transactions as discussed in Section 4.4 were initiated, since the deterministic generation via hashing would lead to duplicate transactionIDs. ๐Ÿ™ˆ
  • Added examples of SCEP messages to give implementers something to aim for. ๐ŸŽ€

Acknowledgements

The editor would like to thank all of the previous editors, authors, and contributors for their work maintaining the document over the years: Cheryl Madson, Xiaoyi Liu, David McGrew, David Cooper, Andy Nourse, Max Pritikin, Jan Vilhuber, and others. The IETF reviewers provided much useful feedback that helped improve the document, and in particular spotted a number of things that were present in SCEP through established practice rather than by being explicitly described in the text. Numerous other people have contributed during the long life cycle of the document, and all deserve thanks. In addition, several PKCS #7 / CMS libraries contributed to interoperability by doing the right thing despite what earlier SCEP documents required. ๐Ÿซถ

The authors of earlier draft versions of this document would like to thank Peter William of ValiCert, Inc. (formerly of VeriSign, Inc.), Alex Deacon of VeriSign, Inc., and Christopher Welles of IRE, Inc. for their contributions to early versions of this protocol and this document. ๐Ÿ’œ

Author's Address

Peter Gutmann
University of Auckland
Department of Computer Science
Auckland
New Zealand

Email: pgut001@cs.auckland.ac.nz
๐Ÿ“ฆ where this page comes from