acme

package
v2.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2024 License: Apache-2.0 Imports: 28 Imported by: 9

Documentation

Overview

Package acme fully implements the ACME protocol specification as described in RFC 8555: https://tools.ietf.org/html/rfc8555.

It is designed to work smoothly in large-scale deployments with high resilience to errors and intermittent network or server issues, with retries built-in at every layer of the HTTP request stack.

NOTE: This is a low-level API. Most users will want the mholt/acmez package which is more concerned with configuring challenges and implementing the order flow. However, using this package directly is recommended for advanced use cases having niche requirements. See the examples in the examples/plumbing folder for a tutorial.

Index

Constants

View Source
const (
	StatusPending     = "pending"
	StatusProcessing  = "processing"
	StatusValid       = "valid"
	StatusInvalid     = "invalid"
	StatusDeactivated = "deactivated"
	StatusExpired     = "expired"
	StatusRevoked     = "revoked"
	StatusReady       = "ready"
)

Possible status values. From several spec sections: - Account §7.1.2 (valid, deactivated, revoked) - Order §7.1.3 (pending, ready, processing, valid, invalid) - Authorization §7.1.4 (pending, valid, invalid, deactivated, expired, revoked) - Challenge §7.1.5 (pending, processing, valid, invalid) - Status changes §7.1.6

View Source
const (
	ReasonUnspecified          = iota // 0
	ReasonKeyCompromise               // 1
	ReasonCACompromise                // 2
	ReasonAffiliationChanged          // 3
	ReasonSuperseded                  // 4
	ReasonCessationOfOperation        // 5
	ReasonCertificateHold             // 6

	ReasonRemoveFromCRL      // 8
	ReasonPrivilegeWithdrawn // 9
	ReasonAACompromise       // 10
)

Reasons for revoking a certificate, as defined by RFC 5280 §5.3.1. https://tools.ietf.org/html/rfc5280#section-5.3.1

View Source
const (
	ChallengeTypeHTTP01         = "http-01"          // RFC 8555 §8.3
	ChallengeTypeDNS01          = "dns-01"           // RFC 8555 §8.4
	ChallengeTypeTLSALPN01      = "tls-alpn-01"      // RFC 8737 §3
	ChallengeTypeDeviceAttest01 = "device-attest-01" // draft-acme-device-attest-00 §5
	ChallengeTypeEmailReply00   = "email-reply-00"   // RFC 8823 §5.2
)

The standard or well-known ACME challenge types.

View Source
const (
	// The ACME error URN prefix.
	ProblemTypeNamespace = "urn:ietf:params:acme:error:"

	ProblemTypeAccountDoesNotExist     = ProblemTypeNamespace + "accountDoesNotExist"
	ProblemTypeAlreadyRevoked          = ProblemTypeNamespace + "alreadyRevoked"
	ProblemTypeBadCSR                  = ProblemTypeNamespace + "badCSR"
	ProblemTypeBadNonce                = ProblemTypeNamespace + "badNonce"
	ProblemTypeBadPublicKey            = ProblemTypeNamespace + "badPublicKey"
	ProblemTypeBadRevocationReason     = ProblemTypeNamespace + "badRevocationReason"
	ProblemTypeBadSignatureAlgorithm   = ProblemTypeNamespace + "badSignatureAlgorithm"
	ProblemTypeCAA                     = ProblemTypeNamespace + "caa"
	ProblemTypeCompound                = ProblemTypeNamespace + "compound"
	ProblemTypeConnection              = ProblemTypeNamespace + "connection"
	ProblemTypeDNS                     = ProblemTypeNamespace + "dns"
	ProblemTypeExternalAccountRequired = ProblemTypeNamespace + "externalAccountRequired"
	ProblemTypeIncorrectResponse       = ProblemTypeNamespace + "incorrectResponse"
	ProblemTypeInvalidContact          = ProblemTypeNamespace + "invalidContact"
	ProblemTypeMalformed               = ProblemTypeNamespace + "malformed"
	ProblemTypeOrderNotReady           = ProblemTypeNamespace + "orderNotReady"
	ProblemTypeRateLimited             = ProblemTypeNamespace + "rateLimited"
	ProblemTypeRejectedIdentifier      = ProblemTypeNamespace + "rejectedIdentifier"
	ProblemTypeServerInternal          = ProblemTypeNamespace + "serverInternal"
	ProblemTypeTLS                     = ProblemTypeNamespace + "tls"
	ProblemTypeUnauthorized            = ProblemTypeNamespace + "unauthorized"
	ProblemTypeUnsupportedContact      = ProblemTypeNamespace + "unsupportedContact"
	ProblemTypeUnsupportedIdentifier   = ProblemTypeNamespace + "unsupportedIdentifier"
	ProblemTypeUserActionRequired      = ProblemTypeNamespace + "userActionRequired"
)

Standard token values for the "type" field of problems, as defined in RFC 8555 §6.7: https://tools.ietf.org/html/rfc8555#section-6.7

"To facilitate automatic response to errors, this document defines the following standard tokens for use in the 'type' field (within the ACME URN namespace 'urn:ietf:params:acme:error:') ... This list is not exhaustive. The server MAY return errors whose 'type' field is set to a URI other than those defined above."

Variables

View Source
var ErrUnsupported = fmt.Errorf("unsupported by ACME server")

ErrUnsupported is used to indicate lack of support by an ACME server.

Functions

func ARIUniqueIdentifier

func ARIUniqueIdentifier(leafCert *x509.Certificate) (string, error)

ARIUniqueIdentifier returns the unique identifier for the certificate as used by ACME Renewal Information. EXPERIMENTAL: ARI is a draft RFC spec: draft-ietf-acme-ari-03

Types

type Account

type Account struct {
	// status (required, string):  The status of this account.  Possible
	// values are "valid", "deactivated", and "revoked".  The value
	// "deactivated" should be used to indicate client-initiated
	// deactivation whereas "revoked" should be used to indicate server-
	// initiated deactivation.  See Section 7.1.6.
	//
	// The client need NOT set this field when creating a new account.
	Status string `json:"status"`

	// contact (optional, array of string):  An array of URLs that the
	// server can use to contact the client for issues related to this
	// account.  For example, the server may wish to notify the client
	// about server-initiated revocation or certificate expiration.  For
	// information on supported URL schemes, see Section 7.3.
	Contact []string `json:"contact,omitempty"`

	// termsOfServiceAgreed (optional, boolean):  Including this field in a
	// newAccount request, with a value of true, indicates the client's
	// agreement with the terms of service.  This field cannot be updated
	// by the client.
	TermsOfServiceAgreed bool `json:"termsOfServiceAgreed,omitempty"`

	// externalAccountBinding (optional, object):  Including this field in a
	// newAccount request indicates approval by the holder of an existing
	// non-ACME account to bind that account to this ACME account.  This
	// field is not updateable by the client (see Section 7.3.4).
	//
	// Use SetExternalAccountBinding() to set this field's value properly.
	ExternalAccountBinding json.RawMessage `json:"externalAccountBinding,omitempty"`

	// orders (required, string):  A URL from which a list of orders
	// submitted by this account can be fetched via a POST-as-GET
	// request, as described in Section 7.1.2.1.
	Orders string `json:"orders"`

	// In response to new-account, "the server returns this account
	// object in a 201 (Created) response, with the account URL
	// in a Location header field." §7.3
	//
	// We transfer the value from the header to this field for
	// storage and recall purposes.
	Location string `json:"location,omitempty"`

	// The private key to the account. Because it is secret, it is
	// not serialized as JSON and must be stored separately (usually
	// a PEM-encoded file).
	//
	// This is a required field when creating a new account.
	PrivateKey crypto.Signer `json:"-"`
}

Account represents a set of metadata associated with an account as defined by the ACME spec §7.1.2: https://tools.ietf.org/html/rfc8555#section-7.1.2

Users of this Go package should generally set Contact, TermsOfServiceAgreed, ExternalAccountBinding if relevant, and PrivateKey fields when creating a new account. Other fields are populated by the ACME server.

func (*Account) SetExternalAccountBinding

func (a *Account) SetExternalAccountBinding(ctx context.Context, client *Client, eab EAB) error

SetExternalAccountBinding sets the ExternalAccountBinding field of the account. It only sets the field value; it does not register the account with the CA. (The client parameter is necessary because the EAB encoding depends on the directory.)

type Authorization

type Authorization struct {
	// identifier (required, object):  The identifier that the account is
	// authorized to represent.
	Identifier Identifier `json:"identifier"`

	// status (required, string):  The status of this authorization.
	// Possible values are "pending", "valid", "invalid", "deactivated",
	// "expired", and "revoked".  See Section 7.1.6.
	Status string `json:"status"`

	// expires (optional, string):  The timestamp after which the server
	// will consider this authorization invalid, encoded in the format
	// specified in [RFC3339].  This field is REQUIRED for objects with
	// "valid" in the "status" field.
	Expires time.Time `json:"expires,omitempty"`

	// challenges (required, array of objects):  For pending authorizations,
	// the challenges that the client can fulfill in order to prove
	// possession of the identifier.  For valid authorizations, the
	// challenge that was validated.  For invalid authorizations, the
	// challenge that was attempted and failed.  Each array entry is an
	// object with parameters required to validate the challenge.  A
	// client should attempt to fulfill one of these challenges, and a
	// server should consider any one of the challenges sufficient to
	// make the authorization valid.
	Challenges []Challenge `json:"challenges"`

	// wildcard (optional, boolean):  This field MUST be present and true
	// for authorizations created as a result of a newOrder request
	// containing a DNS identifier with a value that was a wildcard
	// domain name.  For other authorizations, it MUST be absent.
	// Wildcard domain names are described in Section 7.1.3.
	Wildcard bool `json:"wildcard,omitempty"`

	// "The server allocates a new URL for this authorization and returns a
	// 201 (Created) response with the authorization URL in the Location
	// header field" §7.4.1
	//
	// We transfer the value from the header to this field for storage and
	// recall purposes.
	Location string `json:"-"`
}

Authorization "represents a server's authorization for an account to represent an identifier. In addition to the identifier, an authorization includes several metadata fields, such as the status of the authorization (e.g., 'pending', 'valid', or 'revoked') and which challenges were used to validate possession of the identifier." §7.1.4

func (Authorization) IdentifierValue

func (authz Authorization) IdentifierValue() string

IdentifierValue returns the Identifier.Value field, adjusted according to the Wildcard field.

type Certificate

type Certificate struct {
	// The certificate resource URL as provisioned by
	// the ACME server. Some ACME servers may split
	// the chain into multiple URLs that are Linked
	// together, in which case this URL represents the
	// starting point.
	URL string `json:"url"`

	// The PEM-encoded certificate chain, end-entity first.
	// It is excluded from JSON marshalling since the
	// chain is usually stored in its own file.
	ChainPEM []byte `json:"-"`

	// For convenience, the directory URL of the ACME CA that
	// issued this certificate. This field is not part of the
	// ACME spec, but it can be useful to save this along with
	// the certificate for restoring a lost ACME client config.
	CA string `json:"ca,omitempty"`

	// When to renew the certificate, and related info, as
	// prescribed by ARI.
	RenewalInfo *RenewalInfo `json:"renewal_info,omitempty"`
}

Certificate represents a certificate chain, which we usually refer to as "a certificate" because in practice an end-entity certificate is seldom useful/practical without a chain. This structure can be JSON-encoded and stored alongside the certificate chain to preserve potentially-useful metadata.

type Challenge

type Challenge struct {

	// type (required, string):  The type of challenge encoded in the
	// object.
	Type string `json:"type"`

	// url (required, string):  The URL to which a response can be posted.
	URL string `json:"url"`

	// status (required, string):  The status of this challenge.  Possible
	// values are "pending", "processing", "valid", and "invalid" (see
	// Section 7.1.6).
	Status string `json:"status"`

	// validated (optional, string):  The time at which the server validated
	// this challenge, encoded in the format specified in [RFC3339].
	// This field is REQUIRED if the "status" field is "valid".
	Validated string `json:"validated,omitempty"`

	// error (optional, object):  Error that occurred while the server was
	// validating the challenge, if any, structured as a problem document
	// [RFC7807].  Multiple errors can be indicated by using subproblems
	// Section 6.7.1.  A challenge object with an error MUST have status
	// equal to "invalid".
	Error *Problem `json:"error,omitempty"`

	// "The token for a challenge is a string comprised entirely of
	// characters in the URL-safe base64 alphabet." §8.1
	//
	// Used by the http-01, tls-alpn-01, and dns-01 challenges.
	Token string `json:"token,omitempty"`

	// A key authorization is a string that concatenates the token for the
	// challenge with a key fingerprint, separated by a "." character (§8.1):
	//
	//     keyAuthorization = token || '.' || base64url(Thumbprint(accountKey))
	//
	// This client package automatically assembles and sets this value for you.
	KeyAuthorization string `json:"keyAuthorization,omitempty"`

	// We attach the identifier that this challenge is associated with, which
	// may be useful information for solving a challenge. It is not part of the
	// structure as defined by the spec but is added by us to provide enough
	// information to solve the DNS-01 challenge.
	Identifier Identifier `json:"identifier,omitempty"`

	// From header of email must match with the "from" field of challenge object
	// as described in RFC8823 §3.1 - 2, added on 3-6.3.1
	From string `json:"from,omitempty"`

	// Payload contains a JSON-marshallable value that will be sent to the CA
	// when responding to challenges. If not set, an empty JSON body "{}" will
	// be included in the POST request. This field is applicable when responding
	// to "device-attest-01" challenges.
	Payload any `json:"-"`
}

