Skip to content

proposal: x/crypto/acme: support the ACME Renewal Information standard in Client #60958

Open
@jmhodges

Description

@jmhodges

(This could be viewed as a preliminary ticket because, while Let's Encrypt supports the API in question and there are multiple ACME clients supporting it, the standard hasn't been finalized at time of creation. Recent events described below made me want to write this out.)

It would be nice for x/crypto/acme to give consumers of it a convenient way of knowing when their certificates need renewal especially when their CAs have revoked those certificates early.

One of the outcomes of the recent Let's Encrypt outage was a recognition that many ACME clients aren't renewing certificates that were revoked by the CA. @AGWA wrote a very nice piece on the outage, the renewal problem, and how ACME clients might solve it.

In that post, he mentions that Let's Encrypt currently supports the still-under-development ACME Renewal Information (ARI) standard. That standard and API allows ACME clients to hear from the CA that they need to renew a certificate they previously made. That renewal information also, helpfully, gives guidance on when to attempt a renewal even during normal operation.

It would be nice for x/crypto/acme to support the ARI standard.

One version of this work would be to:

  1. add a new method to acme.Client called GetRenewalInfo accepting a x509 certificate that it extract the necessary CertID from, and returning a RenewalInformation struct with the suggested liveness window and explanatory URL
  2. add a new method to acme.Client called UpdateRenewalInfo accepting a x509 certificate (a more minimal version would not include this method as its not required for correct operation of GetRenewalInfo)

This ticket includes a proposed API below. The method and type names below are meant only as reasonable first attempts at naming and others might have other better names.

The word "CertID" has specific meaning and importance here. The ARI APIs takes the certificate's "DER-encoded CertID ASN.1 sequence" (link). See section 4.1.1 of RFC6960 for the definition of "CertID". A CertID is a combination of what are currently multiple fields in crypto/x509.Certificate. That means GetRenewalInfo and UpdateRenewalInfo, unlike RevokeCert, needs to parse the x509 certificate given to it.

While GetRenewalInfo and UpdateRenewalInfo could take some CertID bytes or struct, that combination of data and encoding is a bit more complicated than we could expect users to re-create well. So, we're left with the choice of taking a []byte of the certificate that is parsed under the hood, or a *x509.Certificate.

A []byte for the cert seems preferable to a *x509.Certificate. First, because the []byte type fits the pattern set by RevokeCert. Second, a []byte would also likely result in less code written by most consumers as most don't care about the individual bytes inside a cert and would be passing it directly from their storage layer. Third, the []byte also avoids them having to learn how to parse x509 well. However, specifying the full x509.Certificate type might better convey what's being requested from the consumer and avoid this library from having to inform the consumer when their x509 certificate is ill-formed. On balance, the []byte seems better, but I'm open to hearing I'm wrong.

One last note: GetRenewalInfo doesn't require its request payload to be signed with the correct account key, but UpdateRenewalInfo does.

type SuggestedWindow struct {
  Start time.Time
  End time.Time
}

type RenewalInformation struct {
  SuggestedWindow SuggestedWindow
  ExplanationURL string
} 

func (c * Client) GetRenewalInfo(ctx context.Context, cert []byte) (*RenewalInformation, error)

func (c * Client) UpdateRenewalInfo(ctx context.Context, cert []byte) (error)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Incoming

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions