PKCS #10 (RFC 2986) certificate signing request (X509_REQ) typically contains at minimum the subject and the public key to be signed into a X.509 certificate issued by a certificate authority (CA). Here is an example of what I send to Let's Encrypt:
$ openssl asn1parse -item X509_REQ -in worship.likai.org.csr
X509_REQ:
req_info:
version: 0
subject: CN=worship.likai.org
pubkey: X509_PUBKEY:
algor:
algorithm: id-ecPublicKey (1.2.840.10045.2.1)
parameter: OBJECT:secp384r1 (1.3.132.0.34)
public_key: (0 unused bits)
0000 - 04 e4 5c 30 85 6f b5 37-35 f4 e9 21 3b 2c 74 ..\0.o.75..!;,t
... omitted for brevity ...
005a - 33 2f c8 fb c1 21 86 3/...!.
attributes:
<EMPTY>
sig_alg:
algorithm: ecdsa-with-SHA256 (1.2.840.10045.4.3.2)
parameter: <ABSENT>
signature: (0 unused bits)
0000 - 30 66 02 31 00 ce 23 b9-c3 b0 53 24 2f d3 8c b0 0f.1..#...S$/...
... omitted for brevity ...
0060 - cc fe a2 64 fa f2 7a e6- ...d..z.
X509_REQ can also contain extensions, most notably subjectAltName. Some of the extensions are documented in x509v3_config, which includes the ability to send arbitrary extensions. PKCS #9 (RFC 2985) defines the attributes that can be used in a PKCS #10 CSR. Arbitrary extensions are defined by an object identifier (OID) key and some ASN.1 value. We can lookup the OID assignments.
As currently defined, X509_REQ does not have the ability to include authentication credentials, i.e. the CA only knows the subject and its public key, and the X509_REQ itself is signed by the same subject's public key for integrity, but the CA does not know whether the subject is whom they claim to be.
That kind of trust has to be verified out of band. In the case of Let's Encrypt (how it works), the ACME protocol performs domain validation by fetching the result of a challenge response over plaintext HTTP at the domain name matching the CN subject in the CSR. This requires the host to be reachable from the public Internet, which is not feasible for internal servers. There are other challenge types: TLS-ALPN-01 also requires the host to be reachable from the public Internet; DNS-01 does not require the host to be reachable, but the requestor must be able to modify the DNS record, which can present other difficulties.
Enrollment over Secure Transport (EST) is another protocol to automate certificate signing (like ACME). It authenticates the CSR at the API layer but not as part of the CSR. However, binding the API authentication to the CSR remains a challenge.
There are a few potential OIDs we can investigate for their suitability to include authentication credential as part of the CSR:
- PKCS #9 defines a challengePassword OID 1.2.840.113549.1.9.7, but it is supposedly used to request certificate revocation. It seems to be unimplemented. Also, it is generally a bad idea to repurpose a field for a different purpose than originally intended.
- The content of challengePassword is plaintext equivalent, which means the CSR should be protected by other means so it is not public accessible.
- RFC 7894 proposes additional fields to disambiguate the PKCS #9 challengePassword, notably: otpChallenge, revocationChallenge, estIdentityLinking.
- It assumes that the primary authentication is done by the underlying transport such as EST. The additional fields can provide a second-factor authentication.
- The SMI mechanism OID 1.3.6.1.5.5 references additional mechanisms tracked by IANA. In particular:
- The aforementioned RFC 7894.
- There is a working draft draft-ietf-lamps-csr-attestation-08 to attest the CSR with evidence, which is intended to come from a hardware TPM or PSA. Presumably, the evidence can be further extended to cover FIDO U2F or Passkeys. These evidences must not contain plaintext equivalent secrets.
- Kerberos OID 1.2.840.113554.1.2.2 (RFC 1964) does not have an OID for the challenge response, as far as I can tell.
These proposals tend to avoid putting plaintext secrets into CSR, but the ability to secure secret credentials in a CSR remains elusive.
Envelope Proposal
To be able to include secrets in CSR, we can use the CA's public key to encrypt the plaintext, and we store the ciphertext in an envelopedData. There are three potential OIDs defining envelopedData:
- 1.2.840.113549.1.7.1.3 under the PKCS #7 namespace for id-data, but seems unused.
- 1.2.840.113549.1.7.3 as part of PKCS #7 (RFC 2315)
envelopedData. - 1.2.840.113549.1.9.16.10.1 under the PKCS #9 S/MIME EIT namespace is used to transport S/MIME Encoded Information Types (RFC 3855) for X.400, an archaic messaging protocol.
The envelopedData defined by PKCS #7 (RFC 2315) is obsolete by the latest version RFC 5652, which seems to be the correct candidate to store encrypted secrets destined for the CA. If the CA has multiple certificates, they can all be included as a recipientInfo.
Inside the envelopedData should be a SEQUENCE that contains at least the SET OF Attribute{{ IOSet }} as defined in PKCS #10 (RFC 2986). This also opens up the possibility to include anything from an LDAP directory OID 2.5.4 (RFC 4519 or ISO/IEC 9594-6:2020 or X.520) that might need to be protected from public access, such as userPassword 2.5.4.35 or maybe encryptedUserPassword 2.5.4.35.2.
Afterthoughts
On one hand, having a global OID means applications and protocols can share relevant definitions when appropriate, but navigating the maze of existing OID assignments can be an exercise in frustration. It is similar to code reuse: sometimes the code you borrow from almost does what you need except for some corner cases, but you cannot change the corner case behavior because it may break existing users relying specifically on the corner case. So you have to choose the OID carefully based on how you expect the OID to evolve in the future, and try to find an OID that best aligns with the use case.
It is tempting to start from scratch and define exactly what an application needs in an application-specific way without any historical baggage, like JOSE (JWS/JWT). Their RFCs are still long, but still shorter than the maze of OIDs, RFCs, and ITU/ISO standards. It would be nearly trivial to abbreviate the CSR to another JSON object.
It is also interesting how OID leaves the breadcrumbs showing how the Internet standards evolved. At first, RSA developed the PKCS under its OID 1.2.840.113549 {iso(1) member-body(2) us(840) rsadsi(113549)}, then this namespace is donated to IANA. Similarly, the OID 1.3.6.1 {iso(1) identified-organization(3) dod(6) internet(1)} used to be owned by DoD (Department of Defense), now hijacked by IANA as well.
No comments:
Post a Comment