blob: aaafea2bc0829680cc6c24db1a6c22acf2ef44f7 [file] [log] [blame]
Alex Vaghin1777f3b2016-03-18 12:12:46 +00001// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Alex Vaghin611beeb2016-08-03 22:16:54 +02005// Package acme provides an implementation of the
Ben Burkert65fa2f72022-01-22 13:59:02 -05006// Automatic Certificate Management Environment (ACME) spec,
7// most famously used by Let's Encrypt.
8//
9// The initial implementation of this package was based on an early version
10// of the spec. The current implementation supports only the modern
11// RFC 8555 but some of the old API surface remains for compatibility.
12// While code using the old API will still compile, it will return an error.
13// Note the deprecation comments to update your code.
14//
15// See https://tools.ietf.org/html/rfc8555 for the spec.
Alex Vaghin1777f3b2016-03-18 12:12:46 +000016//
Alex Vaghind3c11942016-08-31 20:47:52 +020017// Most common scenarios will want to use autocert subdirectory instead,
18// which provides automatic access to certificates from Let's Encrypt
19// and any other ACME-based CA.
Alex Vaghin1777f3b2016-03-18 12:12:46 +000020package acme
21
22import (
Brad Fitzpatrick88915cc2017-04-02 13:18:05 -070023 "context"
Anmol Sethie0d166c2016-08-04 00:47:19 -040024 "crypto"
Alex Vaghin7e016f12016-08-21 18:20:10 +020025 "crypto/ecdsa"
26 "crypto/elliptic"
Alex Vaghin7a1054f2016-08-03 21:58:22 +020027 "crypto/rand"
Alex Vaghin7a1054f2016-08-03 21:58:22 +020028 "crypto/sha256"
29 "crypto/tls"
30 "crypto/x509"
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +000031 "crypto/x509/pkix"
32 "encoding/asn1"
Alex Vaghin1777f3b2016-03-18 12:12:46 +000033 "encoding/base64"
Alex Vaghin7a1054f2016-08-03 21:58:22 +020034 "encoding/hex"
Alex Vaghin1777f3b2016-03-18 12:12:46 +000035 "encoding/json"
Alex Vaghin7a1054f2016-08-03 21:58:22 +020036 "encoding/pem"
Alex Vaghin1777f3b2016-03-18 12:12:46 +000037 "errors"
38 "fmt"
Alex Vaghin7a1054f2016-08-03 21:58:22 +020039 "math/big"
Alex Vaghin1777f3b2016-03-18 12:12:46 +000040 "net/http"
Alex Vaghin1777f3b2016-03-18 12:12:46 +000041 "strings"
Alex Vaghinc2f49472016-06-10 12:13:04 +010042 "sync"
Alex Vaghin1777f3b2016-03-18 12:12:46 +000043 "time"
Alex Vaghin1777f3b2016-03-18 12:12:46 +000044)
45
Alex Vaghinc1264672018-07-23 17:26:11 +020046const (
47 // LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
Alex Vaghina9506012019-10-11 13:06:28 +020048 LetsEncryptURL = "https://acme-v02.api.letsencrypt.org/directory"
Alex Vaghinc1264672018-07-23 17:26:11 +020049
50 // ALPNProto is the ALPN protocol name used by a CA server when validating
51 // tls-alpn-01 challenges.
52 //
Filippo Valsorda56440b82018-08-02 18:11:18 -040053 // Package users must ensure their servers can negotiate the ACME ALPN in
54 // order for tls-alpn-01 challenge verifications to succeed.
55 // See the crypto/tls package's Config.NextProtos field.
Alex Vaghinc1264672018-07-23 17:26:11 +020056 ALPNProto = "acme-tls/1"
57)
Alex Vaghinc2f49472016-06-10 12:13:04 +010058
Jason Baker6ca56c22020-02-10 19:01:28 +000059// idPeACMEIdentifier is the OID for the ACME extension for the TLS-ALPN challenge.
60// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1
61var idPeACMEIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +000062
Alex Vaghin6575f7e2016-08-10 16:19:56 +020063const (
64 maxChainLen = 5 // max depth and breadth of a certificate chain
Alex Vaghin4663e182019-09-09 15:54:00 -040065 maxCertSize = 1 << 20 // max size of a certificate, in DER bytes
66 // Used for decoding certs from application/pem-certificate-chain response,
67 // the default when in RFC mode.
68 maxCertChainSize = maxCertSize * maxChainLen
Alex Vaghin92783772017-02-08 14:36:45 +010069
70 // Max number of collected nonces kept in memory.
71 // Expect usual peak of 1 or 2.
72 maxNonces = 100
Alex Vaghin6575f7e2016-08-10 16:19:56 +020073)
74
Alex Vaghin1777f3b2016-03-18 12:12:46 +000075// Client is an ACME client.
Ben Burkert65fa2f72022-01-22 13:59:02 -050076//
Alex Vaghinc2f49472016-06-10 12:13:04 +010077// The only required field is Key. An example of creating a client with a new key
78// is as follows:
79//
Russ Cox7b82a4e2022-04-11 13:06:45 -040080// key, err := rsa.GenerateKey(rand.Reader, 2048)
81// if err != nil {
82// log.Fatal(err)
83// }
84// client := &Client{Key: key}
Alex Vaghin1777f3b2016-03-18 12:12:46 +000085type Client struct {
Alex Vaghinc2f49472016-06-10 12:13:04 +010086 // Key is the account key used to register with a CA and sign requests.
Anmol Sethie0d166c2016-08-04 00:47:19 -040087 // Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
Alex Vaghinbfa7d422018-10-26 11:44:37 -070088 //
89 // The following algorithms are supported:
90 // RS256, ES256, ES384 and ES512.
Axel Wagner56aed062022-10-11 09:15:35 +020091 // See RFC 7518 for more details about the algorithms.
Anmol Sethie0d166c2016-08-04 00:47:19 -040092 Key crypto.Signer
Alex Vaghinc2f49472016-06-10 12:13:04 +010093
Alex Vaghina548aac2016-08-13 22:38:22 +020094 // HTTPClient optionally specifies an HTTP client to use
95 // instead of http.DefaultClient.
96 HTTPClient *http.Client
97
Alex Vaghinc2f49472016-06-10 12:13:04 +010098 // DirectoryURL points to the CA directory endpoint.
99 // If empty, LetsEncryptURL is used.
100 // Mutating this value after a successful call of Client's Discover method
101 // will have no effect.
102 DirectoryURL string
103
Alex Vaghindf8d4712018-04-26 21:26:21 +0200104 // RetryBackoff computes the duration after which the nth retry of a failed request
105 // should occur. The value of n for the first call on failure is 1.
106 // The values of r and resp are the request and response of the last failed attempt.
107 // If the returned value is negative or zero, no more retries are done and an error
108 // is returned to the caller of the original method.
109 //
110 // Requests which result in a 4xx client error are not retried,
111 // except for 400 Bad Request due to "bad nonce" errors and 429 Too Many Requests.
112 //
113 // If RetryBackoff is nil, a truncated exponential backoff algorithm
114 // with the ceiling of 10 seconds is used, where each subsequent retry n
115 // is done after either ("Retry-After" + jitter) or (2^n seconds + jitter),
116 // preferring the former if "Retry-After" header is found in the resp.
117 // The jitter is a random value up to 1 second.
118 RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
119
Filippo Valsordacc06ce42019-06-20 17:50:31 -0400120 // UserAgent is prepended to the User-Agent header sent to the ACME server,
121 // which by default is this package's name and version.
122 //
123 // Reusable libraries and tools in particular should set this value to be
124 // identifiable by the server, in case they are causing issues.
125 UserAgent string
126
Alex Vaghina8328652019-08-28 23:16:55 +0200127 cacheMu sync.Mutex
128 dir *Directory // cached result of Client's Discover method
Roland Shoemaker30dcbda2021-10-08 11:41:44 -0700129 // KID is the key identifier provided by the CA. If not provided it will be
130 // retrieved from the CA by making a call to the registration endpoint.
131 KID KeyID
Alex Vaghin92783772017-02-08 14:36:45 +0100132
133 noncesMu sync.Mutex
134 nonces map[string]struct{} // nonces collected from previous responses
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000135}
136
Alex Vaghina8328652019-08-28 23:16:55 +0200137// accountKID returns a key ID associated with c.Key, the account identity
138// provided by the CA during RFC based registration.
139// It assumes c.Discover has already been called.
140//
141// accountKID requires at most one network roundtrip.
142// It caches only successful result.
143//
144// When in pre-RFC mode or when c.getRegRFC responds with an error, accountKID
145// returns noKeyID.
Roland Shoemaker30dcbda2021-10-08 11:41:44 -0700146func (c *Client) accountKID(ctx context.Context) KeyID {
Alex Vaghina8328652019-08-28 23:16:55 +0200147 c.cacheMu.Lock()
148 defer c.cacheMu.Unlock()
Roland Shoemaker30dcbda2021-10-08 11:41:44 -0700149 if c.KID != noKeyID {
150 return c.KID
Alex Vaghina8328652019-08-28 23:16:55 +0200151 }
152 a, err := c.getRegRFC(ctx)
153 if err != nil {
154 return noKeyID
155 }
Roland Shoemaker30dcbda2021-10-08 11:41:44 -0700156 c.KID = KeyID(a.URI)
157 return c.KID
Alex Vaghina8328652019-08-28 23:16:55 +0200158}
159
Ben Burkert65fa2f72022-01-22 13:59:02 -0500160var errPreRFC = errors.New("acme: server does not support the RFC 8555 version of ACME")
161
Alex Vaghinc2f49472016-06-10 12:13:04 +0100162// Discover performs ACME server discovery using c.DirectoryURL.
163//
164// It caches successful result. So, subsequent calls will not result in
165// a network round-trip. This also means mutating c.DirectoryURL after successful call
166// of this method will have no effect.
Alex Vaghin807ffea2016-08-16 15:36:38 +0200167func (c *Client) Discover(ctx context.Context) (Directory, error) {
Alex Vaghina8328652019-08-28 23:16:55 +0200168 c.cacheMu.Lock()
169 defer c.cacheMu.Unlock()
Alex Vaghinc2f49472016-06-10 12:13:04 +0100170 if c.dir != nil {
171 return *c.dir, nil
172 }
173
Alex Vaghina4c6cb32019-02-12 18:56:05 +0100174 res, err := c.get(ctx, c.directoryURL(), wantStatus(http.StatusOK))
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000175 if err != nil {
Alex Vaghinc2f49472016-06-10 12:13:04 +0100176 return Directory{}, err
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000177 }
178 defer res.Body.Close()
Alex Vaghin92783772017-02-08 14:36:45 +0100179 c.addNonce(res.Header)
Alex Vaghinc2f49472016-06-10 12:13:04 +0100180
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000181 var v struct {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500182 Reg string `json:"newAccount"`
183 Authz string `json:"newAuthz"`
184 Order string `json:"newOrder"`
185 Revoke string `json:"revokeCert"`
186 Nonce string `json:"newNonce"`
187 KeyChange string `json:"keyChange"`
188 Meta struct {
189 Terms string `json:"termsOfService"`
190 Website string `json:"website"`
191 CAA []string `json:"caaIdentities"`
192 ExternalAcct bool `json:"externalAccountRequired"`
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000193 }
194 }
Alex Vaghinc7af5bf2017-04-25 20:31:00 +0200195 if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
Alex Vaghinc2f49472016-06-10 12:13:04 +0100196 return Directory{}, err
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000197 }
Ben Burkert65fa2f72022-01-22 13:59:02 -0500198 if v.Order == "" {
199 return Directory{}, errPreRFC
x1ddos2afe7c42019-08-20 17:44:11 +0200200 }
Alex Vaghinc2f49472016-06-10 12:13:04 +0100201 c.dir = &Directory{
Ben Burkert65fa2f72022-01-22 13:59:02 -0500202 RegURL: v.Reg,
203 AuthzURL: v.Authz,
204 OrderURL: v.Order,
205 RevokeURL: v.Revoke,
206 NonceURL: v.Nonce,
207 KeyChangeURL: v.KeyChange,
208 Terms: v.Meta.Terms,
209 Website: v.Meta.Website,
210 CAA: v.Meta.CAA,
211 ExternalAccountRequired: v.Meta.ExternalAcct,
Alex Vaghinc2f49472016-06-10 12:13:04 +0100212 }
213 return *c.dir, nil
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000214}
215
Alex Vaghina4c6cb32019-02-12 18:56:05 +0100216func (c *Client) directoryURL() string {
217 if c.DirectoryURL != "" {
218 return c.DirectoryURL
219 }
220 return LetsEncryptURL
221}
222
Ben Burkert65fa2f72022-01-22 13:59:02 -0500223// CreateCert was part of the old version of ACME. It is incompatible with RFC 8555.
Alex Vaghin4663e182019-09-09 15:54:00 -0400224//
Ben Burkert65fa2f72022-01-22 13:59:02 -0500225// Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateOrderCert.
Alex Vaghinc2f49472016-06-10 12:13:04 +0100226func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500227 return nil, "", errPreRFC
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000228}
229
230// FetchCert retrieves already issued certificate from the given url, in DER format.
231// It retries the request until the certificate is successfully retrieved,
232// context is cancelled by the caller or an error response is received.
233//
Alex Vaghin4663e182019-09-09 15:54:00 -0400234// If the bundle argument is true, the returned value also contains the CA (issuer)
235// certificate chain.
Alex Vaghin6575f7e2016-08-10 16:19:56 +0200236//
237// FetchCert returns an error if the CA's response or chain was unreasonably large.
238// Callers are encouraged to parse the returned value to ensure the certificate is valid
239// and has expected features.
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000240func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500241 if _, err := c.Discover(ctx); err != nil {
Alex Vaghin4663e182019-09-09 15:54:00 -0400242 return nil, err
243 }
Ben Burkert65fa2f72022-01-22 13:59:02 -0500244 return c.fetchCertRFC(ctx, url, bundle)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000245}
246
Alex Vaghin9fbab142016-08-16 16:28:56 +0200247// RevokeCert revokes a previously issued certificate cert, provided in DER format.
248//
249// The key argument, used to sign the request, must be authorized
250// to revoke the certificate. It's up to the CA to decide which keys are authorized.
251// For instance, the key pair of the certificate may be authorized.
252// If the key is nil, c.Key is used instead.
253func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500254 if _, err := c.Discover(ctx); err != nil {
Alex Vaghin9fbab142016-08-16 16:28:56 +0200255 return err
256 }
Ben Burkert65fa2f72022-01-22 13:59:02 -0500257 return c.revokeCertRFC(ctx, key, cert, reason)
Alex Vaghin9fbab142016-08-16 16:28:56 +0200258}
259
Alex Vaghina548aac2016-08-13 22:38:22 +0200260// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
Alex Vaghin1da79bd2016-06-10 12:38:58 +0100261// during account registration. See Register method of Client for more details.
Alex Vaghina548aac2016-08-13 22:38:22 +0200262func AcceptTOS(tosURL string) bool { return true }
Alex Vaghin1da79bd2016-06-10 12:38:58 +0100263
Alex Vaghina8328652019-08-28 23:16:55 +0200264// Register creates a new account with the CA using c.Key.
265// It returns the registered account. The account acct is not modified.
Alex Vaghin1da79bd2016-06-10 12:38:58 +0100266//
Alex Vaghina548aac2016-08-13 22:38:22 +0200267// The registration may require the caller to agree to the CA's Terms of Service (TOS).
Alex Vaghin1da79bd2016-06-10 12:38:58 +0100268// If so, and the account has not indicated the acceptance of the terms (see Account for details),
269// Register calls prompt with a TOS URL provided by the CA. Prompt should report
270// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
Alex Vaghina8328652019-08-28 23:16:55 +0200271//
Alex Vaghin4663e182019-09-09 15:54:00 -0400272// When interfacing with an RFC-compliant CA, non-RFC 8555 fields of acct are ignored
Alex Vaghina8328652019-08-28 23:16:55 +0200273// and prompt is called if Directory's Terms field is non-zero.
274// Also see Error's Instance field for when a CA requires already registered accounts to agree
275// to an updated Terms of Service.
276func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
James Kasten9d135272020-11-11 15:47:14 -0800277 if c.Key == nil {
278 return nil, errors.New("acme: client.Key must be set to Register")
279 }
Ben Burkert65fa2f72022-01-22 13:59:02 -0500280 if _, err := c.Discover(ctx); err != nil {
Alex Vaghinc2f49472016-06-10 12:13:04 +0100281 return nil, err
282 }
Ben Burkert65fa2f72022-01-22 13:59:02 -0500283 return c.registerRFC(ctx, acct, prompt)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000284}
285
Alex Vaghina8328652019-08-28 23:16:55 +0200286// GetReg retrieves an existing account associated with c.Key.
287//
Ben Burkert65fa2f72022-01-22 13:59:02 -0500288// The url argument is a legacy artifact of the pre-RFC 8555 API
289// and is ignored.
Alex Vaghin807ffea2016-08-16 15:36:38 +0200290func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500291 if _, err := c.Discover(ctx); err != nil {
Alex Vaghina8328652019-08-28 23:16:55 +0200292 return nil, err
293 }
Ben Burkert65fa2f72022-01-22 13:59:02 -0500294 return c.getRegRFC(ctx)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000295}
296
297// UpdateReg updates an existing registration.
298// It returns an updated account copy. The provided account is not modified.
Alex Vaghina8328652019-08-28 23:16:55 +0200299//
Ben Burkert65fa2f72022-01-22 13:59:02 -0500300// The account's URI is ignored and the account URL associated with
301// c.Key is used instead.
Alex Vaghina8328652019-08-28 23:16:55 +0200302func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error) {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500303 if _, err := c.Discover(ctx); err != nil {
Alex Vaghina8328652019-08-28 23:16:55 +0200304 return nil, err
305 }
Ben Burkert65fa2f72022-01-22 13:59:02 -0500306 return c.updateRegRFC(ctx, acct)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000307}
308
Jason Baker403b0172022-05-13 17:19:05 +0000309// AccountKeyRollover attempts to transition a client's account key to a new key.
310// On success client's Key is updated which is not concurrency safe.
311// On failure an error will be returned.
312// The new key is already registered with the ACME provider if the following is true:
Russ Coxbc19a972022-08-16 10:48:38 -0400313// - error is of type acme.Error
314// - StatusCode should be 409 (Conflict)
315// - Location header will have the KID of the associated account
Jason Baker403b0172022-05-13 17:19:05 +0000316//
317// More about account key rollover can be found at
318// https://tools.ietf.org/html/rfc8555#section-7.3.5.
319func (c *Client) AccountKeyRollover(ctx context.Context, newKey crypto.Signer) error {
320 return c.accountKeyRollover(ctx, newKey)
321}
322
Alex Vaghin4663e182019-09-09 15:54:00 -0400323// Authorize performs the initial step in the pre-authorization flow,
324// as opposed to order-based flow.
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000325// The caller will then need to choose from and perform a set of returned
326// challenges using c.Accept in order to successfully complete authorization.
Alex Vaghin1ba5ec02016-08-19 14:32:09 +0200327//
Alex Vaghin4663e182019-09-09 15:54:00 -0400328// Once complete, the caller can use AuthorizeOrder which the CA
329// should provision with the already satisfied authorization.
330// For pre-RFC CAs, the caller can proceed directly to requesting a certificate
331// using CreateCert method.
332//
Alex Vaghin1ba5ec02016-08-19 14:32:09 +0200333// If an authorization has been previously granted, the CA may return
Alex Vaghin4663e182019-09-09 15:54:00 -0400334// a valid authorization which has its Status field set to StatusValid.
335//
336// More about pre-authorization can be found at
337// https://tools.ietf.org/html/rfc8555#section-7.4.1.
Alex Vaghin807ffea2016-08-16 15:36:38 +0200338func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
Alex Vaghinb01c7a72019-01-29 19:10:45 +0100339 return c.authorize(ctx, "dns", domain)
340}
341
342// AuthorizeIP is the same as Authorize but requests IP address authorization.
343// Clients which successfully obtain such authorization may request to issue
344// a certificate for IP addresses.
345//
346// See the ACME spec extension for more details about IP address identifiers:
347// https://tools.ietf.org/html/draft-ietf-acme-ip.
348func (c *Client) AuthorizeIP(ctx context.Context, ipaddr string) (*Authorization, error) {
349 return c.authorize(ctx, "ip", ipaddr)
350}
351
352func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization, error) {
Alex Vaghin807ffea2016-08-16 15:36:38 +0200353 if _, err := c.Discover(ctx); err != nil {
Alex Vaghinc2f49472016-06-10 12:13:04 +0100354 return nil, err
355 }
356
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000357 type authzID struct {
358 Type string `json:"type"`
359 Value string `json:"value"`
360 }
361 req := struct {
362 Resource string `json:"resource"`
363 Identifier authzID `json:"identifier"`
364 }{
365 Resource: "new-authz",
Alex Vaghinb01c7a72019-01-29 19:10:45 +0100366 Identifier: authzID{Type: typ, Value: val},
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000367 }
Alex Vaghina8328652019-08-28 23:16:55 +0200368 res, err := c.post(ctx, nil, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000369 if err != nil {
370 return nil, err
371 }
372 defer res.Body.Close()
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000373
374 var v wireAuthz
375 if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
Alex Vaghina548aac2016-08-13 22:38:22 +0200376 return nil, fmt.Errorf("acme: invalid response: %v", err)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000377 }
Alex Vaghin1ba5ec02016-08-19 14:32:09 +0200378 if v.Status != StatusPending && v.Status != StatusValid {
Alex Vaghina548aac2016-08-13 22:38:22 +0200379 return nil, fmt.Errorf("acme: unexpected status: %s", v.Status)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000380 }
381 return v.authorization(res.Header.Get("Location")), nil
382}
383
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200384// GetAuthorization retrieves an authorization identified by the given URL.
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000385//
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200386// If a caller needs to poll an authorization until its status is final,
387// see the WaitAuthorization method.
388func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500389 if _, err := c.Discover(ctx); err != nil {
Alex Vaghin4663e182019-09-09 15:54:00 -0400390 return nil, err
391 }
392
Ben Burkert65fa2f72022-01-22 13:59:02 -0500393 res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000394 if err != nil {
395 return nil, err
396 }
397 defer res.Body.Close()
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000398 var v wireAuthz
399 if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
Alex Vaghina548aac2016-08-13 22:38:22 +0200400 return nil, fmt.Errorf("acme: invalid response: %v", err)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000401 }
402 return v.authorization(url), nil
403}
404
Alex Vaghinf9440962016-09-07 16:50:43 +0200405// RevokeAuthorization relinquishes an existing authorization identified
406// by the given URL.
407// The url argument is an Authorization.URI value.
408//
409// If successful, the caller will be required to obtain a new authorization
Alex Vaghin4663e182019-09-09 15:54:00 -0400410// using the Authorize or AuthorizeOrder methods before being able to request
411// a new certificate for the domain associated with the authorization.
Alex Vaghinf9440962016-09-07 16:50:43 +0200412//
413// It does not revoke existing certificates.
414func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
Alex Vaghina8328652019-08-28 23:16:55 +0200415 if _, err := c.Discover(ctx); err != nil {
416 return err
417 }
418
Alex Vaghinf9440962016-09-07 16:50:43 +0200419 req := struct {
420 Resource string `json:"resource"`
Alex Vaghinca7e7f12016-10-25 14:52:55 +0200421 Status string `json:"status"`
Alex Vaghinf9440962016-09-07 16:50:43 +0200422 Delete bool `json:"delete"`
423 }{
424 Resource: "authz",
Alex Vaghinca7e7f12016-10-25 14:52:55 +0200425 Status: "deactivated",
Alex Vaghinf9440962016-09-07 16:50:43 +0200426 Delete: true,
427 }
Alex Vaghina8328652019-08-28 23:16:55 +0200428 res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
Alex Vaghinf9440962016-09-07 16:50:43 +0200429 if err != nil {
430 return err
431 }
432 defer res.Body.Close()
Alex Vaghinf9440962016-09-07 16:50:43 +0200433 return nil
434}
435
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200436// WaitAuthorization polls an authorization at the given URL
437// until it is in one of the final states, StatusValid or StatusInvalid,
Alex Vaghin91a49db2018-02-28 16:18:34 +0100438// the ACME CA responded with a 4xx error code, or the context is done.
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200439//
440// It returns a non-nil Authorization only if its Status is StatusValid.
441// In all other cases WaitAuthorization returns an error.
Alex Vaghin141c7622017-04-12 13:08:46 +0200442// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200443func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500444 if _, err := c.Discover(ctx); err != nil {
Alex Vaghin4663e182019-09-09 15:54:00 -0400445 return nil, err
446 }
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200447 for {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500448 res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200449 if err != nil {
450 return nil, err
451 }
Alex Vaghin91a49db2018-02-28 16:18:34 +0100452
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200453 var raw wireAuthz
454 err = json.NewDecoder(res.Body).Decode(&raw)
455 res.Body.Close()
Alex Vaghindf8d4712018-04-26 21:26:21 +0200456 switch {
457 case err != nil:
458 // Skip and retry.
459 case raw.Status == StatusValid:
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200460 return raw.authorization(url), nil
Alex Vaghindf8d4712018-04-26 21:26:21 +0200461 case raw.Status == StatusInvalid:
Alex Vaghin141c7622017-04-12 13:08:46 +0200462 return nil, raw.error(url)
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200463 }
Alex Vaghindf8d4712018-04-26 21:26:21 +0200464
465 // Exponential backoff is implemented in c.get above.
466 // This is just to prevent continuously hitting the CA
467 // while waiting for a final authorization status.
468 d := retryAfter(res.Header.Get("Retry-After"))
469 if d == 0 {
470 // Given that the fastest challenges TLS-SNI and HTTP-01
471 // require a CA to make at least 1 network round trip
472 // and most likely persist a challenge state,
473 // this default delay seems reasonable.
474 d = time.Second
475 }
476 t := time.NewTimer(d)
477 select {
478 case <-ctx.Done():
479 t.Stop()
480 return nil, ctx.Err()
481 case <-t.C:
482 // Retry.
Alex Vaghinb35ccbc2016-08-19 11:32:22 +0200483 }
484 }
485}
486
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000487// GetChallenge retrieves the current status of an challenge.
488//
489// A client typically polls a challenge status using this method.
Alex Vaghin807ffea2016-08-16 15:36:38 +0200490func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500491 if _, err := c.Discover(ctx); err != nil {
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000492 return nil, err
493 }
Alex Vaghin4663e182019-09-09 15:54:00 -0400494
Ben Burkert65fa2f72022-01-22 13:59:02 -0500495 res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
Alex Vaghin4663e182019-09-09 15:54:00 -0400496 if err != nil {
497 return nil, err
498 }
499
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000500 defer res.Body.Close()
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000501 v := wireChallenge{URI: url}
502 if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
Alex Vaghina548aac2016-08-13 22:38:22 +0200503 return nil, fmt.Errorf("acme: invalid response: %v", err)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000504 }
505 return v.challenge(), nil
506}
507
508// Accept informs the server that the client accepts one of its challenges
509// previously obtained with c.Authorize.
510//
511// The server will then perform the validation asynchronously.
Alex Vaghin807ffea2016-08-16 15:36:38 +0200512func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
Ben Burkert65fa2f72022-01-22 13:59:02 -0500513 if _, err := c.Discover(ctx); err != nil {
Anmol Sethie0d166c2016-08-04 00:47:19 -0400514 return nil, err
515 }
516
Ben Burkert65fa2f72022-01-22 13:59:02 -0500517 res, err := c.post(ctx, nil, chal.URI, json.RawMessage("{}"), wantStatus(
Alex Vaghindf8d4712018-04-26 21:26:21 +0200518 http.StatusOK, // according to the spec
519 http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
520 ))
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000521 if err != nil {
522 return nil, err
523 }
524 defer res.Body.Close()
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000525
526 var v wireChallenge
527 if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
Alex Vaghina548aac2016-08-13 22:38:22 +0200528 return nil, fmt.Errorf("acme: invalid response: %v", err)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000529 }
530 return v.challenge(), nil
531}
532
Alex Vaghin351dc6a2016-08-24 15:50:57 +0200533// DNS01ChallengeRecord returns a DNS record value for a dns-01 challenge response.
534// A TXT record containing the returned value must be provisioned under
535// "_acme-challenge" name of the domain being validated.
536//
537// The token argument is a Challenge.Token value.
538func (c *Client) DNS01ChallengeRecord(token string) (string, error) {
539 ka, err := keyAuth(c.Key.Public(), token)
540 if err != nil {
541 return "", err
542 }
543 b := sha256.Sum256([]byte(ka))
544 return base64.RawURLEncoding.EncodeToString(b[:]), nil
545}
546
Alex Vaghin1f83de12016-08-14 22:48:00 +0200547// HTTP01ChallengeResponse returns the response for an http-01 challenge.
548// Servers should respond with the value to HTTP requests at the URL path
549// provided by HTTP01ChallengePath to validate the challenge and prove control
550// over a domain name.
551//
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000552// The token argument is a Challenge.Token value.
Alex Vaghin1f83de12016-08-14 22:48:00 +0200553func (c *Client) HTTP01ChallengeResponse(token string) (string, error) {
554 return keyAuth(c.Key.Public(), token)
555}
556
557// HTTP01ChallengePath returns the URL path at which the response for an http-01 challenge
558// should be provided by the servers.
559// The response value can be obtained with HTTP01ChallengeResponse.
560//
561// The token argument is a Challenge.Token value.
562func (c *Client) HTTP01ChallengePath(token string) string {
563 return "/.well-known/acme-challenge/" + token
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000564}
565
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200566// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200567//
Ben Burkert65fa2f72022-01-22 13:59:02 -0500568// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200569func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
Anmol Sethie0d166c2016-08-04 00:47:19 -0400570 ka, err := keyAuth(c.Key.Public(), token)
571 if err != nil {
Alex Vaghin595bbbd2016-08-11 17:03:39 +0200572 return tls.Certificate{}, "", err
Anmol Sethie0d166c2016-08-04 00:47:19 -0400573 }
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200574 b := sha256.Sum256([]byte(ka))
575 h := hex.EncodeToString(b[:])
Alex Vaghin595bbbd2016-08-11 17:03:39 +0200576 name = fmt.Sprintf("%s.%s.acme.invalid", h[:32], h[32:])
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200577 cert, err = tlsChallengeCert([]string{name}, opt)
Alex Vaghin595bbbd2016-08-11 17:03:39 +0200578 if err != nil {
579 return tls.Certificate{}, "", err
580 }
581 return cert, name, nil
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200582}
583
584// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200585//
Ben Burkert65fa2f72022-01-22 13:59:02 -0500586// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200587func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200588 b := sha256.Sum256([]byte(token))
589 h := hex.EncodeToString(b[:])
590 sanA := fmt.Sprintf("%s.%s.token.acme.invalid", h[:32], h[32:])
591
Anmol Sethie0d166c2016-08-04 00:47:19 -0400592 ka, err := keyAuth(c.Key.Public(), token)
593 if err != nil {
Alex Vaghin595bbbd2016-08-11 17:03:39 +0200594 return tls.Certificate{}, "", err
Anmol Sethie0d166c2016-08-04 00:47:19 -0400595 }
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200596 b = sha256.Sum256([]byte(ka))
597 h = hex.EncodeToString(b[:])
598 sanB := fmt.Sprintf("%s.%s.ka.acme.invalid", h[:32], h[32:])
599
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200600 cert, err = tlsChallengeCert([]string{sanA, sanB}, opt)
Alex Vaghin595bbbd2016-08-11 17:03:39 +0200601 if err != nil {
602 return tls.Certificate{}, "", err
603 }
604 return cert, sanA, nil
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200605}
606
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +0000607// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
608// Servers can present the certificate to validate the challenge and prove control
609// over a domain name. For more details on TLS-ALPN-01 see
610// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
611//
612// The token argument is a Challenge.Token value.
613// If a WithKey option is provided, its private part signs the returned cert,
614// and the public part is used to specify the signee.
615// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
616//
617// The returned certificate is valid for the next 24 hours and must be presented only when
618// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
619// has been specified.
620func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) (cert tls.Certificate, err error) {
621 ka, err := keyAuth(c.Key.Public(), token)
622 if err != nil {
623 return tls.Certificate{}, err
624 }
625 shasum := sha256.Sum256([]byte(ka))
Roland Shoemakere6b12002018-06-13 13:00:12 -0700626 extValue, err := asn1.Marshal(shasum[:])
627 if err != nil {
628 return tls.Certificate{}, err
629 }
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +0000630 acmeExtension := pkix.Extension{
Jason Baker6ca56c22020-02-10 19:01:28 +0000631 Id: idPeACMEIdentifier,
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +0000632 Critical: true,
Roland Shoemakere6b12002018-06-13 13:00:12 -0700633 Value: extValue,
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +0000634 }
635
636 tmpl := defaultTLSChallengeCertTemplate()
637
638 var newOpt []CertOption
639 for _, o := range opt {
640 switch o := o.(type) {
641 case *certOptTemplate:
642 t := *(*x509.Certificate)(o) // shallow copy is ok
643 tmpl = &t
644 default:
645 newOpt = append(newOpt, o)
646 }
647 }
648 tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension)
649 newOpt = append(newOpt, WithTemplate(tmpl))
650 return tlsChallengeCert([]string{domain}, newOpt)
651}
652
Alex Vaghin92783772017-02-08 14:36:45 +0100653// popNonce returns a nonce value previously stored with c.addNonce
Alex Vaghin2682ddc2019-08-21 01:03:43 +0200654// or fetches a fresh one from c.dir.NonceURL.
655// If NonceURL is empty, it first tries c.directoryURL() and, failing that,
656// the provided url.
Alex Vaghin92783772017-02-08 14:36:45 +0100657func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
658 c.noncesMu.Lock()
659 defer c.noncesMu.Unlock()
660 if len(c.nonces) == 0 {
Alex Vaghin2682ddc2019-08-21 01:03:43 +0200661 if c.dir != nil && c.dir.NonceURL != "" {
662 return c.fetchNonce(ctx, c.dir.NonceURL)
663 }
Alex Vaghina4c6cb32019-02-12 18:56:05 +0100664 dirURL := c.directoryURL()
665 v, err := c.fetchNonce(ctx, dirURL)
666 if err != nil && url != dirURL {
667 v, err = c.fetchNonce(ctx, url)
668 }
669 return v, err
Alex Vaghin92783772017-02-08 14:36:45 +0100670 }
671 var nonce string
672 for nonce = range c.nonces {
673 delete(c.nonces, nonce)
674 break
675 }
676 return nonce, nil
677}
678
James Hartigcbc3d082017-04-09 14:29:52 -0400679// clearNonces clears any stored nonces
680func (c *Client) clearNonces() {
681 c.noncesMu.Lock()
682 defer c.noncesMu.Unlock()
683 c.nonces = make(map[string]struct{})
684}
685
Alex Vaghin92783772017-02-08 14:36:45 +0100686// addNonce stores a nonce value found in h (if any) for future use.
687func (c *Client) addNonce(h http.Header) {
688 v := nonceFromHeader(h)
689 if v == "" {
690 return
691 }
692 c.noncesMu.Lock()
693 defer c.noncesMu.Unlock()
694 if len(c.nonces) >= maxNonces {
695 return
696 }
697 if c.nonces == nil {
698 c.nonces = make(map[string]struct{})
699 }
700 c.nonces[v] = struct{}{}
701}
702
Brad Fitzpatrick6022e332017-04-08 17:54:05 +0000703func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
Alex Vaghindf8d4712018-04-26 21:26:21 +0200704 r, err := http.NewRequest("HEAD", url, nil)
705 if err != nil {
706 return "", err
707 }
708 resp, err := c.doNoRetry(ctx, r)
Alex Vaghin92783772017-02-08 14:36:45 +0100709 if err != nil {
710 return "", err
711 }
712 defer resp.Body.Close()
713 nonce := nonceFromHeader(resp.Header)
714 if nonce == "" {
715 if resp.StatusCode > 299 {
716 return "", responseError(resp)
717 }
718 return "", errors.New("acme: nonce not found")
719 }
720 return nonce, nil
721}
722
723func nonceFromHeader(h http.Header) string {
724 return h.Get("Replay-Nonce")
725}
726
Alex Vaghin6575f7e2016-08-10 16:19:56 +0200727// linkHeader returns URI-Reference values of all Link headers
728// with relation-type rel.
729// See https://tools.ietf.org/html/rfc5988#section-5 for details.
730func linkHeader(h http.Header, rel string) []string {
731 var links []string
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000732 for _, v := range h["Link"] {
733 parts := strings.Split(v, ";")
734 for _, p := range parts {
735 p = strings.TrimSpace(p)
736 if !strings.HasPrefix(p, "rel=") {
737 continue
738 }
739 if v := strings.Trim(p[4:], `"`); v == rel {
Alex Vaghin6575f7e2016-08-10 16:19:56 +0200740 links = append(links, strings.Trim(parts[0], "<>"))
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000741 }
742 }
743 }
Alex Vaghin6575f7e2016-08-10 16:19:56 +0200744 return links
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000745}
746
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000747// keyAuth generates a key authorization string for a given token.
Anmol Sethie0d166c2016-08-04 00:47:19 -0400748func keyAuth(pub crypto.PublicKey, token string) (string, error) {
749 th, err := JWKThumbprint(pub)
750 if err != nil {
751 return "", err
752 }
753 return fmt.Sprintf("%s.%s", token, th), nil
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000754}
755
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +0000756// defaultTLSChallengeCertTemplate is a template used to create challenge certs for TLS challenges.
757func defaultTLSChallengeCertTemplate() *x509.Certificate {
758 return &x509.Certificate{
759 SerialNumber: big.NewInt(1),
760 NotBefore: time.Now(),
761 NotAfter: time.Now().Add(24 * time.Hour),
762 BasicConstraintsValid: true,
763 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
764 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
765 }
766}
767
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200768// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200769// with the given SANs and auto-generated public/private key pair.
Alex Vaghin959b3af2017-10-16 15:58:10 +0200770// The Subject Common Name is set to the first SAN to aid debugging.
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200771// To create a cert with a custom key pair, specify WithKey option.
772func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +0000773 var key crypto.Signer
774 tmpl := defaultTLSChallengeCertTemplate()
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200775 for _, o := range opt {
776 switch o := o.(type) {
777 case *certOptKey:
778 if key != nil {
779 return tls.Certificate{}, errors.New("acme: duplicate key option")
780 }
781 key = o.key
Alex Vaghine3112312016-09-12 12:18:32 +0200782 case *certOptTemplate:
Maciej Dębski8ac0e0d2018-06-07 13:59:11 +0000783 t := *(*x509.Certificate)(o) // shallow copy is ok
Alex Vaghine3112312016-09-12 12:18:32 +0200784 tmpl = &t
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200785 default:
786 // package's fault, if we let this happen:
787 panic(fmt.Sprintf("unsupported option type %T", o))
788 }
789 }
790 if key == nil {
791 var err error
792 if key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil {
793 return tls.Certificate{}, err
794 }
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200795 }
Alex Vaghine3112312016-09-12 12:18:32 +0200796 tmpl.DNSNames = san
Alex Vaghin959b3af2017-10-16 15:58:10 +0200797 if len(san) > 0 {
798 tmpl.Subject.CommonName = san[0]
799 }
Alex Vaghine3112312016-09-12 12:18:32 +0200800
801 der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
Alex Vaghinb13fc1f2016-08-25 09:06:35 +0200802 if err != nil {
803 return tls.Certificate{}, err
804 }
Alex Vaghin5f961cd2016-08-10 16:35:40 +0200805 return tls.Certificate{
806 Certificate: [][]byte{der},
807 PrivateKey: key,
808 }, nil
Alex Vaghin7a1054f2016-08-03 21:58:22 +0200809}
810
811// encodePEM returns b encoded as PEM with block of type typ.
812func encodePEM(typ string, b []byte) []byte {
813 pb := &pem.Block{Type: typ, Bytes: b}
814 return pem.EncodeToMemory(pb)
815}
816
Ben Burkert65fa2f72022-01-22 13:59:02 -0500817// timeNow is time.Now, except in tests which can mess with it.
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000818var timeNow = time.Now