Challenge holds information about an ACME challenge.

"An ACME challenge object represents a server's offer to validate a client's possession of an identifier in a specific way. Unlike the other objects listed above, there is not a single standard structure for a challenge object. The contents of a challenge object depend on the validation method being used. The general structure of challenge objects and an initial set of validation methods are described in Section 8." §7.1.5

func (Challenge) DNS01KeyAuthorization

func (c Challenge) DNS01KeyAuthorization() string

DNS01KeyAuthorization encodes a key authorization value to be used in a TXT record for the _acme-challenge DNS record.

"A client fulfills this challenge by constructing a key authorization from the 'token' value provided in the challenge and the client's account key. The client then computes the SHA-256 digest [FIPS180-4] of the key authorization.

The record provisioned to the DNS contains the base64url encoding of this digest." §8.4

func (Challenge) DNS01TXTRecordName

func (c Challenge) DNS01TXTRecordName() string

DNS01TXTRecordName returns the name of the TXT record to create for solving the dns-01 challenge.

"The client constructs the validation domain name by prepending the label '_acme-challenge' to the domain name being validated, then provisions a TXT record with the digest value under that name." §8.4

func (Challenge) HTTP01ResourcePath

func (c Challenge) HTTP01ResourcePath() string

HTTP01ResourcePath returns the URI path for solving the http-01 challenge.

"The path at which the resource is provisioned is comprised of the fixed prefix '/.well-known/acme-challenge/', followed by the 'token' value in the challenge." §8.3

func (Challenge) MailReply00KeyAuthorization

func (c Challenge) MailReply00KeyAuthorization(mailSubject string) (string, error)

MailReply00KeyAuthorization encodes a key authorization value to be sent back to the reply-to address of the ACME challenge email. The subject of that mail contains token-part1, which must be combined with token-part2, which was received as part of the JSON challenge as described in RFC8823 §3.1.

type Client

type Client struct {
	// The ACME server's directory endpoint.
	Directory string

	// Custom HTTP client.
	HTTPClient *http.Client

	// Augmentation of the User-Agent header. Please set
	// this so that CAs can troubleshoot bugs more easily.
	UserAgent string

	// Delay between poll attempts. Only used if server
	// does not supply a Retry-Afer header. Default: 250ms
	PollInterval time.Duration

	// Maximum duration for polling. Default: 5m
	PollTimeout time.Duration

	// An optional logger. Default: no logs
	Logger *zap.Logger
	// contains filtered or unexported fields
}

Client facilitates ACME client operations as defined by the spec.

Because the client is synchronized for concurrent use, it should not be copied.

Many errors that are returned by a Client are likely to be of type Problem as long as the ACME server returns a structured error response. This package wraps errors that may be of type Problem, so you can access the details using the conventional Go pattern:

var problem Problem
if errors.As(err, &problem) {
    log.Printf("Houston, we have a problem: %+v", problem)
}

All Problem errors originate from the ACME server.

func (*Client) AccountKeyRollover

func (c *Client) AccountKeyRollover(ctx context.Context, account Account, newPrivateKey crypto.Signer) (Account, error)

AccountKeyRollover changes an account's associated key.

"To change the key associated with an account, the client sends a request to the server containing signatures by both the old and new keys." §7.3.5

func (*Client) DeactivateAuthorization

func (c *Client) DeactivateAuthorization(ctx context.Context, account Account, authzURL string) (Authorization, error)

DeactivateAuthorization deactivates an authorization on the server, which is a good idea if the authorization is not going to be utilized by the client.

"If a client wishes to relinquish its authorization to issue certificates for an identifier, then it may request that the server deactivate each authorization associated with it by sending POST requests with the static object {"status": "deactivated"} to each authorization URL." §7.5.2

