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