Offer a new endpoint (and ACME spec update) to list available chains

Obviously, this is an early stage of my idea. I am bringing this up now, and tagging several client authors, in the hopes you will be interested in collaborating on both a proposal to LetsEncrypt and eventually an RFC to the ACME working group.

cc: @rmbolger @webprofusion @mholt @_az @Neilpang @griffin

--

I propose a new endpoint is added to the /directory to list the currently available chains for a client/subscriber.

Why?

A client must know which chains are offered in order to select a "preferred chain", however the current ACME spec doesn't surface this information until an order is finalized โ€“ and at that point the chains would need to be downloaded and analyzed to present options. While this information may be found on websites and through internet searching, it fails on the following points needed for streamlined automation:

  • It is not available in a standardized machine readable format from any ACME powered CA.
  • It is not available in a standardized format that is used by multiple CAs
  • In LetsEncrypt's current service, this information can vary across accounts. [e.g. LetsEncrypt accounts are currently locked to either the X1 (default) or X2 (opt-in) chains].

Additionally, in the near future, LetsEncrypt has scheduled the following two actions:

  • Switching of the default and alternate chains
  • Retirement of the DST X3 chain

The current ACME spec is deficient in features that could be used to better automate certificate procurement in light of the above, and also handle situations in which default chains switch.

As a Client Author and ACME Subscriber, there is a need to query a server to determine the possible roots before procuring a Certificate. While a best-practices Client can (and should) download all chains - or offer the ability to easily switch/rebuild chains after download - adhering to such practices would not address scenarios such as the "X2 opt-in list" wherein a certificate could be procured using an unintended root/chain.

Aside from the immediate benefit to consumers of allowing them to query the available chains/roots, this would influence the standardization of how all clients handle chain selection. Currently (and historically), clients have used their own logic to handle chain selection โ€“ relying not just on different methods, but also different fields or information extrapolated from the chains/certificates. Some have used Issuers, Common Names, fingerprints or other methods. If the ACME spec were extended to present possible chains in a structured manner, Clients would expectedly have their logic influenced by this and rely on these fields.

An endpoint such as this would also allow for programmatic detection of the change in available or default chains โ€“ which clients could then surface to end users.