func (*Client) FinalizeOrder

func (c *Client) FinalizeOrder(ctx context.Context, account Account, order Order, csrASN1DER []byte) (Order, error)

FinalizeOrder finalizes the order with the server and polls under the server has updated the order status. The CSR must be in ASN.1 DER-encoded format. If this succeeds, the certificate is ready to download once this returns.

"Once the client believes it has fulfilled the server's requirements, it should send a POST request to the order resource's finalize URL." §7.4

func (*Client) GetAccount

func (c *Client) GetAccount(ctx context.Context, account Account) (Account, error)

GetAccount looks up an account on the ACME server.

"If a client wishes to find the URL for an existing account and does not want an account to be created if one does not already exist, then it SHOULD do so by sending a POST request to the newAccount URL with a JWS whose payload has an 'onlyReturnExisting' field set to 'true'." §7.3.1

func (*Client) GetAuthorization

func (c *Client) GetAuthorization(ctx context.Context, account Account, authzURL string) (Authorization, error)

GetAuthorization fetches an authorization object from the server.

"Authorization resources are created by the server in response to newOrder or newAuthz requests submitted by an account key holder; their URLs are provided to the client in the responses to these requests."

"When a client receives an order from the server in reply to a newOrder request, it downloads the authorization resources by sending POST-as-GET requests to the indicated URLs. If the client initiates authorization using a request to the newAuthz resource, it will have already received the pending authorization object in the response to that request." §7.5

func (*Client) GetCertificateChain

func (c *Client) GetCertificateChain(ctx context.Context, account Account, certURL string) ([]Certificate, error)

GetCertificateChain downloads all available certificate chains originating from the given certURL. This is to be done after an order is finalized.

"To download the issued certificate, the client simply sends a POST- as-GET request to the certificate URL."

"The server MAY provide one or more link relation header fields [RFC8288] with relation 'alternate'. Each such field SHOULD express an alternative certificate chain starting with the same end-entity certificate. This can be used to express paths to various trust anchors. Clients can fetch these alternates and use their own heuristics to decide which is optimal." §7.4.2

func (*Client) GetDirectory

func (c *Client) GetDirectory(ctx context.Context) (Directory, error)

GetDirectory retrieves the directory configured at c.Directory. It is NOT necessary to call this to provision the client. It is only useful if you want to access a copy of the directory yourself.

func (*Client) GetOrder

func (c *Client) GetOrder(ctx context.Context, account Account, order Order) (Order, error)

GetOrder retrieves an order from the server. The Order's Location field must be populated.

func (*Client) GetRenewalInfo

func (c *Client) GetRenewalInfo(ctx context.Context, leafCert *x509.Certificate) (RenewalInfo, error)

GetRenewalInfo returns the ACME Renewal Information (ARI) for the certificate. It fills in the Retry-After value, if present, onto the returned struct so the caller can poll appropriately. If the ACME server does not support ARI, an error wrapping ErrUnsupported will be returned.

func (*Client) InitiateChallenge

func (c *Client) InitiateChallenge(ctx context.Context, account Account, challenge Challenge) (Challenge, error)

InitiateChallenge "indicates to the server that it is ready for the challenge validation by sending an empty JSON body ('{}') carried in a POST request to the challenge URL (not the authorization URL)." §7.5.1

func (*Client) NewAccount

func (c *Client) NewAccount(ctx context.Context, account Account) (Account, error)

NewAccount creates a new account on the ACME server.

"A client creates a new account with the server by sending a POST request to the server's newAccount URL." §7.3

func (*Client) NewAuthorization

func (c *Client) NewAuthorization(ctx context.Context, account Account, id Identifier) (Authorization, error)

NewAuthorization creates a new authorization for an identifier using the newAuthz endpoint of the directory, if available. This function creates authzs out of the regular order flow.

"Note that because the identifier in a pre-authorization request is the exact identifier to be included in the authorization object, pre- authorization cannot be used to authorize issuance of certificates containing wildcard domain names." §7.4.1

func (*Client) NewOrder

func (c *Client) NewOrder(ctx context.Context, account Account, order Order) (Order, error)

NewOrder creates a new order with the server.

"The client begins the certificate issuance process by sending a POST request to the server's newOrder resource." §7.4

func (*Client) PollAuthorization

func (c *Client) PollAuthorization(ctx context.Context, account Account, authz Authorization) (Authorization, error)

