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