forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/golang.org/x/crypto/acme/rfc8555.go

847 lines
14 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Copyright 2019 The Go Authors. All rights reserved.
2024-02-18 10:42:21 +00:00
// Use of this source code is governed by a BSD-style
2024-02-18 10:42:21 +00:00
// license that can be found in the LICENSE file.
package acme
import (
"context"
"crypto"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"time"
)
// DeactivateReg permanently disables an existing account associated with c.Key.
2024-02-18 10:42:21 +00:00
// A deactivated account can no longer request certificate issuance or access
2024-02-18 10:42:21 +00:00
// resources related to the account, such as orders or authorizations.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It only works with CAs implementing RFC 8555.
2024-02-18 10:42:21 +00:00
func (c *Client) DeactivateReg(ctx context.Context) error {
2024-02-18 10:42:21 +00:00
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
url := string(c.accountKID(ctx))
2024-02-18 10:42:21 +00:00
if url == "" {
2024-02-18 10:42:21 +00:00
return ErrNoAccount
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
req := json.RawMessage(`{"status": "deactivated"}`)
2024-02-18 10:42:21 +00:00
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
res.Body.Close()
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// registerRFC is equivalent to c.Register but for CAs implementing RFC 8555.
2024-02-18 10:42:21 +00:00
// It expects c.Discover to have already been called.
2024-02-18 10:42:21 +00:00
func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
2024-02-18 10:42:21 +00:00
c.cacheMu.Lock() // guard c.kid access
2024-02-18 10:42:21 +00:00
defer c.cacheMu.Unlock()
req := struct {
TermsAgreed bool `json:"termsOfServiceAgreed,omitempty"`
Contact []string `json:"contact,omitempty"`
2024-02-18 10:42:21 +00:00
ExternalAccountBinding *jsonWebSignature `json:"externalAccountBinding,omitempty"`
}{
2024-02-18 10:42:21 +00:00
Contact: acct.Contact,
}
2024-02-18 10:42:21 +00:00
if c.dir.Terms != "" {
2024-02-18 10:42:21 +00:00
req.TermsAgreed = prompt(c.dir.Terms)
2024-02-18 10:42:21 +00:00
}
// set 'externalAccountBinding' field if requested
2024-02-18 10:42:21 +00:00
if acct.ExternalAccountBinding != nil {
2024-02-18 10:42:21 +00:00
eabJWS, err := c.encodeExternalAccountBinding(acct.ExternalAccountBinding)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("acme: failed to encode external account binding: %v", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
req.ExternalAccountBinding = eabJWS
2024-02-18 10:42:21 +00:00
}
res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(
http.StatusOK, // account with this key already registered
2024-02-18 10:42:21 +00:00
http.StatusCreated, // new account created
2024-02-18 10:42:21 +00:00
))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
defer res.Body.Close()
2024-02-18 10:42:21 +00:00
a, err := responseAccount(res)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
// Cache Account URL even if we return an error to the caller.
2024-02-18 10:42:21 +00:00
// It is by all means a valid and usable "kid" value for future requests.
2024-02-18 10:42:21 +00:00
c.KID = KeyID(a.URI)
2024-02-18 10:42:21 +00:00
if res.StatusCode == http.StatusOK {
2024-02-18 10:42:21 +00:00
return nil, ErrAccountAlreadyExists
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return a, nil
2024-02-18 10:42:21 +00:00
}
// encodeExternalAccountBinding will encode an external account binding stanza
2024-02-18 10:42:21 +00:00
// as described in https://tools.ietf.org/html/rfc8555#section-7.3.4.
2024-02-18 10:42:21 +00:00
func (c *Client) encodeExternalAccountBinding(eab *ExternalAccountBinding) (*jsonWebSignature, error) {
2024-02-18 10:42:21 +00:00
jwk, err := jwkEncode(c.Key.Public())
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return jwsWithMAC(eab.Key, eab.KID, c.dir.RegURL, []byte(jwk))
2024-02-18 10:42:21 +00:00
}
// updateRegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555.
2024-02-18 10:42:21 +00:00
// It expects c.Discover to have already been called.
2024-02-18 10:42:21 +00:00
func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) {
2024-02-18 10:42:21 +00:00
url := string(c.accountKID(ctx))
2024-02-18 10:42:21 +00:00
if url == "" {
2024-02-18 10:42:21 +00:00
return nil, ErrNoAccount
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
req := struct {
Contact []string `json:"contact,omitempty"`
}{
2024-02-18 10:42:21 +00:00
Contact: a.Contact,
}
2024-02-18 10:42:21 +00:00
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer res.Body.Close()
2024-02-18 10:42:21 +00:00
return responseAccount(res)
2024-02-18 10:42:21 +00:00
}
// getRegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555.
2024-02-18 10:42:21 +00:00
// It expects c.Discover to have already been called.
2024-02-18 10:42:21 +00:00
func (c *Client) getRegRFC(ctx context.Context) (*Account, error) {
2024-02-18 10:42:21 +00:00
req := json.RawMessage(`{"onlyReturnExisting": true}`)
2024-02-18 10:42:21 +00:00
res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if e, ok := err.(*Error); ok && e.ProblemType == "urn:ietf:params:acme:error:accountDoesNotExist" {
2024-02-18 10:42:21 +00:00
return nil, ErrNoAccount
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
defer res.Body.Close()
2024-02-18 10:42:21 +00:00
return responseAccount(res)
2024-02-18 10:42:21 +00:00
}
func responseAccount(res *http.Response) (*Account, error) {
2024-02-18 10:42:21 +00:00
var v struct {
Status string
2024-02-18 10:42:21 +00:00
Contact []string
Orders string
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("acme: invalid account response: %v", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return &Account{
URI: res.Header.Get("Location"),
Status: v.Status,
Contact: v.Contact,
2024-02-18 10:42:21 +00:00
OrdersURL: v.Orders,
}, nil
2024-02-18 10:42:21 +00:00
}
// accountKeyRollover attempts to perform account key rollover.
2024-02-18 10:42:21 +00:00
// On success it will change client.Key to the new key.
2024-02-18 10:42:21 +00:00
func (c *Client) accountKeyRollover(ctx context.Context, newKey crypto.Signer) error {
2024-02-18 10:42:21 +00:00
dir, err := c.Discover(ctx) // Also required by c.accountKID
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
kid := c.accountKID(ctx)
2024-02-18 10:42:21 +00:00
if kid == noKeyID {
2024-02-18 10:42:21 +00:00
return ErrNoAccount
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
oldKey, err := jwkEncode(c.Key.Public())
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
payload := struct {
Account string `json:"account"`
OldKey json.RawMessage `json:"oldKey"`
2024-02-18 10:42:21 +00:00
}{
2024-02-18 10:42:21 +00:00
Account: string(kid),
OldKey: json.RawMessage(oldKey),
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
inner, err := jwsEncodeJSON(payload, newKey, noKeyID, noNonce, dir.KeyChangeURL)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
res, err := c.post(ctx, nil, dir.KeyChangeURL, base64.RawURLEncoding.EncodeToString(inner), wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer res.Body.Close()
2024-02-18 10:42:21 +00:00
c.Key = newKey
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// AuthorizeOrder initiates the order-based application for certificate issuance,
2024-02-18 10:42:21 +00:00
// as opposed to pre-authorization in Authorize.
2024-02-18 10:42:21 +00:00
// It is only supported by CAs implementing RFC 8555.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The caller then needs to fetch each authorization with GetAuthorization,
2024-02-18 10:42:21 +00:00
// identify those with StatusPending status and fulfill a challenge using Accept.
2024-02-18 10:42:21 +00:00
// Once all authorizations are satisfied, the caller will typically want to poll
2024-02-18 10:42:21 +00:00
// order status using WaitOrder until it's in StatusReady state.
2024-02-18 10:42:21 +00:00
// To finalize the order and obtain a certificate, the caller submits a CSR with CreateOrderCert.
2024-02-18 10:42:21 +00:00
func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) {
2024-02-18 10:42:21 +00:00
dir, err := c.Discover(ctx)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
req := struct {
Identifiers []wireAuthzID `json:"identifiers"`
NotBefore string `json:"notBefore,omitempty"`
NotAfter string `json:"notAfter,omitempty"`
2024-02-18 10:42:21 +00:00
}{}
2024-02-18 10:42:21 +00:00
for _, v := range id {
2024-02-18 10:42:21 +00:00
req.Identifiers = append(req.Identifiers, wireAuthzID{
Type: v.Type,
2024-02-18 10:42:21 +00:00
Value: v.Value,
})
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
for _, o := range opt {
2024-02-18 10:42:21 +00:00
switch o := o.(type) {
2024-02-18 10:42:21 +00:00
case orderNotBeforeOpt:
2024-02-18 10:42:21 +00:00
req.NotBefore = time.Time(o).Format(time.RFC3339)
2024-02-18 10:42:21 +00:00
case orderNotAfterOpt:
2024-02-18 10:42:21 +00:00
req.NotAfter = time.Time(o).Format(time.RFC3339)
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
// Package's fault if we let this happen.
2024-02-18 10:42:21 +00:00
panic(fmt.Sprintf("unsupported order option type %T", o))
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
res, err := c.post(ctx, nil, dir.OrderURL, req, wantStatus(http.StatusCreated))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer res.Body.Close()
2024-02-18 10:42:21 +00:00
return responseOrder(res)
2024-02-18 10:42:21 +00:00
}
// GetOrder retrives an order identified by the given URL.
2024-02-18 10:42:21 +00:00
// For orders created with AuthorizeOrder, the url value is Order.URI.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// If a caller needs to poll an order until its status is final,
2024-02-18 10:42:21 +00:00
// see the WaitOrder method.
2024-02-18 10:42:21 +00:00
func (c *Client) GetOrder(ctx context.Context, url string) (*Order, error) {
2024-02-18 10:42:21 +00:00
if _, err := c.Discover(ctx); err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer res.Body.Close()
2024-02-18 10:42:21 +00:00
return responseOrder(res)
2024-02-18 10:42:21 +00:00
}
// WaitOrder polls an order from the given URL until it is in one of the final states,
2024-02-18 10:42:21 +00:00
// StatusReady, StatusValid or StatusInvalid, the CA responded with a non-retryable error
2024-02-18 10:42:21 +00:00
// or the context is done.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It returns a non-nil Order only if its Status is StatusReady or StatusValid.
2024-02-18 10:42:21 +00:00
// In all other cases WaitOrder returns an error.
2024-02-18 10:42:21 +00:00
// If the Status is StatusInvalid, the returned error is of type *OrderError.
2024-02-18 10:42:21 +00:00
func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
2024-02-18 10:42:21 +00:00
if _, err := c.Discover(ctx); err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
for {
2024-02-18 10:42:21 +00:00
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
o, err := responseOrder(res)
2024-02-18 10:42:21 +00:00
res.Body.Close()
2024-02-18 10:42:21 +00:00
switch {
2024-02-18 10:42:21 +00:00
case err != nil:
2024-02-18 10:42:21 +00:00
// Skip and retry.
2024-02-18 10:42:21 +00:00
case o.Status == StatusInvalid:
2024-02-18 10:42:21 +00:00
return nil, &OrderError{OrderURL: o.URI, Status: o.Status}
2024-02-18 10:42:21 +00:00
case o.Status == StatusReady || o.Status == StatusValid:
2024-02-18 10:42:21 +00:00
return o, nil
2024-02-18 10:42:21 +00:00
}
d := retryAfter(res.Header.Get("Retry-After"))
2024-02-18 10:42:21 +00:00
if d == 0 {
2024-02-18 10:42:21 +00:00
// Default retry-after.
2024-02-18 10:42:21 +00:00
// Same reasoning as in WaitAuthorization.
2024-02-18 10:42:21 +00:00
d = time.Second
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
t := time.NewTimer(d)
2024-02-18 10:42:21 +00:00
select {
2024-02-18 10:42:21 +00:00
case <-ctx.Done():
2024-02-18 10:42:21 +00:00
t.Stop()
2024-02-18 10:42:21 +00:00
return nil, ctx.Err()
2024-02-18 10:42:21 +00:00
case <-t.C:
2024-02-18 10:42:21 +00:00
// Retry.
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
func responseOrder(res *http.Response) (*Order, error) {
2024-02-18 10:42:21 +00:00
var v struct {
Status string
Expires time.Time
Identifiers []wireAuthzID
NotBefore time.Time
NotAfter time.Time
Error *wireError
2024-02-18 10:42:21 +00:00
Authorizations []string
Finalize string
Certificate string
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("acme: error reading order: %v", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
o := &Order{
URI: res.Header.Get("Location"),
Status: v.Status,
Expires: v.Expires,
NotBefore: v.NotBefore,
NotAfter: v.NotAfter,
AuthzURLs: v.Authorizations,
2024-02-18 10:42:21 +00:00
FinalizeURL: v.Finalize,
CertURL: v.Certificate,
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
for _, id := range v.Identifiers {
2024-02-18 10:42:21 +00:00
o.Identifiers = append(o.Identifiers, AuthzID{Type: id.Type, Value: id.Value})
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if v.Error != nil {
2024-02-18 10:42:21 +00:00
o.Error = v.Error.error(nil /* headers */)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return o, nil
2024-02-18 10:42:21 +00:00
}
// CreateOrderCert submits the CSR (Certificate Signing Request) to a CA at the specified URL.
2024-02-18 10:42:21 +00:00
// The URL is the FinalizeURL field of an Order created with AuthorizeOrder.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// If the bundle argument is true, the returned value also contain the CA (issuer)
2024-02-18 10:42:21 +00:00
// certificate chain. Otherwise, only a leaf certificate is returned.
2024-02-18 10:42:21 +00:00
// The returned URL can be used to re-fetch the certificate using FetchCert.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// This method is only supported by CAs implementing RFC 8555. See CreateCert for pre-RFC CAs.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// CreateOrderCert returns an error if the CA's response is unreasonably large.
2024-02-18 10:42:21 +00:00
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
2024-02-18 10:42:21 +00:00
func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
2024-02-18 10:42:21 +00:00
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
2024-02-18 10:42:21 +00:00
return nil, "", err
2024-02-18 10:42:21 +00:00
}
// RFC describes this as "finalize order" request.
2024-02-18 10:42:21 +00:00
req := struct {
CSR string `json:"csr"`
}{
2024-02-18 10:42:21 +00:00
CSR: base64.RawURLEncoding.EncodeToString(csr),
}
2024-02-18 10:42:21 +00:00
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, "", err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer res.Body.Close()
2024-02-18 10:42:21 +00:00
o, err := responseOrder(res)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, "", err
2024-02-18 10:42:21 +00:00
}
// Wait for CA to issue the cert if they haven't.
2024-02-18 10:42:21 +00:00
if o.Status != StatusValid {
2024-02-18 10:42:21 +00:00
o, err = c.WaitOrder(ctx, o.URI)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, "", err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
// The only acceptable status post finalize and WaitOrder is "valid".
2024-02-18 10:42:21 +00:00
if o.Status != StatusValid {
2024-02-18 10:42:21 +00:00
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
2024-02-18 10:42:21 +00:00
return crt, o.CertURL, err
2024-02-18 10:42:21 +00:00
}
// fetchCertRFC downloads issued certificate from the given URL.
2024-02-18 10:42:21 +00:00
// It expects the CA to respond with PEM-encoded certificate chain.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// The URL argument is the CertURL field of Order.
2024-02-18 10:42:21 +00:00
func (c *Client) fetchCertRFC(ctx context.Context, url string, bundle bool) ([][]byte, error) {
2024-02-18 10:42:21 +00:00
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer res.Body.Close()
// Get all the bytes up to a sane maximum.
2024-02-18 10:42:21 +00:00
// Account very roughly for base64 overhead.
2024-02-18 10:42:21 +00:00
const max = maxCertChainSize + maxCertChainSize/33
2024-02-18 10:42:21 +00:00
b, err := io.ReadAll(io.LimitReader(res.Body, max+1))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("acme: fetch cert response stream: %v", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if len(b) > max {
2024-02-18 10:42:21 +00:00
return nil, errors.New("acme: certificate chain is too big")
2024-02-18 10:42:21 +00:00
}
// Decode PEM chain.
2024-02-18 10:42:21 +00:00
var chain [][]byte
2024-02-18 10:42:21 +00:00
for {
2024-02-18 10:42:21 +00:00
var p *pem.Block
2024-02-18 10:42:21 +00:00
p, b = pem.Decode(b)
2024-02-18 10:42:21 +00:00
if p == nil {
2024-02-18 10:42:21 +00:00
break
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if p.Type != "CERTIFICATE" {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("acme: invalid PEM cert type %q", p.Type)
2024-02-18 10:42:21 +00:00
}
chain = append(chain, p.Bytes)
2024-02-18 10:42:21 +00:00
if !bundle {
2024-02-18 10:42:21 +00:00
return chain, nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if len(chain) > maxChainLen {
2024-02-18 10:42:21 +00:00
return nil, errors.New("acme: certificate chain is too long")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if len(chain) == 0 {
2024-02-18 10:42:21 +00:00
return nil, errors.New("acme: certificate chain is empty")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return chain, nil
2024-02-18 10:42:21 +00:00
}
// sends a cert revocation request in either JWK form when key is non-nil or KID form otherwise.
2024-02-18 10:42:21 +00:00
func (c *Client) revokeCertRFC(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
2024-02-18 10:42:21 +00:00
req := &struct {
Cert string `json:"certificate"`
Reason int `json:"reason"`
2024-02-18 10:42:21 +00:00
}{
Cert: base64.RawURLEncoding.EncodeToString(cert),
2024-02-18 10:42:21 +00:00
Reason: int(reason),
}
2024-02-18 10:42:21 +00:00
res, err := c.post(ctx, key, c.dir.RevokeURL, req, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
if isAlreadyRevoked(err) {
2024-02-18 10:42:21 +00:00
// Assume it is not an error to revoke an already revoked cert.
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer res.Body.Close()
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
func isAlreadyRevoked(err error) bool {
2024-02-18 10:42:21 +00:00
e, ok := err.(*Error)
2024-02-18 10:42:21 +00:00
return ok && e.ProblemType == "urn:ietf:params:acme:error:alreadyRevoked"
2024-02-18 10:42:21 +00:00
}
// ListCertAlternates retrieves any alternate certificate chain URLs for the
2024-02-18 10:42:21 +00:00
// given certificate chain URL. These alternate URLs can be passed to FetchCert
2024-02-18 10:42:21 +00:00
// in order to retrieve the alternate certificate chains.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// If there are no alternate issuer certificate chains, a nil slice will be
2024-02-18 10:42:21 +00:00
// returned.
2024-02-18 10:42:21 +00:00
func (c *Client) ListCertAlternates(ctx context.Context, url string) ([]string, error) {
2024-02-18 10:42:21 +00:00
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer res.Body.Close()
// We don't need the body but we need to discard it so we don't end up
2024-02-18 10:42:21 +00:00
// preventing keep-alive
2024-02-18 10:42:21 +00:00
if _, err := io.Copy(io.Discard, res.Body); err != nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("acme: cert alternates response stream: %v", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
alts := linkHeader(res.Header, "alternate")
2024-02-18 10:42:21 +00:00
return alts, nil
2024-02-18 10:42:21 +00:00
}