PollAuthorization polls the authorization resource endpoint until the authorization is considered "finalized" which means that it either succeeded, failed, or was abandoned. It blocks until that happens or until the configured timeout.

"Usually, the validation process will take some time, so the client will need to poll the authorization resource to see when it is finalized."

"For challenges where the client can tell when the server has validated the challenge (e.g., by seeing an HTTP or DNS request from the server), the client SHOULD NOT begin polling until it has seen the validation request from the server." §7.5.1

func (*Client) RevokeCertificate

func (c *Client) RevokeCertificate(ctx context.Context, account Account, cert *x509.Certificate, certKey crypto.Signer, reason int) error

RevokeCertificate revokes the given certificate. If the certificate key is not provided, then the account key is used instead. See §7.6.

func (*Client) UpdateAccount

func (c *Client) UpdateAccount(ctx context.Context, account Account) (Account, error)

UpdateAccount updates account information on the ACME server.

"If the client wishes to update this information in the future, it sends a POST request with updated information to the account URL. The server MUST ignore any updates to the 'orders' field, 'termsOfServiceAgreed' field (see Section 7.3.3), the 'status' field (except as allowed by Section 7.3.6), or any other fields it does not recognize." §7.3.2

This method uses the account.Location value as the account URL.

type Directory

type Directory struct {
	NewNonce    string         `json:"newNonce"`
	NewAccount  string         `json:"newAccount"`
	NewOrder    string         `json:"newOrder"`
	NewAuthz    string         `json:"newAuthz,omitempty"`
	RevokeCert  string         `json:"revokeCert"`
	KeyChange   string         `json:"keyChange"`
	RenewalInfo string         `json:"renewalInfo,omitempty"` // draft-ietf-acme-ari
	Meta        *DirectoryMeta `json:"meta,omitempty"`
}

Directory acts as an index for the ACME server as specified in the spec: "In order to help clients configure themselves with the right URLs for each ACME operation, ACME servers provide a directory object." §7.1.1

type DirectoryMeta

type DirectoryMeta struct {
	TermsOfService          string   `json:"termsOfService,omitempty"`
	Website                 string   `json:"website,omitempty"`
	CAAIdentities           []string `json:"caaIdentities,omitempty"`
	ExternalAccountRequired bool     `json:"externalAccountRequired,omitempty"`
}

DirectoryMeta is optional extra data that may be included in an ACME server directory. §7.1.1

type EAB

type EAB struct {
	// "The key identifier MUST be an ASCII string." §7.3.4
	KeyID string `json:"key_id"`

	// "The MAC key SHOULD be provided in base64url-encoded
	// form, to maximize compatibility between non-ACME
	// provisioning systems and ACME clients." §7.3.4
	MACKey string `json:"mac_key"`
}

EAB (External Account Binding) contains information necessary to bind or map an ACME account to some other account known by the CA.

External account bindings are "used to associate an ACME account with an existing account in a non-ACME system, such as a CA customer database."

"To enable ACME account binding, the CA operating the ACME server needs to provide the ACME client with a MAC key and a key identifier, using some mechanism outside of ACME." §7.3.4

type Identifier

type Identifier struct {
	// type (required, string):  The type of identifier.  This document
	// defines the "dns" identifier type.  See the registry defined in
	// Section 9.7.7 for any others.
	Type string `json:"type"`

	// value (required, string):  The identifier itself.
	Value string `json:"value"`
}

Identifier is used in order and authorization (authz) objects.

type Order

