diff options
Diffstat (limited to 'sign_csr.py')
| -rw-r--r-- | sign_csr.py | 235 | 
1 files changed, 135 insertions, 100 deletions
| diff --git a/sign_csr.py b/sign_csr.py index d24714a..bd840cb 100644 --- a/sign_csr.py +++ b/sign_csr.py @@ -49,22 +49,31 @@ def sign_csr(pubkey, csr):      }      sys.stderr.write("Found public key!\n".format(header)) -    #Step 2: Get the domain name to be certified +    #Step 2: Get the domain names to be certified      sys.stderr.write("Reading csr file...\n")      proc = subprocess.Popen(["openssl", "req", "-in", csr, "-noout", "-text"],          stdout=subprocess.PIPE, stderr=subprocess.PIPE)      out, err = proc.communicate()      if proc.returncode != 0:          raise IOError("Error loading {}".format(csr)) -    domain = re.search("Subject:.*? CN=([^\s,;/]+).*?", out, re.MULTILINE|re.DOTALL).groups()[0] -    sys.stderr.write("Found domain '{}'\n".format(domain)) +    domains = set([]) +    common_name = re.search("Subject:.*? CN=([^\s,;/]+)", out) +    if common_name is not None: +        domains.add(common_name) +    subject_alt_names = re.search("X509v3 Subject Alternative Name: \n +([^\n]+)\n", out, re.MULTILINE|re.DOTALL) +    if subject_alt_names is not None: +        for san in subject_alt_names.group(1).split(", "): +            if san.startswith("DNS:"): +                domains.add(san[4:]) +    sys.stderr.write("Found domains {}\n".format(", ".join(domains)))      #Step 2: Generate the payloads that need to be signed      #registration -    reg_email = "webmaster@{}".format(domain) +    reg_email = "webmaster@{}".format(min(domains, key=len))      reg_raw = json.dumps({          "contact": ["mailto:{}".format(reg_email)],          "agreement": "https://www.letsencrypt-demo.org/terms", +        #"agreement": "https://letsencrypt.org/be-good",      }, sort_keys=True, indent=4)      reg_b64 = _b64(reg_raw)      try: @@ -81,56 +90,79 @@ def sign_csr(pubkey, csr):      reg_file_sig = tempfile.NamedTemporaryFile(dir=".", prefix="register_", suffix=".sig")      reg_file_sig_name = os.path.basename(reg_file_sig.name) -    #identifier -    id_raw = json.dumps({"identifier": {"type": "dns", "value": domain}}, sort_keys=True) -    id_b64 = _b64(id_raw) -    try: -        urllib2.urlopen(nonce_req).info() -    except urllib2.HTTPError as e: -        id_nonce = json.dumps({ -            "nonce": e.hdrs.get("replay-nonce", _b64(os.urandom(16))), +    #need signature for each domain identifier and challenge +    ids = [] +    tests = [] +    for domain in domains: + +        #identifier +        id_raw = json.dumps({"identifier": {"type": "dns", "value": domain}}, sort_keys=True) +        id_b64 = _b64(id_raw) +        try: +            urllib2.urlopen(nonce_req).info() +        except urllib2.HTTPError as e: +            id_nonce = json.dumps({ +                "nonce": e.hdrs.get("replay-nonce", _b64(os.urandom(16))), +            }, sort_keys=True, indent=4) +            id_nonce64 = _b64(id_nonce) +        id_file = tempfile.NamedTemporaryFile(dir=".", prefix="domain_", suffix=".json") +        id_file.write("{}.{}".format(id_nonce64, id_b64)) +        id_file.flush() +        id_file_name = os.path.basename(id_file.name) +        id_file_sig = tempfile.NamedTemporaryFile(dir=".", prefix="domain_", suffix=".sig") +        id_file_sig_name = os.path.basename(id_file_sig.name) +        ids.append({ +            "domain": domain, +            "nonce64": id_nonce64, +            "data64": id_b64, +            "file": id_file, +            "file_name": id_file_name, +            "sig": id_file_sig, +            "sig_name": id_file_sig_name, +        }) + +        #challenge +        test_path = _b64(os.urandom(16)) +        test_raw = json.dumps({ +            "type": "simpleHttp", +            "path": test_path, +            "tls": False,          }, sort_keys=True, indent=4) -        id_nonce64 = _b64(id_nonce) -    id_file = tempfile.NamedTemporaryFile(dir=".", prefix="domain_", suffix=".json") -    id_file.write("{}.{}".format(id_nonce64, id_b64)) -    id_file.flush() -    id_file_name = os.path.basename(id_file.name) -    id_file_sig = tempfile.NamedTemporaryFile(dir=".", prefix="domain_", suffix=".sig") -    id_file_sig_name = os.path.basename(id_file_sig.name) - -    #challenge -    test_path = _b64(os.urandom(16)) -    test_raw = json.dumps({ -        "type": "simpleHttps", -        "path": test_path, -    }, sort_keys=True, indent=4) -    test_b64 = _b64(test_raw) -    try: -        urllib2.urlopen(nonce_req).info() -    except urllib2.HTTPError as e: -        test_nonce = json.dumps({ -            "nonce": e.hdrs.get("replay-nonce", _b64(os.urandom(16))), -        }, sort_keys=True, indent=4) -        test_nonce64 = _b64(test_nonce) -    test_file = tempfile.NamedTemporaryFile(dir=".", prefix="challenge_", suffix=".json") -    test_file.write("{}.{}".format(test_nonce64, test_b64)) -    test_file.flush() -    test_file_name = os.path.basename(test_file.name) -    test_file_sig = tempfile.NamedTemporaryFile(dir=".", prefix="challenge_", suffix=".sig") -    test_file_sig_name = os.path.basename(test_file_sig.name) +        test_b64 = _b64(test_raw) +        try: +            urllib2.urlopen(nonce_req).info() +        except urllib2.HTTPError as e: +            test_nonce = json.dumps({ +                "nonce": e.hdrs.get("replay-nonce", _b64(os.urandom(16))), +            }, sort_keys=True, indent=4) +            test_nonce64 = _b64(test_nonce) +        test_file = tempfile.NamedTemporaryFile(dir=".", prefix="challenge_", suffix=".json") +        test_file.write("{}.{}".format(test_nonce64, test_b64)) +        test_file.flush() +        test_file_name = os.path.basename(test_file.name) +        test_file_sig = tempfile.NamedTemporaryFile(dir=".", prefix="challenge_", suffix=".sig") +        test_file_sig_name = os.path.basename(test_file_sig.name) +        tests.append({ +            "nonce64": test_nonce64, +            "data64": test_b64, +            "file": test_file, +            "file_name": test_file_name, +            "sig": test_file_sig, +            "sig_name": test_file_sig_name, +        })      #Step 3: Ask the user to sign the payloads      sys.stderr.write("""  STEP 1: You need to sign some files (replace 'user.key' with your user private key).  openssl dgst -sha256 -sign user.key -out {} {} -openssl dgst -sha256 -sign user.key -out {} {} -openssl dgst -sha256 -sign user.key -out {} {} +{} +{}  """.format(      reg_file_sig_name, reg_file_name, -    id_file_sig_name, id_file_name, -    test_file_sig_name, test_file_name)) +    "\n".join("openssl dgst -sha256 -sign user.key -out {} {}".format(i['sig_name'], i['file_name']) for i in ids), +    "\n".join("openssl dgst -sha256 -sign user.key -out {} {}".format(i['sig_name'], i['file_name']) for i in tests)))      stdout = sys.stdout      sys.stdout = sys.stderr @@ -140,10 +172,11 @@ openssl dgst -sha256 -sign user.key -out {} {}      #Step 4: Load the signatures      reg_file_sig.seek(0)      reg_sig64 = _b64(reg_file_sig.read()) -    id_file_sig.seek(0) -    id_sig64 = _b64(id_file_sig.read()) -    test_file_sig.seek(0) -    test_sig64 = _b64(test_file_sig.read()) +    for n, i in enumerate(ids): +        i['sig'].seek(0) +        i['sig64'] = _b64(i['sig'].read()) +        tests[n]['sig'].seek(0) +        tests[n]['sig64'] = _b64(tests[n]['sig'].read())      #Step 5: Register the user      sys.stderr.write("Registering {}...\n".format(reg_email)) @@ -169,73 +202,75 @@ openssl dgst -sha256 -sign user.key -out {} {}              sys.stderr.write("\n")              raise -    #Step 6: Get simpleHttps challenge token -    sys.stderr.write("Requesting challenges...\n") -    id_data = json.dumps({ -        "header": header, -        "protected": id_nonce64, -        "payload": id_b64, -        "signature": id_sig64, -    }, sort_keys=True, indent=4) -    try: -        resp = urllib2.urlopen("{}/new-authz".format(CA), id_data) -        result = json.loads(resp.read()) -    except urllib2.HTTPError as e: -        sys.stderr.write("Error: id_data:\n") -        sys.stderr.write(id_data) -        sys.stderr.write("\n") -        sys.stderr.write(e.read()) -        sys.stderr.write("\n") -        raise -    token, uri = [[c['token'], c['uri']] for c in result['challenges'] if c['type'] == "simpleHttps"][0] +    #need to perform challenges for each domain +    csr_authz = [] +    for n, i in enumerate(ids): + +        #Step 6: Get simpleHttps challenge token +        sys.stderr.write("Requesting challenges for {}...\n".format(i['domain'])) +        id_data = json.dumps({ +            "header": header, +            "protected": i['nonce64'], +            "payload": i['data64'], +            "signature": i['sig64'], +        }, sort_keys=True, indent=4) +        try: +            resp = urllib2.urlopen("{}/new-authz".format(CA), id_data) +            result = json.loads(resp.read()) +        except urllib2.HTTPError as e: +            sys.stderr.write("Error: id_data:\n") +            sys.stderr.write(id_data) +            sys.stderr.write("\n") +            sys.stderr.write(e.read()) +            sys.stderr.write("\n") +            raise +        token, uri = [[c['token'], c['uri']] for c in result['challenges'] if c['type'] == "simpleHttp"][0] +        csr_authz.append(re.search("^([^?]+)", uri).group(1)) -    #Step 7: Ask the user to host the token on their server -    sys.stderr.write(""" -STEP 2: You need to run these two commands on {} (don't stop the python command). +        #Step 7: Ask the user to host the token on their server +        sys.stderr.write(""" +STEP {}: You need to run this command on {} (don't stop the python command until the next step). -openssl req -new -newkey rsa:2048 -days 365 -subj "/CN=a" -nodes -x509 -keyout a.key -out a.crt -sudo python -c "import BaseHTTPServer, ssl; \\ +sudo python -c "import BaseHTTPServer; \\      h = BaseHTTPServer.BaseHTTPRequestHandler; \\      h.do_GET = lambda r: r.send_response(200) or r.end_headers() or r.wfile.write('{}'); \\ -    s = BaseHTTPServer.HTTPServer(('0.0.0.0', 443), h); \\ -    s.socket = ssl.wrap_socket(s.socket, keyfile='a.key', certfile='a.crt'); \\ +    s = BaseHTTPServer.HTTPServer(('0.0.0.0', 80), h); \\      s.serve_forever()" -""".format(domain, token, token)) +""".format(n+2, i['domain'], token)) -    stdout = sys.stdout -    sys.stdout = sys.stderr -    raw_input("Press Enter when you've got the python command running on your server...".format(domain)) -    sys.stdout = stdout +        stdout = sys.stdout +        sys.stdout = sys.stderr +        raw_input("Press Enter when you've got the python command running on your server...") +        sys.stdout = stdout -    #Step 8: Let the CA know you're ready for the challenge -    sys.stderr.write("Requesting verification...\n") -    test_data = json.dumps({ -        "header": header, -        "protected": test_nonce64, -        "payload": test_b64, -        "signature": test_sig64, -    }, sort_keys=True, indent=4) -    try: -        resp = urllib2.urlopen(uri, test_data) -        test_result = json.loads(resp.read()) -    except urllib2.HTTPError as e: -        sys.stderr.write("Error: test_data:\n") -        sys.stderr.write(test_data) -        sys.stderr.write("\n") -        sys.stderr.write(e.read()) -        sys.stderr.write("\n") -        raise +        #Step 8: Let the CA know you're ready for the challenge +        sys.stderr.write("Requesting verification for {}...\n".format(i['domain'])) +        test_data = json.dumps({ +            "header": header, +            "protected": tests[n]['nonce64'], +            "payload": tests[n]['data64'], +            "signature": tests[n]['sig64'], +        }, sort_keys=True, indent=4) +        try: +            resp = urllib2.urlopen(uri, test_data) +            test_result = json.loads(resp.read()) +        except urllib2.HTTPError as e: +            sys.stderr.write("Error: test_data:\n") +            sys.stderr.write(test_data) +            sys.stderr.write("\n") +            sys.stderr.write(e.read()) +            sys.stderr.write("\n") +            raise      #Step 9: Build the certificate request payload      proc = subprocess.Popen(["openssl", "req", "-in", csr, "-outform", "DER"],          stdout=subprocess.PIPE, stderr=subprocess.PIPE)      csr_der, err = proc.communicate()      csr_der64 = _b64(csr_der) -    csr_authz = re.search("^([^?]+)", uri).groups()[0]      csr_raw = json.dumps({          "csr": csr_der64, -        "authorizations": [csr_authz], +        "authorizations": csr_authz,      }, sort_keys=True, indent=4)      csr_b64 = _b64(csr_raw)      try: @@ -254,7 +289,7 @@ sudo python -c "import BaseHTTPServer, ssl; \\      #Step 10: Ask the user to sign the certificate request      sys.stderr.write(""" -STEP 3: You need to sign one more file (replace 'user.key' with your user private key). +FINAL STEP: You need to sign one more file (replace 'user.key' with your user private key).  openssl dgst -sha256 -sign user.key -out {} {} | 
