certificate.go raw
1 package api
2
3 import (
4 "bytes"
5 "encoding/pem"
6 "errors"
7 "io"
8 "net/http"
9
10 "github.com/go-acme/lego/v4/acme"
11 )
12
13 // maxBodySize is the maximum size of body that we will read.
14 const maxBodySize = 1024 * 1024
15
16 type CertificateService service
17
18 // Get Returns the certificate and the issuer certificate.
19 // 'bundle' is only applied if the issuer is provided by the 'up' link.
20 func (c *CertificateService) Get(certURL string, bundle bool) ([]byte, []byte, error) {
21 cert, _, err := c.get(certURL, bundle)
22 if err != nil {
23 return nil, nil, err
24 }
25
26 return cert.Cert, cert.Issuer, nil
27 }
28
29 // GetAll the certificates and the alternate certificates.
30 // bundle' is only applied if the issuer is provided by the 'up' link.
31 func (c *CertificateService) GetAll(certURL string, bundle bool) (map[string]*acme.RawCertificate, error) {
32 cert, headers, err := c.get(certURL, bundle)
33 if err != nil {
34 return nil, err
35 }
36
37 certs := map[string]*acme.RawCertificate{certURL: cert}
38
39 // URLs of "alternate" link relation
40 // - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2
41 alts := getLinks(headers, "alternate")
42
43 for _, alt := range alts {
44 altCert, _, err := c.get(alt, bundle)
45 if err != nil {
46 return nil, err
47 }
48
49 certs[alt] = altCert
50 }
51
52 return certs, nil
53 }
54
55 // Revoke Revokes a certificate.
56 func (c *CertificateService) Revoke(req acme.RevokeCertMessage) error {
57 _, err := c.core.post(c.core.GetDirectory().RevokeCertURL, req, nil)
58 return err
59 }
60
61 // get Returns the certificate and the "up" link.
62 func (c *CertificateService) get(certURL string, bundle bool) (*acme.RawCertificate, http.Header, error) {
63 if certURL == "" {
64 return nil, nil, errors.New("certificate[get]: empty URL")
65 }
66
67 resp, err := c.core.postAsGet(certURL, nil)
68 if err != nil {
69 return nil, nil, err
70 }
71
72 data, err := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
73 if err != nil {
74 return nil, resp.Header, err
75 }
76
77 cert := c.getCertificateChain(data, bundle)
78
79 return cert, resp.Header, err
80 }
81
82 // getCertificateChain Returns the certificate and the issuer certificate.
83 func (c *CertificateService) getCertificateChain(cert []byte, bundle bool) *acme.RawCertificate {
84 // Get issuerCert from bundled response from Let's Encrypt
85 // See https://community.letsencrypt.org/t/acme-v2-no-up-link-in-response/64962
86 _, issuer := pem.Decode(cert)
87
88 // If bundle is false, we want to return a single certificate.
89 // To do this, we remove the issuer cert(s) from the issued cert.
90 if !bundle {
91 cert = bytes.TrimSuffix(cert, issuer)
92 }
93
94 return &acme.RawCertificate{Cert: cert, Issuer: issuer}
95 }
96