1 package certificate
2 3 import (
4 "bytes"
5 "crypto"
6 "crypto/x509"
7 "encoding/base64"
8 "errors"
9 "fmt"
10 "io"
11 "net/http"
12 "strings"
13 "time"
14 15 "github.com/go-acme/lego/v4/acme"
16 "github.com/go-acme/lego/v4/acme/api"
17 "github.com/go-acme/lego/v4/certcrypto"
18 "github.com/go-acme/lego/v4/challenge"
19 "github.com/go-acme/lego/v4/log"
20 "github.com/go-acme/lego/v4/platform/wait"
21 "golang.org/x/crypto/ocsp"
22 "golang.org/x/net/idna"
23 )
24 25 const (
26 // DefaultOverallRequestLimit is the overall number of request per second
27 // limited on the "new-reg", "new-authz" and "new-cert" endpoints.
28 // From the documentation the limitation is 20 requests per second,
29 // but using 20 as value doesn't work but 18 do.
30 // https://letsencrypt.org/docs/rate-limits/
31 // ZeroSSL has a limit of 7.
32 // https://help.zerossl.com/hc/en-us/articles/17864245480093-Advantages-over-Using-Let-s-Encrypt#h_01HT4Z1JCJFJQFJ1M3P7S085Q9
33 DefaultOverallRequestLimit = 18
34 )
35 36 // maxBodySize is the maximum size of body that we will read.
37 const maxBodySize = 1024 * 1024
38 39 // Resource represents a CA issued certificate.
40 // PrivateKey, Certificate and IssuerCertificate are all
41 // already PEM encoded and can be directly written to disk.
42 // Certificate may be a certificate bundle,
43 // depending on the options supplied to create it.
44 type Resource struct {
45 Domain string `json:"domain"`
46 CertURL string `json:"certUrl"`
47 CertStableURL string `json:"certStableUrl"`
48 PrivateKey []byte `json:"-"`
49 Certificate []byte `json:"-"`
50 IssuerCertificate []byte `json:"-"`
51 CSR []byte `json:"-"`
52 }
53 54 // ObtainRequest The request to obtain certificate.
55 //
56 // The first domain in domains is used for the CommonName field of the certificate,
57 // all other domains are added using the Subject Alternate Names extension.
58 //
59 // A new private key is generated for every invocation of the function Obtain.
60 // If you do not want that you can supply your own private key in the privateKey parameter.
61 // If this parameter is non-nil it will be used instead of generating a new one.
62 //
63 // If `Bundle` is true, the `[]byte` contains both the issuer certificate and your issued certificate as a bundle.
64 //
65 // If `AlwaysDeactivateAuthorizations` is true, the authorizations are also relinquished if the obtain request was successful.
66 // See https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2.
67 type ObtainRequest struct {
68 Domains []string
69 PrivateKey crypto.PrivateKey
70 MustStaple bool
71 EmailAddresses []string
72 73 NotBefore time.Time
74 NotAfter time.Time
75 Bundle bool
76 PreferredChain string
77 78 // A string uniquely identifying the profile
79 // which will be used to affect issuance of the certificate requested by this Order.
80 // - https://www.ietf.org/id/draft-ietf-acme-profiles-00.html#section-4
81 Profile string
82 83 AlwaysDeactivateAuthorizations bool
84 85 // A string uniquely identifying a previously-issued certificate which this
86 // order is intended to replace.
87 // - https://www.rfc-editor.org/rfc/rfc9773.html#section-5
88 ReplacesCertID string
89 }
90 91 // ObtainForCSRRequest The request to obtain a certificate matching the CSR passed into it.
92 //
93 // If `Bundle` is true, the `[]byte` contains both the issuer certificate and your issued certificate as a bundle.
94 //
95 // If `AlwaysDeactivateAuthorizations` is true, the authorizations are also relinquished if the obtain request was successful.
96 // See https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2.
97 type ObtainForCSRRequest struct {
98 CSR *x509.CertificateRequest
99 100 PrivateKey crypto.PrivateKey
101 102 NotBefore time.Time
103 NotAfter time.Time
104 Bundle bool
105 PreferredChain string
106 107 // A string uniquely identifying the profile
108 // which will be used to affect issuance of the certificate requested by this Order.
109 // - https://www.ietf.org/id/draft-ietf-acme-profiles-00.html#section-4
110 Profile string
111 112 AlwaysDeactivateAuthorizations bool
113 114 // A string uniquely identifying a previously-issued certificate which this
115 // order is intended to replace.
116 // - https://www.rfc-editor.org/rfc/rfc9773.html#section-5
117 ReplacesCertID string
118 }
119 120 type resolver interface {
121 Solve(authorizations []acme.Authorization) error
122 }
123 124 type CertifierOptions struct {
125 KeyType certcrypto.KeyType
126 Timeout time.Duration
127 OverallRequestLimit int
128 DisableCommonName bool
129 }
130 131 // Certifier A service to obtain/renew/revoke certificates.
132 type Certifier struct {
133 core *api.Core
134 resolver resolver
135 options CertifierOptions
136 overallRequestLimit int
137 }
138 139 // NewCertifier creates a Certifier.
140 func NewCertifier(core *api.Core, resolver resolver, options CertifierOptions) *Certifier {
141 c := &Certifier{
142 core: core,
143 resolver: resolver,
144 options: options,
145 }
146 147 c.overallRequestLimit = options.OverallRequestLimit
148 if c.overallRequestLimit <= 0 {
149 c.overallRequestLimit = DefaultOverallRequestLimit
150 }
151 152 return c
153 }
154 155 // Obtain tries to obtain a single certificate using all domains passed into it.
156 //
157 // This function will never return a partial certificate.
158 // If one domain in the list fails, the whole certificate will fail.
159 func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
160 if len(request.Domains) == 0 {
161 return nil, errors.New("no domains to obtain a certificate for")
162 }
163 164 domains := sanitizeDomain(request.Domains)
165 166 if request.Bundle {
167 log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
168 } else {
169 log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
170 }
171 172 orderOpts := &api.OrderOptions{
173 NotBefore: request.NotBefore,
174 NotAfter: request.NotAfter,
175 Profile: request.Profile,
176 ReplacesCertID: request.ReplacesCertID,
177 }
178 179 order, err := c.core.Orders.NewWithOptions(domains, orderOpts)
180 if err != nil {
181 return nil, err
182 }
183 184 authz, err := c.getAuthorizations(order)
185 if err != nil {
186 // If any challenge fails, return. Do not generate partial SAN certificates.
187 c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
188 return nil, err
189 }
190 191 err = c.resolver.Solve(authz)
192 if err != nil {
193 // If any challenge fails, return. Do not generate partial SAN certificates.
194 c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
195 return nil, err
196 }
197 198 log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
199 200 failures := newObtainError()
201 202 cert, err := c.getForOrder(domains, order, request)
203 if err != nil {
204 for _, auth := range authz {
205 failures.Add(challenge.GetTargetedDomain(auth), err)
206 }
207 }
208 209 if request.AlwaysDeactivateAuthorizations {
210 c.deactivateAuthorizations(order, true)
211 }
212 213 return cert, failures.Join()
214 }
215 216 // ObtainForCSR tries to obtain a certificate matching the CSR passed into it.
217 //
218 // The domains are inferred from the CommonName and SubjectAltNames, if any.
219 // The private key for this CSR is not required.
220 //
221 // If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
222 //
223 // This function will never return a partial certificate.
224 // If one domain in the list fails, the whole certificate will fail.
225 func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error) {
226 if request.CSR == nil {
227 return nil, errors.New("cannot obtain resource for CSR: CSR is missing")
228 }
229 230 // figure out what domains it concerns
231 // start with the common name
232 domains := certcrypto.ExtractDomainsCSR(request.CSR)
233 234 if request.Bundle {
235 log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
236 } else {
237 log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
238 }
239 240 orderOpts := &api.OrderOptions{
241 NotBefore: request.NotBefore,
242 NotAfter: request.NotAfter,
243 Profile: request.Profile,
244 ReplacesCertID: request.ReplacesCertID,
245 }
246 247 order, err := c.core.Orders.NewWithOptions(domains, orderOpts)
248 if err != nil {
249 return nil, err
250 }
251 252 authz, err := c.getAuthorizations(order)
253 if err != nil {
254 // If any challenge fails, return. Do not generate partial SAN certificates.
255 c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
256 return nil, err
257 }
258 259 err = c.resolver.Solve(authz)
260 if err != nil {
261 // If any challenge fails, return. Do not generate partial SAN certificates.
262 c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
263 return nil, err
264 }
265 266 log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
267 268 failures := newObtainError()
269 270 var privateKey []byte
271 if request.PrivateKey != nil {
272 privateKey = certcrypto.PEMEncode(request.PrivateKey)
273 }
274 275 cert, err := c.getForCSR(domains, order, request.Bundle, request.CSR.Raw, privateKey, request.PreferredChain)
276 if err != nil {
277 for _, auth := range authz {
278 failures.Add(challenge.GetTargetedDomain(auth), err)
279 }
280 }
281 282 if request.AlwaysDeactivateAuthorizations {
283 c.deactivateAuthorizations(order, true)
284 }
285 286 if cert != nil {
287 // Add the CSR to the certificate so that it can be used for renewals.
288 cert.CSR = certcrypto.PEMEncode(request.CSR)
289 }
290 291 return cert, failures.Join()
292 }
293 294 func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, request ObtainRequest) (*Resource, error) {
295 privateKey := request.PrivateKey
296 297 if privateKey == nil {
298 var err error
299 300 privateKey, err = certcrypto.GeneratePrivateKey(c.options.KeyType)
301 if err != nil {
302 return nil, err
303 }
304 }
305 306 commonName := ""
307 if len(domains[0]) <= 64 && !c.options.DisableCommonName {
308 commonName = domains[0]
309 }
310 311 // RFC8555 Section 7.4 "Applying for Certificate Issuance"
312 // https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4
313 // says:
314 // Clients SHOULD NOT make any assumptions about the sort order of
315 // "identifiers" or "authorizations" elements in the returned order
316 // object.
317 318 var san []string
319 if commonName != "" {
320 san = append(san, commonName)
321 }
322 323 for _, auth := range order.Identifiers {
324 if auth.Value != commonName {
325 san = append(san, auth.Value)
326 }
327 }
328 329 csrOptions := certcrypto.CSROptions{
330 Domain: commonName,
331 SAN: san,
332 MustStaple: request.MustStaple,
333 EmailAddresses: request.EmailAddresses,
334 }
335 336 csr, err := certcrypto.CreateCSR(privateKey, csrOptions)
337 if err != nil {
338 return nil, err
339 }
340 341 return c.getForCSR(domains, order, request.Bundle, csr, certcrypto.PEMEncode(privateKey), request.PreferredChain)
342 }
343 344 func (c *Certifier) getForCSR(domains []string, order acme.ExtendedOrder, bundle bool, csr, privateKeyPem []byte, preferredChain string) (*Resource, error) {
345 respOrder, err := c.core.Orders.UpdateForCSR(order.Finalize, csr)
346 if err != nil {
347 return nil, err
348 }
349 350 certRes := &Resource{
351 Domain: domains[0],
352 CertURL: respOrder.Certificate,
353 PrivateKey: privateKeyPem,
354 }
355 356 if respOrder.Status == acme.StatusValid {
357 // if the certificate is available right away, shortcut!
358 ok, errR := c.checkResponse(respOrder, certRes, bundle, preferredChain)
359 if errR != nil {
360 return nil, errR
361 }
362 363 if ok {
364 return certRes, nil
365 }
366 }
367 368 timeout := c.options.Timeout
369 if c.options.Timeout <= 0 {
370 timeout = 30 * time.Second
371 }
372 373 err = wait.For("certificate", timeout, timeout/60, func() (bool, error) {
374 ord, errW := c.core.Orders.Get(order.Location)
375 if errW != nil {
376 return false, errW
377 }
378 379 done, errW := c.checkResponse(ord, certRes, bundle, preferredChain)
380 if errW != nil {
381 return false, errW
382 }
383 384 return done, nil
385 })
386 387 return certRes, err
388 }
389 390 // checkResponse checks to see if the certificate is ready and a link is contained in the response.
391 //
392 // If so, loads it into certRes and returns true.
393 // If the cert is not yet ready, it returns false.
394 //
395 // The certRes input should already have the Domain (common name) field populated.
396 //
397 // If bundle is true, the certificate will be bundled with the issuer's cert.
398 func (c *Certifier) checkResponse(order acme.ExtendedOrder, certRes *Resource, bundle bool, preferredChain string) (bool, error) {
399 valid, err := checkOrderStatus(order)
400 if err != nil || !valid {
401 return valid, err
402 }
403 404 certs, err := c.core.Certificates.GetAll(order.Certificate, bundle)
405 if err != nil {
406 return false, err
407 }
408 409 // Set the default certificate
410 certRes.IssuerCertificate = certs[order.Certificate].Issuer
411 certRes.Certificate = certs[order.Certificate].Cert
412 certRes.CertURL = order.Certificate
413 certRes.CertStableURL = order.Certificate
414 415 if preferredChain == "" {
416 log.Infof("[%s] Server responded with a certificate.", certRes.Domain)
417 418 return true, nil
419 }
420 421 for link, cert := range certs {
422 ok, err := hasPreferredChain(cert.Issuer, preferredChain)
423 if err != nil {
424 return false, err
425 }
426 427 if ok {
428 log.Infof("[%s] Server responded with a certificate for the preferred certificate chains %q.", certRes.Domain, preferredChain)
429 430 certRes.IssuerCertificate = cert.Issuer
431 certRes.Certificate = cert.Cert
432 certRes.CertURL = link
433 certRes.CertStableURL = link
434 435 return true, nil
436 }
437 }
438 439 log.Infof("lego has been configured to prefer certificate chains with issuer %q, but no chain from the CA matched this issuer. Using the default certificate chain instead.", preferredChain)
440 441 return true, nil
442 }
443 444 // Revoke takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
445 func (c *Certifier) Revoke(cert []byte) error {
446 return c.RevokeWithReason(cert, nil)
447 }
448 449 // RevokeWithReason takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
450 func (c *Certifier) RevokeWithReason(cert []byte, reason *uint) error {
451 certificates, err := certcrypto.ParsePEMBundle(cert)
452 if err != nil {
453 return err
454 }
455 456 x509Cert := certificates[0]
457 if x509Cert.IsCA {
458 return errors.New("certificate bundle starts with a CA certificate")
459 }
460 461 revokeMsg := acme.RevokeCertMessage{
462 Certificate: base64.RawURLEncoding.EncodeToString(x509Cert.Raw),
463 Reason: reason,
464 }
465 466 return c.core.Certificates.Revoke(revokeMsg)
467 }
468 469 // RenewOptions options used by Certifier.RenewWithOptions.
470 type RenewOptions struct {
471 NotBefore time.Time
472 NotAfter time.Time
473 // If true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
474 Bundle bool
475 PreferredChain string
476 477 Profile string
478 479 AlwaysDeactivateAuthorizations bool
480 // Not supported for CSR request.
481 MustStaple bool
482 EmailAddresses []string
483 }
484 485 // Renew takes a Resource and tries to renew the certificate.
486 //
487 // If the renewal process succeeds, the new certificate will be returned in a new CertResource.
488 // Please be aware that this function will return a new certificate in ANY case that is not an error.
489 // If the server does not provide us with a new cert on a GET request to the CertURL
490 // this function will start a new-cert flow where a new certificate gets generated.
491 //
492 // If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
493 //
494 // For private key reuse the PrivateKey property of the passed in Resource should be non-nil.
495 //
496 // Deprecated: use RenewWithOptions instead.
497 func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool, preferredChain string) (*Resource, error) {
498 return c.RenewWithOptions(certRes, &RenewOptions{
499 Bundle: bundle,
500 PreferredChain: preferredChain,
501 MustStaple: mustStaple,
502 })
503 }
504 505 // RenewWithOptions takes a Resource and tries to renew the certificate.
506 //
507 // If the renewal process succeeds, the new certificate will be returned in a new CertResource.
508 // Please be aware that this function will return a new certificate in ANY case that is not an error.
509 // If the server does not provide us with a new cert on a GET request to the CertURL
510 // this function will start a new-cert flow where a new certificate gets generated.
511 //
512 // If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
513 //
514 // For private key reuse the PrivateKey property of the passed in Resource should be non-nil.
515 func (c *Certifier) RenewWithOptions(certRes Resource, options *RenewOptions) (*Resource, error) {
516 // Input certificate is PEM encoded.
517 // Decode it here as we may need the decoded cert later on in the renewal process.
518 // The input may be a bundle or a single certificate.
519 certificates, err := certcrypto.ParsePEMBundle(certRes.Certificate)
520 if err != nil {
521 return nil, err
522 }
523 524 x509Cert := certificates[0]
525 if x509Cert.IsCA {
526 return nil, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", certRes.Domain)
527 }
528 529 // This is just meant to be informal for the user.
530 timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
531 log.Infof("[%s] acme: Trying renewal with %d hours remaining", certRes.Domain, int(timeLeft.Hours()))
532 533 // We always need to request a new certificate to renew.
534 // Start by checking to see if the certificate was based off a CSR,
535 // and use that if it's defined.
536 if len(certRes.CSR) > 0 {
537 csr, errP := certcrypto.PemDecodeTox509CSR(certRes.CSR)
538 if errP != nil {
539 return nil, errP
540 }
541 542 request := ObtainForCSRRequest{CSR: csr}
543 544 if options != nil {
545 request.NotBefore = options.NotBefore
546 request.NotAfter = options.NotAfter
547 request.Bundle = options.Bundle
548 request.PreferredChain = options.PreferredChain
549 request.Profile = options.Profile
550 request.AlwaysDeactivateAuthorizations = options.AlwaysDeactivateAuthorizations
551 }
552 553 return c.ObtainForCSR(request)
554 }
555 556 var privateKey crypto.PrivateKey
557 if certRes.PrivateKey != nil {
558 privateKey, err = certcrypto.ParsePEMPrivateKey(certRes.PrivateKey)
559 if err != nil {
560 return nil, err
561 }
562 }
563 564 request := ObtainRequest{
565 Domains: certcrypto.ExtractDomains(x509Cert),
566 PrivateKey: privateKey,
567 }
568 569 if options != nil {
570 request.MustStaple = options.MustStaple
571 request.NotBefore = options.NotBefore
572 request.NotAfter = options.NotAfter
573 request.Bundle = options.Bundle
574 request.PreferredChain = options.PreferredChain
575 request.EmailAddresses = options.EmailAddresses
576 request.Profile = options.Profile
577 request.AlwaysDeactivateAuthorizations = options.AlwaysDeactivateAuthorizations
578 }
579 580 return c.Obtain(request)
581 }
582 583 // GetOCSP takes a PEM encoded cert or cert bundle returning the raw OCSP response,
584 // the parsed response, and an error, if any.
585 //
586 // The returned []byte can be passed directly into the OCSPStaple property of a tls.Certificate.
587 // If the bundle only contains the issued certificate,
588 // this function will try to get the issuer certificate from the IssuingCertificateURL in the certificate.
589 //
590 // If the []byte and/or ocsp.Response return values are nil, the OCSP status may be assumed OCSPUnknown.
591 func (c *Certifier) GetOCSP(bundle []byte) ([]byte, *ocsp.Response, error) {
592 certificates, err := certcrypto.ParsePEMBundle(bundle)
593 if err != nil {
594 return nil, nil, err
595 }
596 597 // We expect the certificate slice to be ordered downwards the chain.
598 // SRV CRT -> CA. We need to pull the leaf and issuer certs out of it,
599 // which should always be the first two certificates.
600 // If there's no OCSP server listed in the leaf cert, there's nothing to do.
601 // And if we have only one certificate so far, we need to get the issuer cert.
602 603 issuedCert := certificates[0]
604 605 if len(issuedCert.OCSPServer) == 0 {
606 return nil, nil, errors.New("no OCSP server specified in cert")
607 }
608 609 if len(certificates) == 1 {
610 // TODO: build fallback. If this fails, check the remaining array entries.
611 if len(issuedCert.IssuingCertificateURL) == 0 {
612 return nil, nil, errors.New("no issuing certificate URL")
613 }
614 615 resp, errC := c.core.HTTPClient.Get(issuedCert.IssuingCertificateURL[0])
616 if errC != nil {
617 return nil, nil, errC
618 }
619 defer resp.Body.Close()
620 621 issuerBytes, errC := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
622 if errC != nil {
623 return nil, nil, errC
624 }
625 626 issuerCert, errC := x509.ParseCertificate(issuerBytes)
627 if errC != nil {
628 return nil, nil, errC
629 }
630 631 // Insert it into the slice on position 0
632 // We want it ordered right SRV CRT -> CA
633 certificates = append(certificates, issuerCert)
634 }
635 636 issuerCert := certificates[1]
637 638 // Finally kick off the OCSP request.
639 ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil)
640 if err != nil {
641 return nil, nil, err
642 }
643 644 resp, err := c.core.HTTPClient.Post(issuedCert.OCSPServer[0], "application/ocsp-request", bytes.NewReader(ocspReq))
645 if err != nil {
646 return nil, nil, err
647 }
648 defer resp.Body.Close()
649 650 ocspResBytes, err := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
651 if err != nil {
652 return nil, nil, err
653 }
654 655 ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert)
656 if err != nil {
657 return nil, nil, err
658 }
659 660 return ocspResBytes, ocspRes, nil
661 }
662 663 // Get attempts to fetch the certificate at the supplied URL.
664 // The URL is the same as what would normally be supplied at the Resource's CertURL.
665 //
666 // The returned Resource will not have the PrivateKey and CSR fields populated as these will not be available.
667 //
668 // If bundle is true, the Certificate field in the returned Resource includes the issuer certificate.
669 func (c *Certifier) Get(url string, bundle bool) (*Resource, error) {
670 cert, issuer, err := c.core.Certificates.Get(url, bundle)
671 if err != nil {
672 return nil, err
673 }
674 675 // Parse the returned cert bundle so that we can grab the domain from the common name.
676 x509Certs, err := certcrypto.ParsePEMBundle(cert)
677 if err != nil {
678 return nil, err
679 }
680 681 domain, err := certcrypto.GetCertificateMainDomain(x509Certs[0])
682 if err != nil {
683 return nil, err
684 }
685 686 return &Resource{
687 Domain: domain,
688 Certificate: cert,
689 IssuerCertificate: issuer,
690 CertURL: url,
691 CertStableURL: url,
692 }, nil
693 }
694 695 func hasPreferredChain(issuer []byte, preferredChain string) (bool, error) {
696 certs, err := certcrypto.ParsePEMBundle(issuer)
697 if err != nil {
698 return false, err
699 }
700 701 topCert := certs[len(certs)-1]
702 703 if topCert.Issuer.CommonName == preferredChain {
704 return true, nil
705 }
706 707 return false, nil
708 }
709 710 func checkOrderStatus(order acme.ExtendedOrder) (bool, error) {
711 switch order.Status {
712 case acme.StatusValid:
713 return true, nil
714 case acme.StatusInvalid:
715 return false, fmt.Errorf("invalid order: %w", order.Err())
716 default:
717 return false, nil
718 }
719 }
720 721 // https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.4
722 // The domain name MUST be encoded in the form in which it would appear in a certificate.
723 // That is, it MUST be encoded according to the rules in Section 7 of [RFC5280].
724 //
725 // https://www.rfc-editor.org/rfc/rfc5280.html#section-7
726 func sanitizeDomain(domains []string) []string {
727 var sanitizedDomains []string
728 729 for _, domain := range domains {
730 sanitizedDomain, err := idna.ToASCII(domain)
731 if err != nil {
732 log.Infof("skip domain %q: unable to sanitize (punnycode): %v", domain, err)
733 } else {
734 sanitizedDomains = append(sanitizedDomains, sanitizedDomain)
735 }
736 }
737 738 return sanitizedDomains
739 }
740