account.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. package acme
  2. import (
  3. "crypto"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "net/http"
  8. "reflect"
  9. )
  10. // NewAccount registers a new account with the acme service
  11. // Note this function is essentially deprecated and only present for backwards compatibility.
  12. // New programs should implement NewAccountOptions instead.
  13. func (c Client) NewAccount(privateKey crypto.Signer, onlyReturnExisting, termsOfServiceAgreed bool, contact ...string) (Account, error) {
  14. var opts []NewAccountOptionFunc
  15. if onlyReturnExisting {
  16. opts = append(opts, NewAcctOptOnlyReturnExisting())
  17. }
  18. if termsOfServiceAgreed {
  19. opts = append(opts, NewAcctOptAgreeTOS())
  20. }
  21. if len(contact) > 0 {
  22. opts = append(opts, NewAcctOptWithContacts(contact...))
  23. }
  24. return c.NewAccountOptions(privateKey, opts...)
  25. }
  26. // NewAccountOptions registers an account with an acme server with the provided options.
  27. func (c Client) NewAccountOptions(privateKey crypto.Signer, options ...NewAccountOptionFunc) (Account, error) {
  28. newAccountReq := NewAccountRequest{}
  29. account := Account{}
  30. for _, opt := range options {
  31. if err := opt(privateKey, &account, &newAccountReq, c); err != nil {
  32. return account, err
  33. }
  34. }
  35. resp, err := c.post(c.dir.NewAccount, "", privateKey, newAccountReq, &account, http.StatusOK, http.StatusCreated)
  36. if err != nil {
  37. return account, err
  38. }
  39. account.URL = resp.Header.Get("Location")
  40. account.PrivateKey = privateKey
  41. if account.Thumbprint == "" {
  42. account.Thumbprint, err = JWKThumbprint(account.PrivateKey.Public())
  43. if err != nil {
  44. return account, fmt.Errorf("acme: error computing account thumbprint: %v", err)
  45. }
  46. }
  47. return account, nil
  48. }
  49. // UpdateAccount updates an existing account with the acme service.
  50. func (c Client) UpdateAccount(account Account, contact ...string) (Account, error) {
  51. var updateAccountReq interface{}
  52. if !reflect.DeepEqual(account.Contact, contact) {
  53. // Only provide a non-nil updateAccountReq when there is an update to be made.
  54. updateAccountReq = struct {
  55. Contact []string `json:"contact,omitempty"`
  56. }{
  57. Contact: contact,
  58. }
  59. } else {
  60. // Otherwise use "" to trigger a POST-as-GET to fetch up-to-date account
  61. // information from the acme service.
  62. updateAccountReq = ""
  63. }
  64. _, err := c.post(account.URL, account.URL, account.PrivateKey, updateAccountReq, &account, http.StatusOK)
  65. if err != nil {
  66. return account, err
  67. }
  68. if account.Thumbprint == "" {
  69. account.Thumbprint, err = JWKThumbprint(account.PrivateKey.Public())
  70. if err != nil {
  71. return account, fmt.Errorf("acme: error computing account thumbprint: %v", err)
  72. }
  73. }
  74. return account, nil
  75. }
  76. // AccountKeyChange rolls over an account to a new key.
  77. func (c Client) AccountKeyChange(account Account, newPrivateKey crypto.Signer) (Account, error) {
  78. oldJwkKeyPub, err := jwkEncode(account.PrivateKey.Public())
  79. if err != nil {
  80. return account, fmt.Errorf("acme: error encoding new private key: %v", err)
  81. }
  82. keyChangeReq := struct {
  83. Account string `json:"account"`
  84. OldKey json.RawMessage `json:"oldKey"`
  85. }{
  86. Account: account.URL,
  87. OldKey: []byte(oldJwkKeyPub),
  88. }
  89. innerJws, err := jwsEncodeJSON(keyChangeReq, newPrivateKey, "", "", c.dir.KeyChange)
  90. if err != nil {
  91. return account, fmt.Errorf("acme: error encoding inner jws: %v", err)
  92. }
  93. if _, err := c.post(c.dir.KeyChange, account.URL, account.PrivateKey, json.RawMessage(innerJws), nil, http.StatusOK); err != nil {
  94. return account, err
  95. }
  96. account.PrivateKey = newPrivateKey
  97. return account, nil
  98. }
  99. // DeactivateAccount deactivates a given account.
  100. func (c Client) DeactivateAccount(account Account) (Account, error) {
  101. deactivateReq := struct {
  102. Status string `json:"status"`
  103. }{
  104. Status: "deactivated",
  105. }
  106. _, err := c.post(account.URL, account.URL, account.PrivateKey, deactivateReq, &account, http.StatusOK)
  107. return account, err
  108. }
  109. // FetchOrderList fetches a list of orders from the account url provided in the account Orders field
  110. func (c Client) FetchOrderList(account Account) (OrderList, error) {
  111. orderList := OrderList{}
  112. if account.Orders == "" {
  113. return orderList, errors.New("no order list for account")
  114. }
  115. _, err := c.post(account.Orders, account.URL, account.PrivateKey, "", &orderList, http.StatusOK)
  116. return orderList, err
  117. }