...

Source file src/golang.org/x/crypto/acme/rfc8555.go

Documentation: golang.org/x/crypto/acme

     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 retrives 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}
   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}
   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  

View as plain text