Commit 782485db authored by Adrien Dorsaz's avatar Adrien Dorsaz

v2: acme_account_deactivate updates to Requests library

parent 6a396858
#!/usr/bin/env python3
import sys, os, argparse, subprocess, json, base64, binascii, re, copy, logging
import urllib.request
from urllib.error import HTTPError
import sys, argparse, subprocess, json, base64, binascii, re, copy, logging, requests
LOGGER = logging.getLogger("acme_account_deactivate")
LOGGER.addHandler(logging.StreamHandler())
LOGGER.setLevel(logging.INFO)
def account_deactivate(accountkeypath, acme_directory, log=LOGGER):
# helper function base64 encode as defined in acme spec
......@@ -21,12 +18,12 @@ def account_deactivate(accountkeypath, acme_directory, log=LOGGER):
raise IOError("OpenSSL Error: {0}".format(err))
return out
# helper function make signed requests
# helper function to send signed requests
def _send_signed_request(url, payload):
nonlocal jws_nonce
payload64 = _b64(json.dumps(payload).encode("utf8"))
protected = copy.deepcopy(jws_header)
protected["nonce"] = jws_nonce or webclient.open(acme_config["newNonce"]).getheader("Replay-Nonce", None)
protected["nonce"] = jws_nonce or requests.get(acme_config["newNonce"]).headers['Replay-Nonce']
protected["url"] = url
if url == acme_config["newAccount"]:
del protected["kid"]
......@@ -35,22 +32,28 @@ def account_deactivate(accountkeypath, acme_directory, log=LOGGER):
protected64 = _b64(json.dumps(protected).encode("utf8"))
signature = _openssl("dgst", ["-sha256", "-sign", accountkeypath],
"{0}.{1}".format(protected64, payload64).encode("utf8"))
data = json.dumps({
"protected": protected64, "payload": payload64,"signature": _b64(signature)
})
jws = {
"protected": protected64, "payload": payload64, "signature": _b64(signature)
}
try:
resp = webclient.open(url, data.encode("utf8"))
except HTTPError as httperror:
resp = httperror
resp = requests.post(url, json=jws, headers=joseheaders)
except requests.exceptions.RequestException as error:
resp = error.response
finally:
jws_nonce = resp.getheader("Replay-Nonce", None)
return resp.getcode(), resp.read(), resp.getheaders()
jws_nonce = resp.headers['Replay-Nonce']
if resp.text != '':
return resp.status_code, resp.json(), resp.headers
else:
return resp.status_code, json.dumps({}), resp.headers
webclient = urllib.request.build_opener();
webclient.addheaders = [('User-Agent', 'acme-dns-tiny/2.0/account_deactivate')]
log.info("Reading ACME directory.")
directory = webclient.open(acme_directory)
acme_config = json.loads(directory.read().decode("utf8"))
# main code
adtheaders = {'User-Agent': 'acme-dns-tiny/2.0'}
joseheaders = copy.deepcopy(adtheaders)
joseheaders['Content-Type'] = 'application/jose+json'
log.info("Fetch informations from the ACME directory.")
directory = requests.get(acme_directory, headers=adtheaders)
acme_config = directory.json()
log.info("Parsing account key.")
accountkey = _openssl("rsa", ["-in", accountkeypath, "-noout", "-text"])
......@@ -76,7 +79,7 @@ def account_deactivate(accountkeypath, acme_directory, log=LOGGER):
code, result, headers = _send_signed_request(acme_config["newAccount"], account_request)
if code == 200:
jws_header["kid"] = dict(headers).get("Location")
jws_header["kid"] = headers['Location']
else:
raise ValueError("Error looking or account URL: {0} {1}".format(code, result))
......@@ -91,29 +94,27 @@ def account_deactivate(accountkeypath, acme_directory, log=LOGGER):
def main(argv):
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""
This script permanently *deactivate* your account from an ACME server.
description="Tiny ACME client to deactivate ACME account",
epilog="""This script permanently *deactivates* an ACME account.
You should revoke your certificates *before* using this script,
as the server won't accept any further request with this account.
It will need to have access to your private account key, so
PLEASE READ THROUGH IT!
It will need to access the ACME private account key, so PLEASE READ THROUGH IT!
It's around 150 lines, so it won't take long.
=== Example Usage ===
Remove account.key from staging Let's Encrypt:
python3 acme_account_deactivate.py --account-key account.key --acme-directory https://acme-staging-v02.api.letsencrypt.org/directory
"""
Example: deactivate account.key from staging Let's Encrypt:
python3 acme_account_deactivate.py --account-key account.key --acme-directory https://acme-staging-v02.api.letsencrypt.org/directory"""
)
parser.add_argument("--account-key", required = True, help="path to the private account key to deactivate")
parser.add_argument("--acme-directory", required = True, help="ACME directory URL of the ACME server where to remove the key")
parser.add_argument("--account-key", required=True, help="path to the private account key to deactivate")
parser.add_argument("--acme-directory", required=True, help="ACME directory URL of the ACME server where to remove the key")
parser.add_argument("--quiet", action="store_const",
const=logging.ERROR,
help="suppress output except for errors")
args = parser.parse_args(argv)
LOGGER.setLevel(args.quiet or LOGGER.level)
account_deactivate(args.account_key, args.acme_directory)
LOGGER.setLevel(args.quiet or logging.INFO)
account_deactivate(args.account_key, args.acme_directory, log=LOGGER)
if __name__ == "__main__": # pragma: no cover
main(sys.argv[1:])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment