rfc8555.go raw

   1  // Copyright 2019 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  
   5  package acme
   6  
   7  import (
   8  	"context"
   9  	"crypto"
  10  	"encoding/base64"
  11  	"encoding/json"
  12  	"encoding/pem"
  13  	"errors"
  14  	"fmt"
  15  	"io"
  16  	"net/http"
  17  	"time"
  18  )
  19  
  20  // DeactivateReg permanently disables an existing account associated with c.Key.
  21  // A deactivated account can no longer request certificate issuance or access
  22  // resources related to the account, such as orders or authorizations.
  23  //
  24  // It only works with CAs implementing RFC 8555.
  25  func (c *Client) DeactivateReg(ctx context.Context) error {
  26  	if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
  27  		return err
  28  	}
  29  	url := string(c.accountKID(ctx))
  30  	if url == "" {
  31  		return ErrNoAccount
  32  	}
  33  	req := json.RawMessage(`{"status": "deactivated"}`)
  34  	res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
  35  	if err != nil {
  36  		return err
  37  	}
  38  	res.Body.Close()
  39  	return nil
  40  }
  41  
  42  // registerRFC is equivalent to c.Register but for CAs implementing RFC 8555.
  43  // It expects c.Discover to have already been called.
  44  func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
  45  	c.cacheMu.Lock() // guard c.kid access
  46  	defer c.cacheMu.Unlock()
  47  
  48  	req := struct {
  49  		TermsAgreed            bool              `json:"termsOfServiceAgreed,omitempty"`
  50  		Contact                []string          `json:"contact,omitempty"`
  51  		ExternalAccountBinding *jsonWebSignature `json:"externalAccountBinding,omitempty"`
  52  	}{
  53  		Contact: acct.Contact,
  54  	}
  55  	if c.dir.Terms != "" {
  56  		req.TermsAgreed = prompt(c.dir.Terms)
  57  	}
  58  
  59  	// set 'externalAccountBinding' field if requested
  60  	if acct.ExternalAccountBinding != nil {
  61  		eabJWS, err := c.encodeExternalAccountBinding(acct.ExternalAccountBinding)
  62  		if err != nil {
  63  			return nil, fmt.Errorf("acme: failed to encode external account binding: %v", err)
  64  		}
  65  		req.ExternalAccountBinding = eabJWS
  66  	}
  67  
  68  	res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(
  69  		http.StatusOK,      // account with this key already registered
  70  		http.StatusCreated, // new account created
  71  	))
  72  	if err != nil {
  73  		return nil, err
  74  	}
  75  
  76  	defer res.Body.Close()
  77  	a, err := responseAccount(res)
  78  	if err != nil {
  79  		return nil, err
  80  	}
  81  	// Cache Account URL even if we return an error to the caller.
  82  	// It is by all means a valid and usable "kid" value for future requests.
  83  	c.KID = KeyID(a.URI)
  84  	if res.StatusCode == http.StatusOK {
  85  		return nil, ErrAccountAlreadyExists
  86  	}
  87  	return a, nil
  88  }
  89  
  90  // encodeExternalAccountBinding will encode an external account binding stanza
  91  // as described in https://tools.ietf.org/html/rfc8555#section-7.3.4.
  92  func (c *Client) encodeExternalAccountBinding(eab *ExternalAccountBinding) (*jsonWebSignature, error) {
  93  	jwk, err := jwkEncode(c.Key.Public())
  94  	if err != nil {
  95  		return nil, err
  96  	}
  97  	return jwsWithMAC(eab.Key, eab.KID, c.dir.RegURL, []byte(jwk))
  98  }
  99  
 100  // updateRegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555.
 101  // It expects c.Discover to have already been called.
 102  func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) {
 103  	url := string(c.accountKID(ctx))
 104  	if url == "" {
 105  		return nil, ErrNoAccount
 106  	}
 107  	req := struct {
 108  		Contact []string `json:"contact,omitempty"`
 109  	}{
 110  		Contact: a.Contact,
 111  	}
 112  	res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
 113  	if err != nil {
 114  		return nil, err
 115  	}
 116  	defer res.Body.Close()
 117  	return responseAccount(res)
 118  }
 119  
 120  // getRegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555.
 121  // It expects c.Discover to have already been called.
 122  func (c *Client) getRegRFC(ctx context.Context) (*Account, error) {
 123  	req := json.RawMessage(`{"onlyReturnExisting": true}`)
 124  	res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(http.StatusOK))
 125  	if e, ok := err.(*Error); ok && e.ProblemType == "urn:ietf:params:acme:error:accountDoesNotExist" {
 126  		return nil, ErrNoAccount
 127  	}
 128  	if err != nil {
 129  		return nil, err
 130  	}
 131  
 132  	defer res.Body.Close()
 133  	return responseAccount(res)
 134  }
 135  
 136  func responseAccount(res *http.Response) (*Account, error) {
 137  	var v struct {
 138  		Status  string
 139  		Contact []string
 140  		Orders  string
 141  	}
 142  	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 143  		return nil, fmt.Errorf("acme: invalid account response: %v", err)
 144  	}
 145  	return &Account{
 146  		URI:       res.Header.Get("Location"),
 147  		Status:    v.Status,
 148  		Contact:   v.Contact,
 149  		OrdersURL: v.Orders,
 150  	}, nil
 151  }
 152  
 153  // accountKeyRollover attempts to perform account key rollover.
 154  // On success it will change client.Key to the new key.
 155  func (c *Client) accountKeyRollover(ctx context.Context, newKey crypto.Signer) error {
 156  	dir, err := c.Discover(ctx) // Also required by c.accountKID
 157  	if err != nil {
 158  		return err
 159  	}
 160  	kid := c.accountKID(ctx)
 161  	if kid == noKeyID {
 162  		return ErrNoAccount
 163  	}
 164  	oldKey, err := jwkEncode(c.Key.Public())
 165  	if err != nil {
 166  		return err
 167  	}
 168  	payload := struct {
 169  		Account string          `json:"account"`
 170  		OldKey  json.RawMessage `json:"oldKey"`
 171  	}{
 172  		Account: string(kid),
 173  		OldKey:  json.RawMessage(oldKey),
 174  	}
 175  	inner, err := jwsEncodeJSON(payload, newKey, noKeyID, noNonce, dir.KeyChangeURL)
 176  	if err != nil {
 177  		return err
 178  	}
 179  
 180  	res, err := c.post(ctx, nil, dir.KeyChangeURL, base64.RawURLEncoding.EncodeToString(inner), wantStatus(http.StatusOK))
 181  	if err != nil {
 182  		return err
 183  	}
 184  	defer res.Body.Close()
 185  	c.Key = newKey
 186  	return nil
 187  }
 188  
 189  // AuthorizeOrder initiates the order-based application for certificate issuance,
 190  // as opposed to pre-authorization in Authorize.
 191  // It is only supported by CAs implementing RFC 8555.
 192  //
 193  // The caller then needs to fetch each authorization with GetAuthorization,
 194  // identify those with StatusPending status and fulfill a challenge using Accept.
 195  // Once all authorizations are satisfied, the caller will typically want to poll
 196  // order status using WaitOrder until it's in StatusReady state.
 197  // To finalize the order and obtain a certificate, the caller submits a CSR with CreateOrderCert.
 198  func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) {
 199  	dir, err := c.Discover(ctx)
 200  	if err != nil {
 201  		return nil, err
 202  	}
 203  
 204  	req := struct {
 205  		Identifiers []wireAuthzID `json:"identifiers"`
 206  		NotBefore   string        `json:"notBefore,omitempty"`
 207  		NotAfter    string        `json:"notAfter,omitempty"`
 208  	}{}
 209  	for _, v := range id {
 210  		req.Identifiers = append(req.Identifiers, wireAuthzID{
 211  			Type:  v.Type,
 212  			Value: v.Value,
 213  		})
 214  	}
 215  	for _, o := range opt {
 216  		switch o := o.(type) {
 217  		case orderNotBeforeOpt:
 218  			req.NotBefore = time.Time(o).Format(time.RFC3339)
 219  		case orderNotAfterOpt:
 220  			req.NotAfter = time.Time(o).Format(time.RFC3339)
 221  		default:
 222  			// Package's fault if we let this happen.
 223  			panic(fmt.Sprintf("unsupported order option type %T", o))
 224  		}
 225  	}
 226  
 227  	res, err := c.post(ctx, nil, dir.OrderURL, req, wantStatus(http.StatusCreated))
 228  	if err != nil {
 229  		return nil, err
 230  	}
 231  	defer res.Body.Close()
 232  	return responseOrder(res)
 233  }
 234  
 235  // GetOrder retrieves an order identified by the given URL.
 236  // For orders created with AuthorizeOrder, the url value is Order.URI.
 237  //
 238  // If a caller needs to poll an order until its status is final,
 239  // see the WaitOrder method.
 240  func (c *Client) GetOrder(ctx context.Context, url string) (*Order, error) {
 241  	if _, err := c.Discover(ctx); err != nil {
 242  		return nil, err
 243  	}
 244  
 245  	res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
 246  	if err != nil {
 247  		return nil, err
 248  	}
 249  	defer res.Body.Close()
 250  	return responseOrder(res)
 251  }
 252  
 253  // WaitOrder polls an order from the given URL until it is in one of the final states,
 254  // StatusReady, StatusValid or StatusInvalid, the CA responded with a non-retryable error
 255  // or the context is done.
 256  //
 257  // It returns a non-nil Order only if its Status is StatusReady or StatusValid.
 258  // In all other cases WaitOrder returns an error.
 259  // If the Status is StatusInvalid, the returned error is of type *OrderError.
 260  func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
 261  	if _, err := c.Discover(ctx); err != nil {
 262  		return nil, err
 263  	}
 264  	for {
 265  		res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
 266  		if err != nil {
 267  			return nil, err
 268  		}
 269  		o, err := responseOrder(res)
 270  		res.Body.Close()
 271  		switch {
 272  		case err != nil:
 273  			// Skip and retry.
 274  		case o.Status == StatusInvalid:
 275  			return nil, &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
 276  		case o.Status == StatusReady || o.Status == StatusValid:
 277  			return o, nil
 278  		}
 279  
 280  		d := retryAfter(res.Header.Get("Retry-After"))
 281  		if d == 0 {
 282  			// Default retry-after.
 283  			// Same reasoning as in WaitAuthorization.
 284  			d = time.Second
 285  		}
 286  		t := time.NewTimer(d)
 287  		select {
 288  		case <-ctx.Done():
 289  			t.Stop()
 290  			return nil, ctx.Err()
 291  		case <-t.C:
 292  			// Retry.
 293  		}
 294  	}
 295  }
 296  
 297  func responseOrder(res *http.Response) (*Order, error) {
 298  	var v struct {
 299  		Status         string
 300  		Expires        time.Time
 301  		Identifiers    []wireAuthzID
 302  		NotBefore      time.Time
 303  		NotAfter       time.Time
 304  		Error          *wireError
 305  		Authorizations []string
 306  		Finalize       string
 307  		Certificate    string
 308  	}
 309  	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
 310  		return nil, fmt.Errorf("acme: error reading order: %v", err)
 311  	}
 312  	o := &Order{
 313  		URI:         res.Header.Get("Location"),
 314  		Status:      v.Status,
 315  		Expires:     v.Expires,
 316  		NotBefore:   v.NotBefore,
 317  		NotAfter:    v.NotAfter,
 318  		AuthzURLs:   v.Authorizations,
 319  		FinalizeURL: v.Finalize,
 320  		CertURL:     v.Certificate,
 321  	}
 322  	for _, id := range v.Identifiers {
 323  		o.Identifiers = append(o.Identifiers, AuthzID{Type: id.Type, Value: id.Value})
 324  	}
 325  	if v.Error != nil {
 326  		o.Error = v.Error.error(nil /* headers */)
 327  	}
 328  	return o, nil
 329  }
 330  
 331  // CreateOrderCert submits the CSR (Certificate Signing Request) to a CA at the specified URL.
 332  // The URL is the FinalizeURL field of an Order created with AuthorizeOrder.
 333  //
 334  // If the bundle argument is true, the returned value also contain the CA (issuer)
 335  // certificate chain. Otherwise, only a leaf certificate is returned.
 336  // The returned URL can be used to re-fetch the certificate using FetchCert.
 337  //
 338  // This method is only supported by CAs implementing RFC 8555. See CreateCert for pre-RFC CAs.
 339  //
 340  // CreateOrderCert returns an error if the CA's response is unreasonably large.
 341  // Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
 342  func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
 343  	if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
 344  		return nil, "", err
 345  	}
 346  
 347  	// RFC describes this as "finalize order" request.
 348  	req := struct {
 349  		CSR string `json:"csr"`
 350  	}{
 351  		CSR: base64.RawURLEncoding.EncodeToString(csr),
 352  	}
 353  	res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
 354  	if err != nil {
 355  		return nil, "", err
 356  	}
 357  	defer res.Body.Close()
 358  	o, err := responseOrder(res)
 359  	if err != nil {
 360  		return nil, "", err
 361  	}
 362  
 363  	// Wait for CA to issue the cert if they haven't.
 364  	if o.Status != StatusValid {
 365  		o, err = c.WaitOrder(ctx, o.URI)
 366  	}
 367  	if err != nil {
 368  		return nil, "", err
 369  	}
 370  	// The only acceptable status post finalize and WaitOrder is "valid".
 371  	if o.Status != StatusValid {
 372  		return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
 373  	}
 374  	crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
 375  	return crt, o.CertURL, err
 376  }
 377  
 378  // fetchCertRFC downloads issued certificate from the given URL.
 379  // It expects the CA to respond with PEM-encoded certificate chain.
 380  //
 381  // The URL argument is the CertURL field of Order.
 382  func (c *Client) fetchCertRFC(ctx context.Context, url string, bundle bool) ([][]byte, error) {
 383  	res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
 384  	if err != nil {
 385  		return nil, err
 386  	}
 387  	defer res.Body.Close()
 388  
 389  	// Get all the bytes up to a sane maximum.
 390  	// Account very roughly for base64 overhead.
 391  	const max = maxCertChainSize + maxCertChainSize/33
 392  	b, err := io.ReadAll(io.LimitReader(res.Body, max+1))
 393  	if err != nil {
 394  		return nil, fmt.Errorf("acme: fetch cert response stream: %v", err)
 395  	}
 396  	if len(b) > max {
 397  		return nil, errors.New("acme: certificate chain is too big")
 398  	}
 399  
 400  	// Decode PEM chain.
 401  	var chain [][]byte
 402  	for {
 403  		var p *pem.Block
 404  		p, b = pem.Decode(b)
 405  		if p == nil {
 406  			break
 407  		}
 408  		if p.Type != "CERTIFICATE" {
 409  			return nil, fmt.Errorf("acme: invalid PEM cert type %q", p.Type)
 410  		}
 411  
 412  		chain = append(chain, p.Bytes)
 413  		if !bundle {
 414  			return chain, nil
 415  		}
 416  		if len(chain) > maxChainLen {
 417  			return nil, errors.New("acme: certificate chain is too long")
 418  		}
 419  	}
 420  	if len(chain) == 0 {
 421  		return nil, errors.New("acme: certificate chain is empty")
 422  	}
 423  	return chain, nil
 424  }
 425  
 426  // sends a cert revocation request in either JWK form when key is non-nil or KID form otherwise.
 427  func (c *Client) revokeCertRFC(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
 428  	req := &struct {
 429  		Cert   string `json:"certificate"`
 430  		Reason int    `json:"reason"`
 431  	}{
 432  		Cert:   base64.RawURLEncoding.EncodeToString(cert),
 433  		Reason: int(reason),
 434  	}
 435  	res, err := c.post(ctx, key, c.dir.RevokeURL, req, wantStatus(http.StatusOK))
 436  	if err != nil {
 437  		if isAlreadyRevoked(err) {
 438  			// Assume it is not an error to revoke an already revoked cert.
 439  			return nil
 440  		}
 441  		return err
 442  	}
 443  	defer res.Body.Close()
 444  	return nil
 445  }
 446  
 447  func isAlreadyRevoked(err error) bool {
 448  	e, ok := err.(*Error)
 449  	return ok && e.ProblemType == "urn:ietf:params:acme:error:alreadyRevoked"
 450  }
 451  
 452  // ListCertAlternates retrieves any alternate certificate chain URLs for the
 453  // given certificate chain URL. These alternate URLs can be passed to FetchCert
 454  // in order to retrieve the alternate certificate chains.
 455  //
 456  // If there are no alternate issuer certificate chains, a nil slice will be
 457  // returned.
 458  func (c *Client) ListCertAlternates(ctx context.Context, url string) ([]string, error) {
 459  	if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
 460  		return nil, err
 461  	}
 462  
 463  	res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
 464  	if err != nil {
 465  		return nil, err
 466  	}
 467  	defer res.Body.Close()
 468  
 469  	// We don't need the body but we need to discard it so we don't end up
 470  	// preventing keep-alive
 471  	if _, err := io.Copy(io.Discard, res.Body); err != nil {
 472  		return nil, fmt.Errorf("acme: cert alternates response stream: %v", err)
 473  	}
 474  	alts := linkHeader(res.Header, "alternate")
 475  	return alts, nil
 476  }
 477