A formal specification could also state the logic compliant clients utilize. The most "official" guidance on this topic I've been able to find comes from LE's Aaron Gable in an Issue and PR against Certbot [See Providing different alternate chain selection methods ยท Issue #8577 ยท certbot/certbot ยท GitHub], and in that discussion we can see some great points.

Below is a potential format for an availableChains JSON object.

  • A key is created for every Type of the EndEntity/Leaf Certificate
  • a "_default" key indicates the default chain by Type and Position.
  • The value is a List of Certificates in the chain:
    • the 0 element is the Root, followed by the intermediates
    • Each element is a list in the format of: Common Name, Issuer, URL, Expiry, Type
{
    "availableChains": [
        "RSA": [
            [["DST Root CA X3", "DST Root CA X3", "", "September 30, 2021", "RSA"]
             ["ISRG Root X1", "DST Root CA X3", "https://letsencrypt.org/certs/isrg-root-x1-cross-signed.pem", "September 30, 2024", "RSA"]
             ],
            [["ISRG Root X1", "ISRG Root X1", "https://letsencrypt.org/certs/isrgrootx1.pem", "June 4, 2035", "RSA"],
             ]
        ], 
        "ECDSA": [
            [["ISRG Root X1", "ISRG Root X1", "https://letsencrypt.org/certs/isrgrootx1.pem", "June 4, 2035", "RSA"],
             ["ISRG Root X2", "ISRG Root X1", "https://letsencrypt.org/certs/isrg-root-x2-cross-signed.pem", "September 15, 2025", "ECDSA"]
             ],
            [["ISRG Root X2", "ISRG Root X2", "https://letsencrypt.org/certs/isrgrootx2.pem", "September 17, 2040", "ECDSA"],
             ]
        ],
        "_default": ["RSA", 0],
}

I stress: this is an early stage idea. This has been annoying me since the various default chain switches during the DST expiry. About a week or two ago, I realized a new account-specific endpoint is needed. I am not sure about the output (though I offer a suggestion) or the input (should a listing of domains be posted in the payload because it could affect the possible chains?)

10 Likes

This is a well-considered idea. (Thanks for the tag)

To add a data point to this discussion, and maybe some insight into what my clients are doing: we always download all the available chains and simply use the first one by default, but we expose a ChainPreference config parameter that allows you to select a chain based on:

  • Size (i.e. smallest number of bytes)
  • Root common name (i.e. the first chain with a root containing one of the given strings)
  • Any common name (i.e. like 'root common name' but for any cert in the chain)

This has been sufficient for my users so far; I've never had a request for selecting a chain outside of these parameters.


Overall, I'm in favor of simple changes to the spec that give clients more control/info. I'm not sure how much we would need or use this feature in my clients which are fully-automated backend infrastructure servers, but I can see some clients like with GUIs that might use this info.

My one question at this point is: this method presumes the CA will always issue certificates with these chains, is that always the case? I like the current approach because it's more like, "Here's what we got, now choose one." as opposed to, "Here's what to expect, so choose one." Then if that one doesn't appear... then what?

Ok actually I have two questions:

Any reason to not use an object with key/value pairs? The array of array of array is a little... deep :slight_smile: ... and makes the structure less flexible for changes later.

4 Likes

Thanks for bringing this up, in writing this reply I feel like I'm griping more than I'm coming up with answers, but I agree wholeheartedly that something like this should get added; there are just a lot of details to work through.

It's more complicated than that, even, in that the chains are different not just based on account, but based on whether using an ECDSA or RSA key for the certificate. (I have an E1-allowlisted account, and use it getting both ECDSA-signed-by-E1 and RSA-signed-by-R3 certificates.)

I could imagine a CA with more complicated rules (though I don't know what the "industry standard" is), such as using a 4096-bit RSA intermediate when signing a 4096-bit RSA certificate but only a 2048-bit intermediate when signing a 2048-bit cert. Or maybe just using different intermediates "randomly" at the same time. I know Amazon's CA seems to just pick a random intermediate of theirs as far as I can tell (though that isn't via ACME), and Let's Encrypt has at some point talked about the possibility of using a different intermediate depending on which datacenter is processing the request.

Yes, I think that in order to find out which chains are going to be available for a certificate, one would need to present to the server at least some of the details about it (key type and size, at the very least, though maybe domain names or the like too), and then the server might need to remember which intermediate it "promised" it would use for that client. I'm guessing that it could get really convoluted for some CAs, to make sure that further requests from an account get routed to the same datacenter or whatever in order to get the same options.

And, I think the "type" concept which seems to only have values of "RSA" or "ECDSA" might need to be expanded a bit to include other key properties, and maybe other things like "datacenter" or some concept like that.

Other things that you might want to add, somewhere, somehow, in order to make a client's life easier (though I understand this is drifting at least a bit off topic):

  • Which signature/hash methods are allowed for the CSR. (I mean, the industry in general is in for a world of hurt if there ends up being any need to move off of SHA-256, especially if the need is to move quickly. But I'm assuming it will need to go away at some point, and when it does it'd be good if there would be a way for clients to tell what hashes the server will accept before just trying them out.
  • If ARI is a thing resembling the current draft, which hash algorithms are allowed for ARI requests.
  • Which key lengths and types are supported for certificates.
  • Which key lengths and types are supported for account keys.
  • Information on applicable rate limits.
  • Whether there is support for requesting a specific notBefore or notAfter, and what constraints there are on it. (Or at the very least, some information what the default certificate lifetime is.)
  • What CSR extensions (like OCSP Must-Staple) are supported.
  • If the directory is for a "staging" environment, and maybe a link to the corresponding staging/live environment from the other. (It's kind of awkward that clients basically hard-code Let's Encrypt's staging server, and use it even when configured to use a different CA for production.)
  • Policies on revocation options (like, Let's Encrypt requests revoking for keyCompromise to be by the certificate key only.)

I guess I'm just saying, while I agree more chain information should be exposed, I'd like to add even more things while we're talking about changing things, and maybe present the idea that I'm not even sure that the chain options are the highest priority for stuff that should be added to ACME in order to allow clients to actually just have a Directory URL and go from there.

8 Likes

This alone makes the concept of an "availableChains" endpoint kind of impossible, right? At least not without a way to provide more context such as the key type of the cert you're requesting (or whatever other context the CA might use to differentiate chains).

At the moment, the CA doesn't know what the key details are for your cert until you send the CSR in the finalize step. And the very next step is when you'd be downloading the cert where the response contains the chain(s) you'd be picking from. But maybe context isn't ultimately that critical? The endpoint could return all possible chains regardless of context which is still more info than we have today.

I don't mean to sound negative because I like the concept. But this seems like it might be more trouble than it's worth. Are any of the other ACME CAs offering alternate chains? Or is this purely an LE specific issue at the moment? Do we risk tailoring a standards change too closely to the needs of one provider?

8 Likes

Well, that's why the proposal has the object first indexed by key type, so that the client can look at it and see which chain options there are based on which key type it is planning on using.

Yes, what I was trying to say in my long-winded ranting was that just because Let's Encrypt picks an intermediate based on the key type, I don't know as that's the general case among CAs. (Maybe it is, but I don't think it needs to be.)

I think that one is almost wanting something along the lines of content-negotiation, where the CA can pick chains based on aspects of the presented key, but the client might want to pick a key type/length/etc. based on which roots (and lengths of chains) it would get based on those choices. That is, both the CA and the client might be wanting to make decisions based on the other party's choice.

4 Likes

Good point. The nested list should be an object. I originally had this as a tuple of CN + URL โ€“ then realized it needed issuer and expiry.

There is a disconnect with both my intent and usage on some points. I'll address them below, but first...

Using Certbot as an example flow, as it is the most popular client: One must first determine the current options for a chain by searching the CA's online documentation. Then they must submit the chain's identifier via the --preferred-chain argument. Cerbot will then download all the chains and either: 1) use the one which best matches logic or 2) use the default if none match. All other chains are tossed (not a good idea, IMHO).

So the intended usage patterns are:

  1. querying the server to determine which chains are currently offered, so an existing client like Certbot can present the current options for commandline arguments, without needing to change their UX/UI to present an interactive chain selection.
  2. enabling a non-interactive client to detect a change in active chains before renewal

The intent of using the ACME spec to influence chain selection is to standardize what the arguments would be in order to increase interoperability between clients, and guide clients to use select criteria. Referecing the Certbot issue I linked to, a fingerprint was briefly considered but then sidelined for concerns over subscribers incorrectly calculating the value. By exposing select information in the payload - such as CN - a client would be more likely to use those fields as the label. Stated differently: if the payload contains CNs and not fingerprints, client maintainers are highly likely to use the CNs and unlikely to use fingerprints.

Side note: Those are good parameters; I'll add size on my next edit.

This endpoint is intended to address changing chains/roots, and should be expected to vary across requests as the payload would be ephemeral and account-specific.

Under the current ACME implementation, one must finalize an order to see the current chains - a "chicken vs egg" situation.

A purpose of the endpoint is to allow a subscriber to query the server to determine what the current options are for the initial order or renewal. Ideally a client would then surface to their users a message like:

Currently valid options for our "preferred-chain" are:

The client could then list all the chains as they wish, with a client-specific selector.

If a subscriber requires a specific chain on renewal, a client could then query the server before renewal to ensure the chain is still offered. If it is no longer offered, a client could potentially:

  • abort the renewal then notify the client
  • continue with the renewal, use a different chain, then notify the client

Your approach works for initial interactive enrollment. I am trying to address automatic non-interactive renewals.

  1. If the requested chain does not appear, the logic is up to the client. I believe Certbot will use the default chain if none match.

  2. For an interactive client, I think the approach you outlined is absolutely correct. My concern is for non-interactive processes and renewals - and being able to surface the situation where a chain is no longer offered before finalizing (or even starting) an ACME Order. A subscriber should be able to determine if their preferred chain is no longer an option before consuming a (metered) ACME Order or restarting servies under a chain they do not wish to run.

  3. There are two usage patterns that come to mind regarding "Here's what we got" and "it doesn't appear" .

  • These two are months/days apart - the inital interactive order and a renewal. Polling this endpoint would allow the client to surface the change to the subscriber before doing a renewal.
  • These two are minutes apart, the change being due to an upstream deployment. A client should download the certificates (the order has been finalized) and alert the user to the mismatch. The client/subscriber should decide what happens next - e.g. should services under the new certificate be restarted or not?

Flipping from Certbot to certmagic for a moment, I believe the utility would be in this scenario: Before or during renewal, certmagic determines the previously selected chain preference is no longer valid. Certmagic determines whether the renewal should proceed or not, then notifies the user. The user can then invoke certmagic to query the endpoint and list the current options, which are then used to update the renewal configuration for the instant domain (and any other affected ones).

5 Likes

But they can't. Because, for an ecdsa certificate,

  • I can get the leaf-E1-X2-X1 chain and eventually leaf-E1-X2; but
  • a random newly registered acme account cannot, it only gets leaf-R3-X1

Available chains depends on key type and acme account.

3 Likes

I think the proposal (though maybe it's not spelled out clearly) is:

  • Directory includes an entry for "availableChains" which is a URL.
  • Client does a POST-as-GET to that URL (so is authenticating with their account)
  • Server returns some kind of object representing the available chains for that account.

And so, for instance, Let's Encrypt in its current configuration would return a different result for an account which is on the E1-allowlist and one which is not.

5 Likes

I like the idea in general, because as @petercooperjr suggested there are many optional features a CA might expose, and I agree the chain selection is one of them. We'd probably have to consider each thing separately and it may derail the effort if we try to encompass everything in in one go (or maybe it wouldn't?).

I'm directly interested in knowing the following about a CA :

  • Up-front chain options for a given key type (and possibly strength). This just helps the user make a preference choice but would still be ignored by the app if the chain wasn't present in the end result, it's a "preference" after all. The UI could also flag if a CA no longer declares support for your currently set preference, my client actually send diagnostic notifications to users and this would be a candidate.
  • Generally supported key types and strengths: i.e. which can I request and my order won't fail.
  • Whether notAfter is supported and if so, what's the min, max and default lifetime. Some CAs currently blow up the order if you try to specify notAfter, some don't. Incidentally I'd prefer all CAs ignored it rather than error as this currently hinders CA fallback.
  • subject support: SAN limits, wildcard support, domain+www , internal/private host names
  • identifier types supported (dns, IP, TnAuthList etc). Challenge types to some extent, but these are often implied from the identifier type you're trying to get (e.g. Authority Token for TnAuthList).
  • ARI support (I deduce this from the presence of the endpoint though)

Agree that the JSON should be optimised for deserialization to objects (as per JS, dotnet etc), the array in an array thing is mostly seen in python and I think PHP to some extent but it can require special work to parse elsewhere. Likewise I'd use simple names and avoid the _ prefix as it doesn't seem essential.

Definitely interesting!

4 Likes

I'll respond to several at once....

I do not know which are currently offering alternate chains,

However:

  1. Alternate Chains are explicitly defined in the ACME spec, so I consider this as addressing a deficiency in the spec that wasn't realized until production use.
  2. We can expect every CA to either support Alternate Chains or switch chains as their Trusted Roots expire. The DST retirement will eventually be repeated by every other CA as long as ACME is in use.
  3. I don't know if this is happening within ACME yet, but Commercial CAs often (a) own multiple roots and brands, allowing a subscriber to select a preferred option at checkout or download; and (b) offer cross-signed roots or intermediates.

Beyond that - I see the situation as this: As client authors we can generate a working draft that addresses our needs and gains ISRG's endorsement - then push it into the standards track at which point other CAs would would share their needs, concerns and edits. The end product would work for all providers, otherwise it would never be accepted. Under current conditions, CAs will decide what they offer to clients and how โ€“ likely without the input of client authors.

Realistically, our options are either:

  • Come together as client authors and try to influence the specs to best work with our needs.
  • Do nothing and have a CA develop their own spec or divergence - then deal with whatever information/flow they have graciously given us.

There is definitely a lot of room for improvement with the ACME spec. I am focused on advancing a proposal regarding Chains right now, because this is changing a lot with the LetsEncrypt CA in the near future, and the current system and client behaviors can cause operational difficulties.

As LetsEncrypt subscribers, we know that chains will change several times in the near future:

  • swapping of long and short X1 chains
  • removal of long X1 chain
  • introduction of X2 chain

We've also had several changes to the chains in the past:

  • swapping of long and short X1 chains
  • introduction of X2 chain to specific accounts
  • re-signing of the long X1 chain (the expiry date changed)

Across every CA, we know there will changes to available chains when their roots near expiry.

I work a lot in publishing with high traffic websites. Chain selection is incredibly important, because the chain is directly related to reach and usability. "Trust" is not the only concern, key type and chain length can affect compatibility as well (especially when you're dealing with embedded browsers on mobile devices). While some subscribers are fine if their services restart with a different chain, others will be negatively impacted - losing traffic, revenue and consumer confidence. While I'd love to see many various improvements to the ACME spec, I am less concerned with addressing Chain Selection as a deficiency in the spec and more concerned with avoiding the tangible problems switching a chain can create. If I have N days of validity left on a certificate and my preferred root is not available, I do not want to renew that certificate with a different trust anchor and deploy it โ€“ nor do I want to consume a Certificate credit to lean about this change. I want to catch this before renewal, and research my options.

IMHO these are both particularly great things to bring up, but they do not frequently change and submitting an invalid option to the CA will result in a failure with informative docstrings. With the existing ACME RFC and client behaviors, marking a preferred chain will either:

  • Consume an allocated Certificate "credit" (i.e. duplicate certificate limit with LE, total certificate limit with commercial CAs) (best-case scenario)
  • Restart web services with an unwanted Certificate chain (worst-case scenario)

+1. Making results specific to an account/key might best be proposed as a potential extension or behavior. IMHO the important thing right now is being able to tell if a human needs to intervene and review/update the configurations because the CA has changed their offerings, and a behavior like this would cover most of those situations.

We're on the same page here for all of that.

I wonder if this could be consolidated into the same endpoint or a different one.

It seems like this would need to be account-specific, as LE shards X1 and X2 access by account. I also wonder if this might be account-specific with commercial CAs, who often vary features across pricing tiers.

I would love to see this, but I don't think it's going to happen as we're in "divergences" territory against 7.1.3 and 7.4 AND trying to avoid conflicts with the spec.

The field is supposed to be optional:

LE's behavior is officially documented as a divergence (https://github.com/letsencrypt/boulder/blob/main/docs/acme-divergences.md) against 7.4

Boulder does not accept the optional notBefore and notAfter fields of a newOrder request paylod.

Ignoring the fields should not happen as that's a bigger divergence than not supporting an optional field. I don't think that will likely happen unless the RFC itself is changed. 7.4 states:

The server MUST return an error if it cannot fulfill the request as specified, and it MUST NOT issue a certificate with contents other than those requested.

IIRC, the CAs that do not accept these fields made the decision because doing so would require them to issue matching certificates and they wished to control the validity period.

I think the server could advertise if it accepts the optional params, but I don't think there would be industry support to standardize a way to advertise "we're going to ignore the optional params and issue a certificate in violation of the RFC".

4 Likes

Okay, I'm going to come in swinging (sorry!) by saying that I don't think something like what is proposed here should be standardized. I think the overall idea is a good one, and very well motivated and obviously well intentioned, but there are a few things that make this specific idea not the right direction to go. I'll talk a little bit about why that is, and then suggest the direction we should be going in instead.

Let's Encrypt intends to do this in the very near future.

I think that this line gets at the core of why this proposal is not the right direction. This is confusing two very different concepts: what chains are available, and what profiles are available. The profile affects the actual contents of your end-entity cert: being signed by an ECDSA intermediate means you have a different signature algorithm. The chain just affects the certs above your end-entity, such as whether you present X2-signed-by-X1 or X2-self-signed.

And much of the follow-on conversation here has the same issue, unfortunately. The difference between X1 and X2 is generally just a matter of chains, yes, but the difference between E1 and R3 is not, and should not be confused with merely chain selection.

How would one define a "Type"? Is it always going to be just RSA and ECDSA? What about when Ed25519 finally gets allowed? What if people want other things (like KEM certificates)? What if people want EV certificates (not from LE, but from some other ACME server) -- does that count as a Type? Even if that CA only issues EV from a subset of their intermediates?

If this field is super strictly limited, then it becomes difficult to extend. If it is wide-open, it becomes non-machine-readable.

This list is really the core of the issue here: there are many different criteria that a client might want to know about ahead of time and select. Too many. A smart CA doesn't actually want to let clients select any combination of these attributes, because some combinations are useless, or worse, disallowed. Today, the mechanism for selecting things like this is the CSR (that's how we determine that the client wants OCSP must-staple), but CSRs are bad for this because CAs consistently make mistakes regarding copying values directly from the CSR to the final cert. And we don't really want to balloon the newOrder request with a dozen different fields, which will obviously be a mis-prediction of which fields are actually useful (observe: the notBefore and notAfter fields in the newOrder request today, which we ignore).

So, what should we do instead?

I already have a private draft of what follows, which I intend to share with the IETF ACME WG in the near future. I've been talking through this plan with other folks here and at other ACME CAs for a while now and we think this is the best -- simple, obvious, flexible, minimal -- path forward.

Profile Selection. The Directory's Meta object gets a new sub-field listing the available profiles. Each profile has a name and either a short text description or a URL pointing at a documentation page (we're not sure which is better yet).


   HTTP/1.1 200 OK
   Content-Type: application/json

   {
     "newNonce": "https://example.com/acme/new-nonce",
     "newAccount": "https://example.com/acme/new-account",
     "newOrder": "https://example.com/acme/new-order",
     "newAuthz": "https://example.com/acme/new-authz",
     "revokeCert": "https://example.com/acme/revoke-cert",
     "keyChange": "https://example.com/acme/key-change",
     "meta": {
       "termsOfService": "https://example.com/acme/terms/2017-5-30",
       "website": "https://www.example.com/",
       "caaIdentities": ["example.com"],
       "externalAccountRequired": false,
       "profiles": {
         "default": "The profile that Let's Encrypt has been using for the last 6+ years.",
         "short": "The same as 'default', but with a validity period of 10 days.",
         "minimal": "A stripped-down profile with a short lifetime, no OCSP, no keyEncipherment KU, no clientAuth EKU, etc."
       }
     }
   }

The client exposes these options and their descriptions to its operator, who selects one (e.g. by putting the profile name in a config file). The client then includes that profile name in a new field of the newOrder request.

   POST /acme/new-order HTTP/1.1
   Host: example.com
   Content-Type: application/jose+json

   {
     "protected": base64url({
       "alg": "ES256",
       "kid": "https://example.com/acme/acct/evOfKhNU60wg",
       "nonce": "5XJ1L3lEkMG7tR6pA00clA",
       "url": "https://example.com/acme/new-order"
     }),
     "payload": base64url({
       "identifiers": [
         { "type": "dns", "value": "www.example.org" },
         { "type": "dns", "value": "example.org" }
       ],
       "profile": "minimal"
     }),
     "signature": "H6ZXtGjTZyUnPeKn...wEA4TklBdh3e454g"
   }

That's basically it. There are obviously details (which profile is used if none is indicated?) but this mechanism gets both CAs and clients 90+% of what they need to more formally advertise, negotiate, and configure different issuance options.

11 Likes

I like your idea, but I think there are two different goals someone might have, and these different solutions are targeting one or the other:

  1. I want configuring a system to use a particular CA, configuring all the options I can for it.
  2. I want to not configure a system with anything beyond a list of available ACME directory URLs, and the client should figure out the optimal options based on which CAs which are currently available. Such a client might regularly change from one CA to another.
3 Likes

Oh... I really want to see that challenge.

I like this. But how does that work with a whitelist?

Because I can see "default" being rsa-signed-by-rsa and ecdsa-signed-by-ecdsa and so on omogenously. But that's not the current default. Or, well, it is and it isn't.

3 Likes

I don't think there's a good solution for this. I've spent a lot of time thinking about it, and fundamentally the issue is that I cannot predict the full set of a criteria that a client might want to use to select between CAs: what signature algorithms they use, what trust stores their roots are in, what validity periods they use, how customizable those validity periods are, what extensions they will or will not include, how many names they'll include, what validation methods they use, whether they require External Account Binding, what their pricing structure is, what their rate limits are.... really I could go on forever. Therefore I think this answer is to not try, and to continue to let humans make that selection.

7 Likes

I mean, DigiCert does it. The organization validation stuff is all handled out-of-band, and ACME is just used to automate the certificate issuance.

4 Likes

So it's like --manual where the Acme client would tell you "now go talk with my humans" instead of "I'm putting this file in .well-known"

Very automated indeed :smiley:

3 Likes

More that you get your organization validated, they give you a directory endpoint & EAB and such to use, and your ACME client just does the domain name validation. The next year (or whenever it expires) you get your organization validation redone, and the ACME client just keeps ticking away.

8 Likes

That is fair. The point of bringing this up for public discourse is trying to find a solution that works for all in the ecosystem.

I don't think your proposal as-is reflects certain needs of Subscribers and Clients in the ecosystem, or the issues/concerns Subscribers and Clients have faced in the past - and will in the future.

As I noted in an above comment, the ACME specs have been largely driven by CAs so far and would benefit from the input of Clients and Subscribers.

100% in agreement with this paragraph.

The example snippet above would not adequately address many operational concerns of Subscribers and Clients.

As a Subscriber or Client, I should be able to determine if a renewal attempt will return a chain that is "substantially different" without ordering/finalizing a Certificate โ€“ and potentially restarting services with an unwanted Root/Chain.

Let's talk about "substantially different". The entirety of a chain is largely irrelevant to this discussion. While size is a performance concern that can be handled post-issuance, the primary concern to many are the Trusted Roots. ISRG's current offering โ€“ DST, X1, X2 โ€“ each have completely different potential audiences amongst worldwide consumers. A secondary concern is the key technology (R3 vs E1).

If the listing of profiles included options that were pinned to specific roots, I think it would adequately address this concern:

For example:

    "profiles": {
        "default": "The profile that Let's Encrypt has been using for the last 6+ years.",
        "default-X1": "A version of default that will serve X1 as the Root"
        "default-X2": "A version of default that will serve X2 as the Root"
    }

In the above edit, we can consider the "default" profile to be the Server's recommendation for full automation, while there are several variations of that profile pinned to different chains. The variations might even be a child object of the profile key.

    "profiles": {
        "default": {
            "url": "http://example.com/policy.html",
            "description": "The profile that Let's Encrypt has been using for the last 6+ years.",
            "variants": {
                "X1": "X1 is Root. Alt Chain ordering is best-practices.",
                "X2": "X2 is Root. Alt Chain ordering is best-practices.",
            },

Under that framework, a Client would easily be able to determine if a specific profile (i.e. expected root) is available or not. If the profile is no longer available, the client might be configured to alert the Subscriber, then either stop or continue with an unpinned version.

There could (should) even be variants that advertise the topmost chain ordering. E.g.

                "X1-DST": "X1 is Root. Chains are pinned: DST-Cross, X1 (alt)",
                "X1-X1": "X1 is Root. Chains are pinned: X1, DST-Cross (alt)",

This would allow clients to immediately determine the most substantial parts of chains have switched, so appropriate action can be taken.

5 Likes

If a profile was an array of standard features that might work, but a text label means nothing to an app trying to negotiate a feature set (not sure if that was just there for illustration):

"profiles": {
         "default": [
                 {"description":""},
                 {"lifetime_default_hours":"2160"},
                 {"liftetime_minimum":"24"},

                 ... etc
        ]

[It would be optional for a profile to declare a feature, absence of a declaration does not strictly mean absence of a feature, but presence of a declaration means the feature is definitely available]

For me the profile details don't have to hang off the directory and make it huge, it just could be a profile endpoint like /profile/default/.

For context, I'm regularly working with multiple CA accounts (e.g., 7 different production CAs, different feature sets, different available cert lifetimes, different keys type support etc) and having the system try to juggle between those as failures occur. This is because I'm currently working on the problem of large scale renewal management from a single instance/cluster. Different certs have different feature requirements and just attempting against CAs in a round-robin style until one works is a little bit unsophisticated.

In my comments here I'm not really talking about Let's Encrypt, I'm talking about ACME CAs generally, especially including internal CAs like step-ca and vault.

Regarding chain selection, I just want to be able to present a list of acceptable options for "preferred chain".

5 Likes

Sorry, I'm still slightly confused. It's not clear to me if the goal is to provide capabilities that ACME simply doesn't have today, or to improve the affordances of capabilities it does. I thought the goal was the former, hence my suggestion for the profile-selection system, which has no equivalent in current ACME. But the emphasis on chain selection I'm seeing feels more like the latter.

ACME already has chain selection mechanisms: you finalize an order, download the cert+chain, follow the link-rel headers, download the alternate cert+chains, and pick one you like based on what the user has configured. I definitely acknowledge that this system isn't very good: it's not clear what the options are ahead of time, there are no human-readable names associated with the options (although there could be! no need to have them be numbered like we currently do, they could be named), and the client has to parse the chains in order to make decisions based on their contents.

But fundamentally chain selection is disconnected from profiles. Chain selection is a choice that can be made after the fact, and even changed without re-issuing the certificate. I think it would be incredibly bad to have a newOrder request lock someone into the EE <-- E1 <-- X2 chain, when they should be able to seamlessly switch to the EE <-- E1 <-- X2 <-- X1 <-- DST chain if they want to.

Also, there are many criteria that might cause someone to prefer one chain over another: algorithms used (some are stronger than others), total bytes in the chain (to reduce network usage in handshakes), number of signatures in the chain (to reduce validation time), EKUs in the chain (some people believe strongly in single-use hierarchies), etc. And again, we can't predict the full set of criteria that people might choose to care about. The easiest way to let them make that determination is to provide the whole chain, and let them make the determination using whatever heuristics they want. That's what the current chain selection system does: let the client see the whole thing.

Yes, this is the point of my other reply above. We can't predict the full set of features that people will want to condition on, so I don't think I'm interested in specifying a set of feature names to include in descriptions like this. If we try to severely limit the set of things that can be advertised, we'll get it wrong. If we try to include everything, we'll completely reinvent x509. Neither of these is a good outcome.

7 Likes