This commit is contained in:
2026-02-19 10:07:43 +00:00
parent 007438e372
commit 6e637ecf77
1763 changed files with 60820 additions and 279516 deletions

View File

@@ -270,10 +270,7 @@ func (c *Client) FetchRenewalInfo(ctx context.Context, leaf []byte) (*RenewalInf
return nil, fmt.Errorf("parsing leaf certificate: %w", err)
}
renewalURL, err := c.getRenewalURL(parsedLeaf)
if err != nil {
return nil, fmt.Errorf("generating renewal info URL: %w", err)
}
renewalURL := c.getRenewalURL(parsedLeaf)
res, err := c.get(ctx, renewalURL, wantStatus(http.StatusOK))
if err != nil {
@@ -288,16 +285,20 @@ func (c *Client) FetchRenewalInfo(ctx context.Context, leaf []byte) (*RenewalInf
return &info, nil
}
func (c *Client) getRenewalURL(cert *x509.Certificate) (string, error) {
func (c *Client) getRenewalURL(cert *x509.Certificate) string {
// See https://www.ietf.org/archive/id/draft-ietf-acme-ari-04.html#name-the-renewalinfo-resource
// for how the request URL is built.
url := c.dir.RenewalInfoURL
if !strings.HasSuffix(url, "/") {
url += "/"
}
return url + certRenewalIdentifier(cert)
}
func certRenewalIdentifier(cert *x509.Certificate) string {
aki := base64.RawURLEncoding.EncodeToString(cert.AuthorityKeyId)
serial := base64.RawURLEncoding.EncodeToString(cert.SerialNumber.Bytes())
return fmt.Sprintf("%s%s.%s", url, aki, serial), nil
return aki + "." + serial
}
// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service

View File

@@ -7,6 +7,7 @@ package acme
import (
"context"
"crypto"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
@@ -205,6 +206,7 @@ func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderO
Identifiers []wireAuthzID `json:"identifiers"`
NotBefore string `json:"notBefore,omitempty"`
NotAfter string `json:"notAfter,omitempty"`
Replaces string `json:"replaces,omitempty"`
}{}
for _, v := range id {
req.Identifiers = append(req.Identifiers, wireAuthzID{
@@ -218,6 +220,14 @@ func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderO
req.NotBefore = time.Time(o).Format(time.RFC3339)
case orderNotAfterOpt:
req.NotAfter = time.Time(o).Format(time.RFC3339)
case orderReplacesCert:
req.Replaces = certRenewalIdentifier(o.cert)
case orderReplacesCertDER:
cert, err := x509.ParseCertificate(o)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate being replaced: %w", err)
}
req.Replaces = certRenewalIdentifier(cert)
default:
// Package's fault if we let this happen.
panic(fmt.Sprintf("unsupported order option type %T", o))

View File

@@ -391,6 +391,30 @@ type orderNotAfterOpt time.Time
func (orderNotAfterOpt) privateOrderOpt() {}
// WithOrderReplacesCert indicates that this Order is for a replacement of an
// existing certificate.
// See https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
func WithOrderReplacesCert(cert *x509.Certificate) OrderOption {
return orderReplacesCert{cert}
}
type orderReplacesCert struct {
cert *x509.Certificate
}
func (orderReplacesCert) privateOrderOpt() {}
// WithOrderReplacesCertDER indicates that this Order is for a replacement of
// an existing DER-encoded certificate.
// See https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
func WithOrderReplacesCertDER(der []byte) OrderOption {
return orderReplacesCertDER(der)
}
type orderReplacesCertDER []byte
func (orderReplacesCertDER) privateOrderOpt() {}
// Authorization encodes an authorization response.
type Authorization struct {
// URI uniquely identifies a authorization.

View File

@@ -14,9 +14,6 @@ import (
"net/http"
"net/textproto"
"strconv"
"strings"
"golang.org/x/net/http/httpguts"
)
// ResponseRecorder is an implementation of [http.ResponseWriter] that
@@ -59,10 +56,6 @@ func NewRecorder() *ResponseRecorder {
}
}
// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
// an explicit DefaultRemoteAddr isn't set on [ResponseRecorder].
const DefaultRemoteAddr = "1.2.3.4"
// Header implements [http.ResponseWriter]. It returns the response
// headers to mutate within a handler. To test the headers that were
// written after a handler completes, use the [ResponseRecorder.Result] method and see
@@ -206,37 +199,6 @@ func (rw *ResponseRecorder) Result() *http.Response {
res.Body = http.NoBody
}
res.ContentLength = parseContentLength(res.Header.Get("Content-Length"))
if trailers, ok := rw.snapHeader["Trailer"]; ok {
res.Trailer = make(http.Header, len(trailers))
for _, k := range trailers {
for _, k := range strings.Split(k, ",") {
k = http.CanonicalHeaderKey(textproto.TrimString(k))
if !httpguts.ValidTrailerHeader(k) {
// Ignore since forbidden by RFC 7230, section 4.1.2.
continue
}
vv, ok := rw.HeaderMap[k]
if !ok {
continue
}
vv2 := make([]string, len(vv))
copy(vv2, vv)
res.Trailer[k] = vv2
}
}
}
for k, vv := range rw.HeaderMap {
if !strings.HasPrefix(k, http.TrailerPrefix) {
continue
}
if res.Trailer == nil {
res.Trailer = make(http.Header)
}
for _, v := range vv {
res.Trailer.Add(strings.TrimPrefix(k, http.TrailerPrefix), v)
}
}
return res
}