Browse Source

added badNonce retries

Daniel Roesler 7 years ago
parent
commit
68c565ec32
1 changed files with 11 additions and 6 deletions
  1. 11 6
      acme_tiny.py

+ 11 - 6
acme_tiny.py

@@ -1,9 +1,9 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, logging
 import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, logging
 try:
 try:
-    from urllib.request import urlopen # Python 3
+    from urllib.request import urlopen, Request # Python 3
 except ImportError:
 except ImportError:
-    from urllib2 import urlopen # Python 2
+    from urllib2 import urlopen, Request # Python 2
 
 
 DEFAULT_CA = "https://acme-v02.api.letsencrypt.org" # DEPRECATED! USE DEFAULT_DIRECTORY_URL INSTEAD
 DEFAULT_CA = "https://acme-v02.api.letsencrypt.org" # DEPRECATED! USE DEFAULT_DIRECTORY_URL INSTEAD
 DEFAULT_DIRECTORY_URL = "https://acme-v02.api.letsencrypt.org/directory"
 DEFAULT_DIRECTORY_URL = "https://acme-v02.api.letsencrypt.org/directory"
@@ -27,9 +27,9 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check
         return proc, out, err
         return proc, out, err
 
 
     # helper function - make request and automatically parse json response
     # helper function - make request and automatically parse json response
-    def _do_request(url, data=None, err_msg="Error"):
+    def _do_request(url, data=None, err_msg="Error", depth=0):
         try:
         try:
-            resp = urlopen(url, data)
+            resp = urlopen(Request(url, data=data, headers={"Content-Type": "application/jose+json"}))
             resp_data, code, headers = resp.read().decode("utf8"), resp.getcode(), resp.headers
             resp_data, code, headers = resp.read().decode("utf8"), resp.getcode(), resp.headers
             resp_data = json.loads(resp_data) # try to parse json results
             resp_data = json.loads(resp_data) # try to parse json results
         except ValueError:
         except ValueError:
@@ -37,12 +37,14 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check
         except IOError as e:
         except IOError as e:
             resp_data = e.read().decode("utf8") if hasattr(e, "read") else str(e)
             resp_data = e.read().decode("utf8") if hasattr(e, "read") else str(e)
             code, headers = getattr(e, "code", None), {}
             code, headers = getattr(e, "code", None), {}
+        if depth < 100 and code == 400 and json.loads(resp_data)['type'] == "urn:ietf:params:acme:error:badNonce":
+            raise IndexError(resp_data) # allow 100 retrys for bad nonces
         if code not in [200, 201, 204]:
         if code not in [200, 201, 204]:
             raise ValueError("{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".format(err_msg, url, data, code, resp_data))
             raise ValueError("{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".format(err_msg, url, data, code, resp_data))
         return resp_data, code, headers
         return resp_data, code, headers
 
 
     # helper function - make signed requests
     # helper function - make signed requests
-    def _send_signed_request(url, payload, err_msg):
+    def _send_signed_request(url, payload, err_msg, depth=0):
         payload64 = _b64(json.dumps(payload).encode('utf8'))
         payload64 = _b64(json.dumps(payload).encode('utf8'))
         new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce']
         new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce']
         protected = {"url": url, "alg": alg, "nonce": new_nonce}
         protected = {"url": url, "alg": alg, "nonce": new_nonce}
@@ -53,7 +55,10 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check
         if proc.returncode != 0:
         if proc.returncode != 0:
             raise IOError("OpenSSL Error: {0}".format(err))
             raise IOError("OpenSSL Error: {0}".format(err))
         data = json.dumps({"protected": protected64, "payload": payload64, "signature": _b64(out)})
         data = json.dumps({"protected": protected64, "payload": payload64, "signature": _b64(out)})
-        return _do_request(url, data=data.encode('utf8'), err_msg=err_msg)
+        try:
+            return _do_request(url, data=data.encode('utf8'), err_msg=err_msg, depth=depth)
+        except IndexError: # retry bad nonces (they raise IndexError)
+            return _send_signed_request(url, payload, err_msg, depth=(depth + 1))
 
 
     # helper function - poll until complete
     # helper function - poll until complete
     def _poll_until_not(url, pending_statuses, err_msg):
     def _poll_until_not(url, pending_statuses, err_msg):