| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- package acme
- import (
- "crypto"
- "crypto/hmac"
- "crypto/tls"
- "crypto/x509"
- "encoding/base64"
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "time"
- )
- // OptionFunc function prototype for passing options to NewClient
- type OptionFunc func(client *Client) error
- // WithHTTPTimeout sets a timeout on the http client used by the Client
- func WithHTTPTimeout(duration time.Duration) OptionFunc {
- return func(client *Client) error {
- client.httpClient.Timeout = duration
- return nil
- }
- }
- // WithInsecureSkipVerify sets InsecureSkipVerify on the http client transport tls client config used by the Client
- func WithInsecureSkipVerify() OptionFunc {
- return func(client *Client) error {
- client.httpClient.Transport = &http.Transport{
- TLSClientConfig: &tls.Config{
- InsecureSkipVerify: true,
- },
- }
- return nil
- }
- }
- // WithUserAgentSuffix appends a user agent suffix for http requests to acme resources
- func WithUserAgentSuffix(userAgentSuffix string) OptionFunc {
- return func(client *Client) error {
- client.userAgentSuffix = userAgentSuffix
- return nil
- }
- }
- // WithAcceptLanguage sets an Accept-Language header on http requests
- func WithAcceptLanguage(acceptLanguage string) OptionFunc {
- return func(client *Client) error {
- client.acceptLanguage = acceptLanguage
- return nil
- }
- }
- // WithRetryCount sets the number of times the acme client retries when receiving an api error (eg, nonce failures, etc).
- // Default: 5
- func WithRetryCount(retryCount int) OptionFunc {
- return func(client *Client) error {
- if retryCount < 1 {
- return errors.New("retryCount must be > 0")
- }
- client.retryCount = retryCount
- return nil
- }
- }
- // WithHTTPClient Allows setting a custom http client for acme connections
- func WithHTTPClient(httpClient *http.Client) OptionFunc {
- return func(client *Client) error {
- if httpClient == nil {
- return errors.New("client must not be nil")
- }
- client.httpClient = httpClient
- return nil
- }
- }
- // WithRootCerts sets the httpclient transport to use a given certpool for root certs
- func WithRootCerts(pool *x509.CertPool) OptionFunc {
- return func(client *Client) error {
- client.httpClient.Transport = &http.Transport{
- TLSClientConfig: &tls.Config{
- RootCAs: pool,
- },
- }
- return nil
- }
- }
- // NewAccountOptionFunc function prototype for passing options to NewClient
- type NewAccountOptionFunc func(crypto.Signer, *Account, *NewAccountRequest, Client) error
- // NewAcctOptOnlyReturnExisting sets the new client request to only return existing accounts
- func NewAcctOptOnlyReturnExisting() NewAccountOptionFunc {
- return func(privateKey crypto.Signer, account *Account, request *NewAccountRequest, client Client) error {
- request.OnlyReturnExisting = true
- return nil
- }
- }
- // NewAcctOptAgreeTOS sets the new account request as agreeing to the terms of service
- func NewAcctOptAgreeTOS() NewAccountOptionFunc {
- return func(privateKey crypto.Signer, account *Account, request *NewAccountRequest, client Client) error {
- request.TermsOfServiceAgreed = true
- return nil
- }
- }
- // NewAcctOptWithContacts adds contacts to a new account request
- func NewAcctOptWithContacts(contacts ...string) NewAccountOptionFunc {
- return func(privateKey crypto.Signer, account *Account, request *NewAccountRequest, client Client) error {
- request.Contact = contacts
- return nil
- }
- }
- // NewAcctOptExternalAccountBinding adds an external account binding to the new account request
- // Code adopted from jwsEncodeJSON
- func NewAcctOptExternalAccountBinding(binding ExternalAccountBinding) NewAccountOptionFunc {
- return func(privateKey crypto.Signer, account *Account, request *NewAccountRequest, client Client) error {
- if binding.KeyIdentifier == "" {
- return errors.New("acme: NewAcctOptExternalAccountBinding has no KeyIdentifier set")
- }
- if binding.MacKey == "" {
- return errors.New("acme: NewAcctOptExternalAccountBinding has no MacKey set")
- }
- if binding.Algorithm == "" {
- return errors.New("acme: NewAcctOptExternalAccountBinding has no Algorithm set")
- }
- if binding.HashFunc == 0 {
- return errors.New("acme: NewAcctOptExternalAccountBinding has no HashFunc set")
- }
- jwk, err := jwkEncode(privateKey.Public())
- if err != nil {
- return fmt.Errorf("acme: external account binding error encoding public key: %v", err)
- }
- payload := base64.RawURLEncoding.EncodeToString([]byte(jwk))
- phead := fmt.Sprintf(`{"alg":%q,"kid":%q,"url":%q}`,
- binding.Algorithm, binding.KeyIdentifier, client.Directory().NewAccount)
- phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
- decodedAccountMac, err := base64.RawURLEncoding.DecodeString(binding.MacKey)
- if err != nil {
- return fmt.Errorf("acme: external account binding error decoding mac key: %v", err)
- }
- macHash := hmac.New(binding.HashFunc.New, decodedAccountMac)
- if _, err := macHash.Write([]byte(phead + "." + payload)); err != nil {
- return err
- }
- enc := struct {
- Protected string `json:"protected"`
- Payload string `json:"payload"`
- Sig string `json:"signature"`
- }{
- Protected: phead,
- Payload: payload,
- Sig: base64.RawURLEncoding.EncodeToString(macHash.Sum(nil)),
- }
- jwsEab, err := json.Marshal(&enc)
- if err != nil {
- return fmt.Errorf("acme: external account binding error marshalling struct: %v", err)
- }
- request.ExternalAccountBinding = jwsEab
- account.ExternalAccountBinding = binding
- return nil
- }
- }
|