type Order struct {
	// status (required, string):  The status of this order.  Possible
	// values are "pending", "ready", "processing", "valid", and
	// "invalid".  See Section 7.1.6.
	Status string `json:"status,omitempty"`

	// expires (optional, string):  The timestamp after which the server
	// will consider this order invalid, encoded in the format specified
	// in [RFC3339].  This field is REQUIRED for objects with "pending"
	// or "valid" in the status field.
	Expires time.Time `json:"expires,omitempty"`

	// identifiers (required, array of object):  An array of identifier
	// objects that the order pertains to.
	Identifiers []Identifier `json:"identifiers"`

	// replaces (string, optional): A string uniquely identifying a
	// previously-issued certificate which this order is intended to replace.
	// This unique identifier is constructed in the same way as the path
	// component for GET requests described above. Clients SHOULD include
	// this field in New Order requests if there is a clear predecessor
	// certificate, as is the case for most certificate renewals.
	//
	// EXPERIMENTAL:  Draft ACME extension ARI: draft-ietf-acme-ari-03
	Replaces string `json:"replaces,omitempty"`

	// notBefore (optional, string):  The requested value of the notBefore
	// field in the certificate, in the date format defined in [RFC3339].
	NotBefore *time.Time `json:"notBefore,omitempty"`

	// notAfter (optional, string):  The requested value of the notAfter
	// field in the certificate, in the date format defined in [RFC3339].
	NotAfter *time.Time `json:"notAfter,omitempty"`

	// error (optional, object):  The error that occurred while processing
	// the order, if any.  This field is structured as a problem document
	// [RFC7807].
	Error *Problem `json:"error,omitempty"`

	// authorizations (required, array of string):  For pending orders, the
	// authorizations that the client needs to complete before the
	// requested certificate can be issued (see Section 7.5), including
	// unexpired authorizations that the client has completed in the past
	// for identifiers specified in the order.  The authorizations
	// required are dictated by server policy; there may not be a 1:1
	// relationship between the order identifiers and the authorizations
	// required.  For final orders (in the "valid" or "invalid" state),
	// the authorizations that were completed.  Each entry is a URL from
	// which an authorization can be fetched with a POST-as-GET request.
	Authorizations []string `json:"authorizations"`

	// finalize (required, string):  A URL that a CSR must be POSTed to once
	// all of the order's authorizations are satisfied to finalize the
	// order.  The result of a successful finalization will be the
	// population of the certificate URL for the order.
	Finalize string `json:"finalize"`

	// certificate (optional, string):  A URL for the certificate that has
	// been issued in response to this order.
	Certificate string `json:"certificate"`

	// Similar to new-account, the server returns a 201 response with
	// the URL to the order object in the Location header.
	//
	// We transfer the value from the header to this field for
	// storage and recall purposes.
	Location string `json:"-"`
}

Order is an object that "represents a client's request for a certificate and is used to track the progress of that order through to issuance. Thus, the object contains information about the requested certificate, the authorizations that the server requires the client to complete, and any certificates that have resulted from this order." §7.1.3

type Problem

type Problem struct {
	// "type" (string) - A URI reference [RFC3986] that identifies the
	// problem type.  This specification encourages that, when
	// dereferenced, it provide human-readable documentation for the
	// problem type (e.g., using HTML [W3C.REC-html5-20141028]).  When
	// this member is not present, its value is assumed to be
	// "about:blank". §3.1
	Type string `json:"type"`

	// "title" (string) - A short, human-readable summary of the problem
	// type.  It SHOULD NOT change from occurrence to occurrence of the
	// problem, except for purposes of localization (e.g., using
	// proactive content negotiation; see [RFC7231], Section 3.4). §3.1
	Title string `json:"title,omitempty"`

	// "status" (number) - The HTTP status code ([RFC7231], Section 6)
	// generated by the origin server for this occurrence of the problem.
	// §3.1
	Status int `json:"status,omitempty"`

	// "detail" (string) - A human-readable explanation specific to this
	// occurrence of the problem. §3.1
	//
	// RFC 8555 §6.7: "Clients SHOULD display the 'detail' field of all
	// errors."
	Detail string `json:"detail,omitempty"`

	// "instance" (string) - A URI reference that identifies the specific
	// occurrence of the problem.  It may or may not yield further
	// information if dereferenced. §3.1
	Instance string `json:"instance,omitempty"`

	// "Sometimes a CA may need to return multiple errors in response to a
	// request.  Additionally, the CA may need to attribute errors to
	// specific identifiers.  For instance, a newOrder request may contain
	// multiple identifiers for which the CA cannot issue certificates.  In
	// this situation, an ACME problem document MAY contain the
	// 'subproblems' field, containing a JSON array of problem documents."
	// RFC 8555 §6.7.1
	Subproblems []Subproblem `json:"subproblems,omitempty"`

	// For convenience, we've added this field to associate with a value
	// that is related to or caused the problem. It is not part of the
	// spec, but, if a challenge fails for example, we can associate the
	// error with the problematic authz object by setting this field.
	// Challenge failures will have this set to an Authorization type.
	Resource any `json:"-"`
}

Problem carries the details of an error from HTTP APIs as defined in RFC 7807: https://tools.ietf.org/html/rfc7807 and as extended by RFC 8555 §6.7: https://tools.ietf.org/html/rfc8555#section-6.7

