order.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package acme
  2. import (
  3. "crypto/x509"
  4. "encoding/base64"
  5. "errors"
  6. "fmt"
  7. "net/http"
  8. "time"
  9. )
  10. type OrderExtension struct {
  11. Profile string
  12. }
  13. // NewOrder initiates a new order for a new certificate. This method does not use ACME Renewal Info.
  14. func (c Client) NewOrder(account Account, identifiers []Identifier) (Order, error) {
  15. return c.ReplacementOrder(account, nil, identifiers)
  16. }
  17. // NewOrderDomains takes a list of domain dns identifiers for a new certificate. Essentially a helper function.
  18. func (c Client) NewOrderDomains(account Account, domains ...string) (Order, error) {
  19. var identifiers []Identifier
  20. for _, d := range domains {
  21. identifiers = append(identifiers, Identifier{Type: "dns", Value: d})
  22. }
  23. return c.ReplacementOrder(account, nil, identifiers)
  24. }
  25. // NewOrderExtension takes a struct providing any extensions onto the order
  26. func (c Client) NewOrderExtension(account Account, identifiers []Identifier, ext OrderExtension) (Order, error) {
  27. return c.ReplacementOrderExtension(account, nil, identifiers, ext)
  28. }
  29. // ReplacementOrder takes an existing *x509.Certificate and initiates a new
  30. // order for a new certificate, but with the order being marked as a
  31. // replacement. Replacement orders which are valid replacements are (currently)
  32. // exempt from Let's Encrypt NewOrder rate limits, but may not be exempt from
  33. // other ACME CAs ACME Renewal Info implementations. At least one identifier
  34. // must match the list of identifiers from the parent order to be considered as
  35. // a valid replacement order.
  36. // See https://datatracker.ietf.org/doc/html/draft-ietf-acme-ari-03#section-5
  37. func (c Client) ReplacementOrder(account Account, oldCert *x509.Certificate, identifiers []Identifier) (Order, error) {
  38. return c.ReplacementOrderExtension(account, oldCert, identifiers, OrderExtension{})
  39. }
  40. // ReplacementOrderExtension takes a struct providing any extensions onto the order
  41. func (c Client) ReplacementOrderExtension(account Account, oldCert *x509.Certificate, identifiers []Identifier, ext OrderExtension) (Order, error) {
  42. // If an old cert being replaced is present and the acme directory doesn't list a RenewalInfo endpoint,
  43. // throw an error. This endpoint being present indicates support for ARI.
  44. if oldCert != nil && c.dir.RenewalInfo == "" {
  45. return Order{}, ErrRenewalInfoNotSupported
  46. }
  47. // optional fields are listed as 'omitempty' so the json encoder doesn't
  48. // include those keys if their values are not provided.
  49. newOrderReq := struct {
  50. Identifiers []Identifier `json:"identifiers"`
  51. Replaces string `json:"replaces,omitempty"`
  52. Profile string `json:"Profile,omitempty"`
  53. }{
  54. Identifiers: identifiers,
  55. }
  56. newOrderResp := Order{}
  57. if ext.Profile != "" {
  58. _, ok := c.Directory().Meta.Profiles[ext.Profile]
  59. if !ok {
  60. return Order{}, fmt.Errorf("requested Profile not advertised by directory: %v", ext.Profile)
  61. }
  62. newOrderReq.Profile = ext.Profile
  63. }
  64. // If present, add the ari cert ID from the original/old certificate
  65. if oldCert != nil {
  66. replacesCertID, err := GenerateARICertID(oldCert)
  67. if err != nil {
  68. return Order{}, fmt.Errorf("acme: error generating replacement certificate id: %v", err)
  69. }
  70. newOrderReq.Replaces = replacesCertID
  71. newOrderResp.Replaces = replacesCertID // server does not appear to set this currently?
  72. }
  73. // Submit the order
  74. resp, err := c.post(c.dir.NewOrder, account.URL, account.PrivateKey, newOrderReq, &newOrderResp, http.StatusCreated)
  75. if err != nil {
  76. return newOrderResp, err
  77. }
  78. defer resp.Body.Close()
  79. newOrderResp.URL = resp.Header.Get("Location")
  80. return newOrderResp, nil
  81. }
  82. // FetchOrder fetches an existing order given an order url.
  83. func (c Client) FetchOrder(account Account, orderURL string) (Order, error) {
  84. orderResp := Order{
  85. URL: orderURL, // boulder response doesn't seem to contain location header for this request
  86. }
  87. _, err := c.post(orderURL, account.URL, account.PrivateKey, "", &orderResp, http.StatusOK)
  88. return orderResp, err
  89. }
  90. // Helper function to determine whether an order is "finished" by its status.
  91. func checkFinalizedOrderStatus(order Order) (bool, error) {
  92. switch order.Status {
  93. case "invalid":
  94. // "invalid": The certificate will not be issued. Consider this
  95. // order process abandoned.
  96. if order.Error.Type != "" {
  97. return true, order.Error
  98. }
  99. return true, errors.New("acme: finalized order is invalid, no error provided")
  100. case "pending":
  101. // "pending": The server does not believe that the client has
  102. // fulfilled the requirements. Check the "authorizations" array for
  103. // entries that are still pending.
  104. return true, errors.New("acme: authorizations not fulfilled")
  105. case "ready":
  106. // "ready": The server agrees that the requirements have been
  107. // fulfilled, and is awaiting finalization. Submit a finalization
  108. // request.
  109. return true, errors.New("acme: unexpected 'ready' state")
  110. case "processing":
  111. // "processing": The certificate is being issued. Send a GET request
  112. // after the time given in the "Retry-After" header field of the
  113. // response, if any.
  114. return false, nil
  115. case "valid":
  116. // "valid": The server has issued the certificate and provisioned its
  117. // URL to the "certificate" field of the order. Download the
  118. // certificate.
  119. return true, nil
  120. default:
  121. return true, fmt.Errorf("acme: unknown order status: %s", order.Status)
  122. }
  123. }
  124. // FinalizeOrder indicates to the acme server that the client considers an order complete and "finalizes" it.
  125. // If the server believes the authorizations have been filled successfully, a certificate should then be available.
  126. // This function assumes that the order status is "ready".
  127. func (c Client) FinalizeOrder(account Account, order Order, csr *x509.CertificateRequest) (Order, error) {
  128. finaliseReq := struct {
  129. Csr string `json:"csr"`
  130. }{
  131. Csr: base64.RawURLEncoding.EncodeToString(csr.Raw),
  132. }
  133. resp, err := c.post(order.Finalize, account.URL, account.PrivateKey, finaliseReq, &order, http.StatusOK)
  134. if err != nil {
  135. return order, err
  136. }
  137. order.URL = resp.Header.Get("Location")
  138. updateOrder := func(resp *http.Response) (bool, error) {
  139. if finished, err := checkFinalizedOrderStatus(order); finished {
  140. return true, err
  141. }
  142. retryAfter, err := parseRetryAfter(resp.Header.Get("Retry-After"))
  143. if err != nil {
  144. return false, fmt.Errorf("acme: error parsing retry-after header: %v", err)
  145. }
  146. order.RetryAfter = retryAfter
  147. return false, nil
  148. }
  149. if finished, err := updateOrder(resp); finished || err != nil {
  150. return order, err
  151. }
  152. fetchOrder := func() (bool, error) {
  153. resp, err := c.post(order.URL, account.URL, account.PrivateKey, "", &order, http.StatusOK)
  154. if err != nil {
  155. return false, nil
  156. }
  157. return updateOrder(resp)
  158. }
  159. if !c.IgnoreRetryAfter && !order.RetryAfter.IsZero() {
  160. _, pollTimeout := c.getPollingDurations()
  161. end := time.Now().Add(pollTimeout)
  162. for {
  163. if time.Now().After(end) {
  164. return order, errors.New("acme: finalized order timeout")
  165. }
  166. diff := time.Until(order.RetryAfter)
  167. _, pollTimeout := c.getPollingDurations()
  168. if diff > pollTimeout {
  169. return order, fmt.Errorf("acme: Retry-After (%v) longer than poll timeout (%v)", diff, c.PollTimeout)
  170. }
  171. if diff > 0 {
  172. time.Sleep(diff)
  173. }
  174. if finished, err := fetchOrder(); finished || err != nil {
  175. return order, err
  176. }
  177. }
  178. }
  179. if !c.IgnoreRetryAfter {
  180. pollInterval, pollTimeout := c.getPollingDurations()
  181. end := time.Now().Add(pollTimeout)
  182. for {
  183. if time.Now().After(end) {
  184. return order, errors.New("acme: finalized order timeout")
  185. }
  186. time.Sleep(pollInterval)
  187. if finished, err := fetchOrder(); finished || err != nil {
  188. return order, err
  189. }
  190. }
  191. }
  192. return order, err
  193. }