Guest User

Untitled

a guest
Nov 6th, 2022
126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 10.43 KB | Source Code | 0 0
  1. package certrefresh
  2.  
  3. // LetsEncrypt.org Client
  4.  
  5. import (
  6.   "bytes"
  7.   "crypto"
  8.   "crypto/rand"
  9.   "crypto/rsa"
  10.   "crypto/sha256"
  11.   "encoding/base64"
  12.   "encoding/json"
  13.   "fmt"
  14.   "io"
  15.   "log"
  16.   "math/big"
  17.   "net/http"
  18.   "sync"
  19. )
  20.  
  21. // rpcUrls maps LetsEncrypt RPC names to their URLs.
  22. type rpcUrls struct {
  23.   KeyChange string `json:"keyChange"`
  24.   NewAccount string `json:"newAccount"`
  25.   NewNonce string `json:"newNonce"`
  26.   NewOrder string `json:"newOrder"`
  27.   RenewalInfo string `json:"renewalInfo"`
  28.   RevokeCert string `json:"revokeCert"`
  29. }
  30.  
  31. type LetsEncrypt struct {
  32.   dirUrl string
  33.  
  34.   mu sync.Mutex  // protects the fields below
  35.   rpcUrls *rpcUrls
  36.   privateKey *rsa.PrivateKey
  37.   nonce string  // for use in the next LetsEncrypt RPC
  38.   kid string  // key ID in LetsEncrypt's database
  39.   challengeToken string  // for the ongoing http-01 challenge
  40.   challengeUrl string // URL to poll while waiting for LetsEncrypt
  41. }
  42.  
  43. func NewLetsEncryptClientStaging() *LetsEncrypt {
  44.   return &LetsEncrypt{
  45.     dirUrl: "https://acme-staging-v02.api.letsencrypt.org/directory",
  46.   }
  47. }
  48.  
  49. // get returns the body of the HTTP GET response.
  50. func get(url string) ([]byte, error) {
  51.   log.Printf("HTTP GET %s", url)
  52.   resp, err := http.Get(url)
  53.   if err != nil {
  54.     return nil, err
  55.   }
  56.   defer resp.Body.Close()
  57.   return io.ReadAll(resp.Body)
  58. }
  59.  
  60. // head returns the headers of the HTTP HEAD response.
  61. func head(url string) (*http.Header, error) {
  62.   req, err := http.NewRequest("HEAD", url, nil)
  63.   if err != nil {
  64.     return nil, err
  65.   }
  66.   client := &http.Client{}
  67.   log.Printf("HTTP HEAD %s", url)
  68.   resp, err := client.Do(req)
  69.   if err != nil {
  70.     return nil, err
  71.   }
  72.   err = resp.Body.Close()
  73.   if err != nil {
  74.     return nil, err
  75.   }
  76.   return &resp.Header, nil
  77. }
  78.  
  79. // fetchEndpoints gets the RPC URLs from 'dirUrl', caching it locally.
  80. func (le *LetsEncrypt) fetchEndpoints() (*rpcUrls, error) {
  81.   le.mu.Lock()
  82.   defer le.mu.Unlock()
  83.   return le.fetchEndpointsLocked()
  84. }
  85.  
  86. // fetchEndpointsLocked is fetchEndpoints when le.mu is locked.
  87. func (le *LetsEncrypt) fetchEndpointsLocked() (*rpcUrls, error) {
  88.   if le.rpcUrls != nil {
  89.     return le.rpcUrls, nil
  90.   }
  91.  
  92.   body, err := get(le.dirUrl)
  93.   if err != nil {
  94.     return nil, err
  95.   }
  96.   urls := &rpcUrls{}
  97.   err = json.Unmarshal(body, urls)
  98.   if err != nil {
  99.     return nil, err
  100.   }
  101.  
  102.   le.rpcUrls = urls
  103.   return urls, nil
  104. }
  105.  
  106. // fetchNonce gets a new nonce using an HTTP HEAD request.
  107. func (le *LetsEncrypt) fetchNonce() (string, error) {
  108.   le.mu.Lock()
  109.   defer le.mu.Unlock()
  110.  
  111.   if le.nonce == "" {
  112.     urls, err := le.fetchEndpointsLocked()
  113.     if err != nil {
  114.       return "", err
  115.     }
  116.  
  117.     headers, err := head(urls.NewNonce)
  118.     if err != nil {
  119.       return "", err
  120.     }
  121.  
  122.     le.nonce = headers.Get("Replay-Nonce")
  123.   }
  124.  
  125.   nonce := le.nonce
  126.   le.nonce = ""
  127.   return nonce, nil
  128. }
  129.  
  130. // b64 Base64-encodes the string the way LetsEncrypt likes it.
  131. func b64(s []byte) string {
  132.   return base64.RawURLEncoding.EncodeToString(s)
  133. }
  134.  
  135. // fetchPrivateKeyLocked loads or creates the private key.
  136. // Assumes that le.mu is locked.
  137. func (le *LetsEncrypt) fetchPrivateKeyLocked() (*rsa.PrivateKey, error) {
  138.   if le.privateKey != nil {
  139.     return le.privateKey, nil
  140.   }
  141.  
  142.   var err error
  143.   le.privateKey, err = rsa.GenerateKey(rand.Reader, 2048)
  144.   return le.privateKey, err
  145. }
  146.  
  147. func (le *LetsEncrypt) fetchPrivateKey() (*rsa.PrivateKey, error) {
  148.   le.mu.Lock()
  149.   defer le.mu.Unlock()
  150.   return le.fetchPrivateKeyLocked()
  151. }
  152.  
  153. type jsonWebKey struct {
  154.   Kty string `json:"kty"`
  155.   E string `json:"e"`
  156.   N string `json:"n"`
  157. }
  158.  
  159. // keyToJwk serializes the key as a JSON web key.
  160. func keyToJwk(key rsa.PublicKey) *jsonWebKey {
  161.   return &jsonWebKey {
  162.     Kty: "RSA",
  163.     E: b64(big.NewInt(int64(key.E)).Bytes()),
  164.     N: b64(key.N.Bytes()),
  165.   }
  166. }
  167.  
  168. func (le *LetsEncrypt) protectedHeader(url string) (string, error) {
  169.   nonce, err := le.fetchNonce()
  170.   if err != nil {
  171.     return "", err
  172.   }
  173.  
  174.   var metadata struct {
  175.     Alg string `json:"alg"`
  176.     Nonce string `json:"nonce"`
  177.     Url string `json:"url"`
  178.     Kid string `json:"kid,omitempty"`
  179.     Jwk *jsonWebKey `json:"jwk,omitempty"`
  180.   }
  181.  
  182.   metadata.Alg = "RS256"
  183.   metadata.Nonce = nonce
  184.   metadata.Url = url
  185.  
  186.   le.mu.Lock()
  187.   if le.kid != "" {
  188.     metadata.Kid = le.kid
  189.   } else {
  190.     privateKey, err := le.fetchPrivateKeyLocked()
  191.     if err != nil {
  192.       le.mu.Unlock()
  193.       return "", err
  194.     }
  195.     metadata.Jwk = keyToJwk(privateKey.PublicKey)
  196.   }
  197.   le.mu.Unlock()
  198.  
  199.   metaStr, err := json.Marshal(metadata)
  200.   if err != nil {
  201.     return "", err
  202.   }
  203.   return b64(metaStr), nil
  204. }
  205.  
  206. // rpc sends an HTTP POST.
  207. func (le *LetsEncrypt) rpc(url string, req, resp interface{}) (*http.Header, error) {
  208.   reqStr, err := json.Marshal(req)
  209.   if err != nil {
  210.     return nil, err
  211.   }
  212.   if req == nil {
  213.     reqStr = nil
  214.   }
  215.  
  216.   var rreq struct {
  217.     Payload string `json:"payload"`
  218.     Protected string `json:"protected"`
  219.     Signature string `json:"signature"`
  220.   }
  221.  
  222.   rreq.Payload = b64(reqStr)
  223.  
  224.   rreq.Protected, err = le.protectedHeader(url)
  225.   if err != nil {
  226.     return nil, err
  227.   }
  228.  
  229.   sig := sha256.New()
  230.   sig.Write([]byte(rreq.Protected))
  231.   sig.Write([]byte("."))
  232.   sig.Write([]byte(rreq.Payload))
  233.   privateKey, err := le.fetchPrivateKey()
  234.   if err != nil {
  235.     return nil, err
  236.   }
  237.   ssig, err := privateKey.Sign(rand.Reader, sig.Sum(nil), crypto.SHA256)
  238.   if err != nil {
  239.     return nil, err
  240.   }
  241.   rreq.Signature = b64(ssig)
  242.  
  243.   buf, err := json.Marshal(rreq)
  244.   if err != nil {
  245.     return nil, err
  246.   }
  247.  
  248.   hreq, err := http.NewRequest("POST", url, bytes.NewBuffer(buf))
  249.   if err != nil {
  250.     return nil, err
  251.   }
  252.   hreq.Header.Set("Content-Type", "application/jose+json")
  253.   hreq.Header.Set("User-Agent", "OscarKilo CertRefresh")
  254.   client := &http.Client{}
  255.   log.Printf("HTTP POST %s", url)
  256.   rresp, err := client.Do(hreq)
  257.   defer rresp.Body.Close()
  258.   body, err := io.ReadAll(rresp.Body)
  259.   if err != nil {
  260.     return &rresp.Header, err
  261.   }
  262.   log.Printf("--- response:\n%s", string(body))  // DEBUG
  263.   if rresp.StatusCode/100 != 2 {
  264.     return &rresp.Header, fmt.Errorf("StatusCode=%d, body=%s", rresp.StatusCode, string(body))
  265.   }
  266.   if resp != nil {
  267.     err = json.Unmarshal(body, resp)
  268.     if err != nil {
  269.       return &rresp.Header, err
  270.     }
  271.   }
  272.  
  273.   le.mu.Lock()
  274.   le.nonce = rresp.Header.Get("Replay-Nonce")
  275.   le.mu.Unlock()
  276.  
  277.   return &rresp.Header, nil
  278. }
  279.  
  280. func (le *LetsEncrypt) NewAccount() error {
  281.   urls, err := le.fetchEndpoints()
  282.   if err != nil {
  283.     return err
  284.   }
  285.  
  286.   var req struct {
  287.     TermsOfServiceAgreed bool `json:"termsOfServiceAgreed"`
  288.     Contact []string `json:"contact"`
  289.   }
  290.   req.TermsOfServiceAgreed = true
  291.   req.Contact = []string{"mailto:igor@shygypsy.com"}
  292.  
  293.   var resp struct {
  294.     Key jsonWebKey `json:"key"`
  295.     Contact []string `json:"contact"`
  296.     InitialIp string `json:"initialIp"`
  297.     CreatedAt string `json:"createdAt"`
  298.     Status string `json:"status"`
  299.   }
  300.  
  301.   headers, err := le.rpc(urls.NewAccount, req, &resp)
  302.   if err != nil {
  303.     return err
  304.   }
  305.   if resp.Status != "valid" {
  306.     return fmt.Errorf("status=%s", resp.Status)
  307.   }
  308.  
  309.   le.mu.Lock()
  310.   le.kid = headers.Get("Location")
  311.   le.mu.Unlock()
  312.  
  313.   return nil
  314. }
  315.  
  316. func (le *LetsEncrypt) NewOrder() error {
  317.   urls, err := le.fetchEndpoints()
  318.   if err != nil {
  319.     return err
  320.   }
  321.  
  322.   type id struct {
  323.     Type string `json:"type"`
  324.     Value string `json:"value"`
  325.   }
  326.  
  327.   var req struct {
  328.     Identifiers []id `json:"identifiers"`
  329.   }
  330.   req.Identifiers = []id{
  331.     {Type: "dns", Value: "oscarkilo.com"},
  332.   }
  333.  
  334.   var resp struct {
  335.     Status string `json:"status"`
  336.     Expires string `json:"expires"`
  337.     Identifiers []id `json:"identifiers"`
  338.     Authorizations []string `json:"authorizations"`
  339.     Finalize string `json:"finalize"`
  340.   }
  341.  
  342.   _, err = le.rpc(urls.NewOrder, req, &resp)
  343.   if err != nil {
  344.     return err
  345.   }
  346.   if resp.Status != "pending" {
  347.     return fmt.Errorf("newOrder returned status=%s", resp.Status)
  348.   }
  349.  
  350.   if len(resp.Authorizations) != 1 {
  351.     return fmt.Errorf("newOrder returned %v", resp)
  352.   }
  353.  
  354.   type challenge struct {
  355.     Type string `json:"type"`
  356.     Status string `json:"status"`
  357.     Url string `json:"url"`
  358.     Token string `json:"token"`
  359.   }
  360.  
  361.   var authResp struct {
  362.     Identifier id `json:"identifier"`
  363.     Status string `json:"status"`
  364.     Expires string `json:"expires"`
  365.     Challenges []challenge `json:"challenges"`
  366.   }
  367.  
  368.   _, err = le.rpc(resp.Authorizations[0], nil, &authResp)
  369.   if err != nil {
  370.     return err
  371.   }
  372.  
  373.   le.mu.Lock()
  374.   le.challengeToken = ""
  375.   challengeUrl := ""
  376.   for _, ch := range authResp.Challenges {
  377.     if ch.Type == "http-01" && ch.Status == "pending" {
  378.       le.challengeToken = ch.Token
  379.       le.challengeUrl = ch.Url
  380.       challengeUrl = ch.Url
  381.       break
  382.     }
  383.   }
  384.   if le.challengeToken == "" {
  385.     err = fmt.Errorf("LetsEncrypt didn't allow for an http-01 challenge")
  386.   }
  387.   le.mu.Unlock()
  388.   if err != nil {
  389.     return err
  390.   }
  391.  
  392.   var empty struct{}
  393.   _, err = le.rpc(challengeUrl, empty, nil)
  394.   return err
  395. }
  396.  
  397. // CheckChallengeStatus() returns the status of the challenge.
  398. func (le *LetsEncrypt) CheckChallengeStatus() (string, error) {
  399.   challengeUrl := le.GetChallengeUrl()
  400.   if challengeUrl == "" {
  401.     return "none", nil
  402.   }
  403.  
  404.   // DEBUG
  405.   i := len(challengeUrl) - 1
  406.   for challengeUrl[i] != '/' {
  407.     i--
  408.   }
  409.   orderUrl := challengeUrl[:i]
  410.   _, err := le.rpc(orderUrl, nil, nil)
  411.  
  412.   var resp struct {
  413.     Status string `json:"status"`
  414.   }
  415.   var empty struct{}
  416.   _, err = le.rpc(challengeUrl, empty, &resp)
  417.   if err != nil {
  418.     return "error", err
  419.   }
  420.  
  421.   return resp.Status, nil
  422. }
  423.  
  424. // GetChallengeToken returns the active http-01 challenge token, or "".
  425. func (le *LetsEncrypt) GetChallengeToken() string {
  426.   le.mu.Lock()
  427.   defer le.mu.Unlock()
  428.   return le.challengeToken
  429. }
  430.  
  431. // GetChallengeUrl returns the active http-01 challenge URL, or "".
  432. func (le *LetsEncrypt) GetChallengeUrl() string {
  433.   le.mu.Lock()
  434.   defer le.mu.Unlock()
  435.   return le.challengeUrl
  436. }
  437.  
  438. // GetJwkThumbprint returns the http-01 challenge response.
  439. func (le *LetsEncrypt) GetJwkThumbprint() (string, error) {
  440.   privateKey, err := le.fetchPrivateKey()
  441.   if err != nil {
  442.     return "", err
  443.   }
  444.  
  445.   jwk := keyToJwk(privateKey.PublicKey)
  446.   jwks, err := json.Marshal(jwk)
  447.   if err != nil {
  448.     return "", err
  449.   }
  450.  
  451.   sig := sha256.New()
  452.   sig.Write(jwks)
  453.   return b64(sig.Sum(nil)), nil
  454. }
  455.  
Add Comment
Please, Sign In to add comment