func (Problem) Error

func (p Problem) Error() string

func (Problem) MarshalLogObject

func (p Problem) MarshalLogObject(enc zapcore.ObjectEncoder) error

MarshalLogObject satisfies the zapcore.ObjectMarshaler interface. This allows problems to be serialized by the zap logger.

type RenewalInfo

type RenewalInfo struct {
	// suggestedWindow (object, required): A JSON object with two keys,
	// "start" and "end", whose values are timestamps, encoded in the
	// format specified in [RFC3339], which bound the window of time
	// in which the CA recommends renewing the certificate.
	SuggestedWindow struct {
		Start time.Time `json:"start"`
		End   time.Time `json:"end"`
	} `json:"suggestedWindow"`

	// explanationURL (string, optional): A URL pointing to a page which may
	// explain why the suggested renewal window is what it is. For example,
	// it may be a page explaining the CA's dynamic load-balancing strategy,
	// or a page documenting which certificates are affected by a mass
	// revocation event. Conforming clients SHOULD provide this URL to their
	// operator, if present.
	ExplanationURL string `json:"explanationURL,omitempty"`

	// "The unique identifer is constructed by concatenating the
	// base64url-encoding Section 5 of [RFC4648] of the bytes of the
	// keyIdentifier field of certificate's Authority Key Identifier
	// (AKI) Section 4.2.1.1 of [RFC5280] extension, a literal period,
	// and the base64url-encoding of the bytes of the DER encoding of
	// the certificate's Serial Number (without the tag and length bytes).
	// All trailing "=" characters MUST be stripped from both parts of
	// the unique identifier."
	//
	// We generate this once and store it so the certificate does not
	// need to be stored in its decoded form or decoded multiple times.
	UniqueIdentifier string `json:"_uniqueIdentifier,omitempty"`

	// The next poll time based on the Retry-After response header for
	// the benefit of the caller for scheduling renewals. If specified,
	// GetRenewalInfo should not be called again before this time.
	//
	// "The server SHOULD include a Retry-After header indicating the polling
	// interval that the ACME server recommends. Conforming clients SHOULD
	// query the renewalInfo URL again after the Retry-After period has passed,
	// as the server may provide a different suggestedWindow."
	RetryAfter *time.Time `json:"_retryAfter,omitempty"`

	// The client should "select a uniform random time within the suggested
	// window." We select this time when getting the renewal info from the
	// server, though this behavior is ambiguous:
	// https://github.com/aarongable/draft-acme-ari/issues/70
	SelectedTime time.Time `json:"_selectedTime"`
}

RenewalInfo "is a new resource type introduced to ACME protocol. This new resource allows clients to query the server for suggestions on when they should renew certificates."

ACME Renewal Information (ARI): https://www.ietf.org/archive/id/draft-ietf-acme-ari-03.html §4.2

This is a DRAFT specification and the API is subject to change.

func (RenewalInfo) HasWindow added in v2.0.1

func (ari RenewalInfo) HasWindow() bool

HasWindow returns true if this ARI has a window. If not, it's likely because ARI is not supported or available.

func (RenewalInfo) NeedsRefresh added in v2.0.1

func (ari RenewalInfo) NeedsRefresh() bool

NeedsRefresh returns true if the renewal info needs updating. It returns false otherwise, or if the renewal info is empty (window is missing), assuming that there is no ARI available.

func (RenewalInfo) SameWindow added in v2.0.1

func (ari RenewalInfo) SameWindow(other RenewalInfo) bool

SameWindow returns true if this ARI has the same window as the ARI passed in. Note that suggested windows can move in either direction, expand, or contract, so this method compares both start and end values for exact equality.

type Subproblem

type Subproblem struct {
	Problem

	// "If present, the 'identifier' field MUST contain an ACME
	// identifier (Section 9.7.7)." §6.7.1
	Identifier Identifier `json:"identifier,omitempty"`
}

Subproblem describes a more specific error in a problem according to RFC 8555 §6.7.1: "An ACME problem document MAY contain the 'subproblems' field, containing a JSON array of problem documents, each of which MAY contain an 'identifier' field."

func (Subproblem) MarshalLogObject

func (sp Subproblem) MarshalLogObject(enc zapcore.ObjectEncoder) error

MarshalLogObject satisfies the zapcore.ObjectMarshaler interface. This allows subproblems to be serialized by the zap logger.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL