rfc8555.go (14354B)
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 }