1
2
3
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
21
22
23
24
25 func (c *Client) DeactivateReg(ctx context.Context) error {
26 if _, err := c.Discover(ctx); err != nil {
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
43
44 func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
45 c.cacheMu.Lock()
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
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,
70 http.StatusCreated,
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
82
83 c.KID = KeyID(a.URI)
84 if res.StatusCode == http.StatusOK {
85 return nil, ErrAccountAlreadyExists
86 }
87 return a, nil
88 }
89
90
91
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
101
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
121
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
154
155 func (c *Client) accountKeyRollover(ctx context.Context, newKey crypto.Signer) error {
156 dir, err := c.Discover(ctx)
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
190
191
192
193
194
195
196
197
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
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
236
237
238
239
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
254
255
256
257
258
259
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
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
283
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
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 )
327 }
328 return o, nil
329 }
330
331
332
333
334
335
336
337
338
339
340
341
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 {
344 return nil, "", err
345 }
346
347
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
364 if o.Status != StatusValid {
365 o, err = c.WaitOrder(ctx, o.URI)
366 }
367 if err != nil {
368 return nil, "", err
369 }
370
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
379
380
381
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
390
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
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
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
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
453
454
455
456
457
458 func (c *Client) ListCertAlternates(ctx context.Context, url string) ([]string, error) {
459 if _, err := c.Discover(ctx); err != nil {
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
470
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 }
477
View as plain text