def acme_from_config_key(config, key): "Wrangle ACME client construction" # TODO: Allow for other alg types besides RS256 net = acme_client.ClientNetwork(key, verify_ssl=(not config.no_verify_ssl), user_agent=_determine_user_agent(config)) return acme_client.Client(config.server, key=key, net=net)
def lambda_handler(event, context): bucket = event['bucket'] region = event['region'] LOG.info( "Retrieving configuration file from bucket '{0}' in region '{1}' ". format(bucket, region)) connection = s3.connect_to_region(region, calling_format=OrdinaryCallingFormat()) try: bucket = connection.get_bucket(bucket) except S3ResponseError as e: print(e) LOG.error("Cannot fetch bucket : {}".format(bucket)) exit(1) conf = load_config(bucket) if conf == None: LOG.error("Cannot find file 'letslambda.yml' in S3 bucket '{0}".format( bucket)) exit(1) key = loadAccountKey(bucket, conf) acme_client = client.Client(conf['directory'], key) for domain in conf['domains']: authorization_resource = get_authorization(acme_client, domain) challenge = get_dns_challenge(authorization_resource) answer_dns_challenge(acme_client, domain, challenge) (certificate, key) = requestCertificate(acme_client, bucket, domain, authorization_resource) iam_cert = createIAMCertificate(domain, certificate, key) updateELB(domain, iam_cert)
def test_revoke_by_privkey(): client = make_client(None) domains = [random_domain()] key = OpenSSL.crypto.PKey() key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) csr_pem = acme_crypto_util.make_csr(key_pem, domains, False) order = client.new_order(csr_pem) cleanup = do_http_challenges(client, order.authorizations) try: order = client.poll_order_and_request_issuance(order) finally: cleanup() # Create a new client with the JWK as the cert private key jwk = jose.JWKRSA(key=key) net = acme_client.ClientNetwork(key, acme_version=2, user_agent="Boulder integration tester") new_client = acme_client.Client(os.getenv( 'DIRECTORY', 'http://localhost:4001/directory'), key=jwk, net=net, acme_version=2) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(jose.ComparableX509(cert), 0)
def main(): if sys.argv[1] == 'staging': directory = 'https://acme-staging.api.letsencrypt.org/directory' else: directory = 'https://acme-v01.api.letsencrypt.org/directory' key = josepy.JWKRSA(key=serialization.load_pem_private_key( sys.stdin.read(), password=None, backend=default_backend()) ) net = acme_client.ClientNetwork(key) client = acme_client.Client( directory=directory, key=key, net=net ) new_reg = messages.NewRegistration.from_data( email=sys.argv[2] ) acct = None try: regr = client.register(new_reg) except errors.ConflictError as e: acct = e.location print(json.dumps({'body': {}, 'uri': acct}))
def MakeACMEJOSEKey(): path = os.path.join(KEY_FOLDER, "private_key.json") textfile = open(path,'r') filetext = textfile.read() textfile.close() global KEY KEY = jose.JWK.json_loads(filetext) global ACME_CLIENT ACME_CLIENT = client.Client(PRODUCTION_CA+'directory', KEY)
def _revoke(rawkey, rawcert): ns = ConfigNamespace(None) acme = acme_client.Client( ns.server, key=JWKRSA(key=serialization.load_pem_private_key( rawkey, password=None, backend=default_backend()))) acme.revoke( jose.ComparableX509( OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, rawcert)))
def make_client(email=None): """Build an acme.Client and register a new account with a random key.""" key = josepy.JWKRSA(key=rsa.generate_private_key(65537, 2048, default_backend())) net = acme_client.ClientNetwork(key, user_agent="Boulder integration tester") client = acme_client.Client(DIRECTORY, key=key, net=net) account = client.register(messages.NewRegistration.from_data(email=email)) client.agree_to_tos(account) client.account = account return client
def registerNewAccount(conf, key): """ Attempt to create a new account on the ACME server with the key. No problem if it fails because this kye is already used. """ LOG.info("Registering with ACME server with the new key") newReg = messages.NewRegistration(contact=tuple(conf['info']), key=key.public_key()) acme_client = client.Client(conf['directory'], key) registration_resource = acme_client.register(newReg) LOG.info("Agreeing on the TOS on your behalf") acme_client.agree_to_tos(registration_resource)
def action_register(self): if not self.key: self._generate_key key = self._deserialize_key(self.key) acme = client.Client(DIRECTORY_URL, key) regr = acme.register() self.tos_text = regr.terms_of_service acme.agree_to_tos(regr) self.tos = True self.state = "cert" return self.tos
def revoke(args, config, unused_plugins): # TODO: coop with renewal config """Revoke a previously obtained certificate.""" if args.key_path is not None: # revocation by cert key logger.debug("Revoking %s using cert key %s", args.cert_path[0], args.key_path[0]) acme = acme_client.Client( config.server, key=jose.JWK.load(args.key_path[1])) else: # revocation by account key logger.debug("Revoking %s using Account Key", args.cert_path[0]) acc, _ = _determine_account(args, config) # pylint: disable=protected-access acme = client._acme_from_config_key(config, acc.key) acme.revoke(jose.ComparableX509(crypto_util.pyopenssl_load_certificate( args.cert_path[1])[0]))
def __init__(self, installer, config, no_confirm=False): # XXX self.acme = acme_client.Client(new_reg_uri=None, key=None, alg=None) self.installer = installer self.config = config self.no_confirm = no_confirm le_util.make_or_verify_dir(config.cert_key_backup, 0o700, os.geteuid()) # TODO: Find a better solution for this... self.list_path = os.path.join(config.cert_key_backup, "LIST") # Make sure that the file is available for use for rest of class open(self.list_path, "a").close()
def wile(obj, directory_url, staging, account_key_path, new_account_key_size, verbose): if verbose > 1: logging.basicConfig(level=logging.DEBUG) elif verbose > 0: logging.basicConfig(level=logging.INFO) else: logging.basicConfig(level=logging.WARNING) if staging: directory_url = 'https://acme-staging.api.letsencrypt.org/directory' account_key = get_or_gen_key(account_key_path, new_account_key_size) logger.debug('connecting to ACME directory at %s' % directory_url) obj['account_key'] = account_key obj['acme'] = client.Client(directory_url, account_key)
def make_client(email=None): """Build an acme.Client and register a new account with a random key.""" key = josepy.JWKRSA(key=rsa.generate_private_key(65537, 2048, default_backend())) net = acme_client.ClientNetwork(key, acme_version=2, user_agent="Boulder integration tester") client = acme_client.Client(DIRECTORY, key=key, net=net, acme_version=2) tos = client.directory.meta.terms_of_service if tos == ACCEPTABLE_TOS: net.account = client.register(messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) else: raise Exception("Unrecognized terms of service URL %s" % tos) return client
def __init__(self, configuration, test=False): if test: self.key = None else: with open(configuration.cm_key, "r") as key_file: private_key = serialization.load_pem_private_key( key_file.read(), password=None, backend=default_backend() ) self.key = jose.JWKRSA(key=private_key) user_agent = 'bigacme (https://github.com/magnuswatn/bigacme/)' network = client.ClientNetwork(self.key, user_agent=user_agent) network.session.proxies = {'https': configuration.ca_proxy} acme_client = client.Client(directory=configuration.ca, key=self.key, net=network) self.client = acme_client
def make_client(email=None): """Build an acme.Client and register a new account with a random key.""" key = jose.JWKRSA( key=rsa.generate_private_key(65537, 2048, default_backend())) net = acme_client.ClientNetwork(key, verify_ssl=False, user_agent="Boulder integration tester") client = acme_client.Client(DIRECTORY, key=key, net=net) tos = client.directory.meta.terms_of_service if tos is not None and "Do%20what%20thou%20wilt" in tos: net.account = client.register( messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) else: raise Exception("Unrecognized terms of service URL %s" % tos) return client
from acme import messages logging.basicConfig(level=logging.DEBUG) DIRECTORY_URL = 'https://acme-staging.api.letsencrypt.org/directory' BITS = 2048 # minimum for Boulder DOMAIN = 'example1.com' # example.com is ignored by Boulder # generate_private_key requires cryptography>=0.5 key = jose.JWKRSA(key=rsa.generate_private_key( public_exponent=65537, key_size=BITS, backend=default_backend())) acme = client.Client(DIRECTORY_URL, key) regr = acme.register() logging.info('Auto-accepting TOS: %s', regr.terms_of_service) acme.agree_to_tos(regr) logging.debug(regr) authzr = acme.request_challenges( identifier=messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=DOMAIN)) logging.debug(authzr) authzr, authzr_response = acme.poll(authzr) csr = OpenSSL.crypto.load_certificate_request( OpenSSL.crypto.FILETYPE_ASN1, pkg_resources.resource_string( 'acme', os.path.join('testdata', 'csr.der')))
def request_cert(domain): domain = domain.lower() print("Generating user key") user_key = josepy.JWKRSA( key=rsa.generate_private_key( public_exponent=65537, key_size=KEY_SIZE, backend=default_backend() ) ) print("Connecting to Let's Encrypt on {}".format(DIRECTORY_URL)) acme = client.Client(DIRECTORY_URL, user_key) print("Registering") regr = acme.register() print("Agreeing to ToS") acme.agree_to_tos(regr) print("Requesting challenges") authzr = acme.request_challenges( identifier=messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=domain) ) print("Looking for HTTP challenge") challenge = get_http_challenge(authzr) print("You need to set up the challenge response.") print("URL: http://{}{}".format(domain, challenge.chall.path)) print("Content: {}".format(challenge.chall.validation(user_key))) response = challenge.chall.response(user_key) while not response.simple_verify(challenge.chall, domain, user_key.public_key()): raw_input("It doesn't look like it's set up yet; press return when it is.") print("Authorizing -- here goes...") auth_response = acme.answer_challenge(challenge, challenge.chall.response(user_key)) print("Response was {}".format(auth_response)) print("Waiting for authorization to become valid") while True: print("Polling") authzr, authzr_response = acme.poll(authzr) challenge = get_http_challenge(authzr) if challenge.status.name == "valid": break print("HTTP challenge is currently {}".format(challenge)) time.sleep(1) print("Auth valid") print("Generating CSR") certificate_key = crypto.PKey() certificate_key.generate_key(crypto.TYPE_RSA, 2048) csr = crypto.X509Req() csr.get_subject().CN = domain csr.set_pubkey(certificate_key) csr.sign(certificate_key, "sha256") print("Requesting certificate") certificate_response = acme.request_issuance(josepy.util.ComparableX509(csr), [authzr]) print("Got it!") print("Fetching chain") chain = acme.fetch_chain(certificate_response) print("Done!") print("Here are the details:") print("Private key:") print(crypto.dump_privatekey(FILETYPE_PEM, certificate_key)) print("Combined cert:") print(crypto.dump_certificate(FILETYPE_PEM, certificate_response.body.wrapped)) for cert in chain: print(crypto.dump_certificate(FILETYPE_PEM, cert.wrapped))
import Crypto.PublicKey.RSA import M2Crypto from acme import client from acme import messages from acme import jose logging.basicConfig(level=logging.DEBUG) NEW_REG_URL = 'https://www.letsencrypt-demo.org/acme/new-reg' BITS = 2048 # minimum for Boulder DOMAIN = 'example1.com' # example.com is ignored by Boulder key = jose.JWKRSA.load( Crypto.PublicKey.RSA.generate(BITS).exportKey(format="PEM")) acme = client.Client(NEW_REG_URL, key) regr = acme.register(contact=()) logging.info('Auto-accepting TOS: %s', regr.terms_of_service) acme.update_registration( regr.update(body=regr.body.update(agreement=regr.terms_of_service))) logging.debug(regr) authzr = acme.request_challenges(identifier=messages.Identifier( typ=messages.IDENTIFIER_FQDN, value=DOMAIN), new_authzr_uri=regr.new_authzr_uri) logging.debug(authzr) authzr, authzr_response = acme.poll(authzr) csr = M2Crypto.X509.load_request_string(
def _acme_from_config_key(config, key): # TODO: Allow for other alg types besides RS256 return acme_client.Client(directory=config.server, key=key, verify_ssl=(not config.no_verify_ssl))
def get_client(self): client = acme_client.Client(self.directory_url, self.get_jwk_key()) if not self.is_registered: self.register_account(client) return client
def acme(self): if not self.__client: self.__client = client.Client(self.__directory_url, self.account_key) return self.__client
def run_acme_reg_to_finish(domain, regr_uri, accnt_key, site_key, csr, tmp_chall_dict, directory_url): accnt_key = jose.JWKRSA(key=accnt_key) acme = client.Client(directory_url, accnt_key) msg = messages.RegistrationResource(uri=regr_uri) regr = acme.query_registration(msg) log.info('Auto-accepting TOS: %s from: %s', regr.terms_of_service, directory_url) acme.agree_to_tos(regr) authzr = acme.request_challenges( identifier=messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=domain)) log.debug('Created auth client %s', authzr) def get_http_challenge(x, y): return y if isinstance(y.chall, challenges.HTTP01) else x challb = reduce(get_http_challenge, authzr.body.challenges, None) chall_tok = challb.chall.validation(accnt_key) v = chall_tok.split('.')[0] log.info('Exposing challenge on %s', v) tmp_chall_dict.set(v, ChallTok(chall_tok)) test_path = 'http://localhost:8082{}'.format(challb.path) local_req = Request(test_path, headers={'Host': domain}) log.debug('Testing local url path: %s', test_path) try: resp = urlopen(local_req) t = resp.read().decode('utf-8').strip() if t != chall_tok: raise ValueError except (IOError, ValueError): log.info('Resolving challenge locally failed. ACME request will fail. %s', test_path) raise cr = acme.answer_challenge(challb, challb.chall.response(accnt_key)) log.debug('Acme CA responded to challenge request with: %s', cr) try: # Wrap this step and log the failure particularly here because this is # the expected point of failure for applications that are not reachable # from the public internet. cert_res, _ = acme.poll_and_request_issuance(jose.util.ComparableX509(csr), (authzr,)) # NOTE pylint disabled due to spurious reporting. See docs: # https://letsencrypt.readthedocs.io/projects/acme/en/latest/api/jose/util.html#acme.jose.util.ComparableX509 # pylint: disable=no-member cert_str = cert_res.body._dump(FILETYPE_PEM) except messages.Error as error: log.err("Failed in request issuance step %s", error) raise chain_certs = acme.fetch_chain(cert_res) # The chain certs returned by the LE CA will always have at least one # intermediate cert. Other certificate authorities that run ACME may # behave differently, but we aren't using them. chain_str = dump_certificate(FILETYPE_PEM, chain_certs[0]) # pylint: disable=no-member expr_date = convert_asn1_date(cert_res.body.wrapped.get_notAfter()) log.info('Retrieved cert using ACME that expires on %s', expr_date) return cert_str, chain_str
def register_account_key(directory_url, accnt_key): accnt_key = jose.JWKRSA(key=accnt_key) acme = client.Client(directory_url, accnt_key) regr = acme.register() return regr.uri, regr.terms_of_service
def _update(self): if not self.dom_key or not self.dom_csr: self.state = 'cert' return if not self.key: self.state = 'priv_key' return k = self._deserialize_key(self.key) acme = client.Client(DIRECTORY_URL, k) authzr = acme.request_challenges(identifier=messages.Identifier( typ=messages.IDENTIFIER_FQDN, value=self.name)) challb = self._supported_challb(authzr) if not challb: _logger.warning( _("Didn't find any http01 challenge. Just try again.")) _logger.warning(authzr) raise exceptions.Warning( _("Didn't find any http01 challenge. Just try again.")) response, self.challenge_validation = challb.response_and_validation(k) self.challenge_path = challb.path.split('/')[-1] challenge = Challenge() challenge.set_challenge(self.challenge_path, self.challenge_validation) _logger.info("Need to response %s on url %s", self.challenge_validation, self.challenge_path) # write data to file, because it seems that the data is not written to the database bevor # the controller requests it. So the controller loads it from the file: #challenge_file = os.path.join(get_challenge_dir(), self.challenge_path) #f = open(challenge_file, 'w') #f.write(self.challenge_validation) #_logger.info("saved %s to '%s'", self.challenge_validation, challenge_file) #self.dom_verified = response.simple_verify( # challb.chall, self.name, acme.key.public_key()) #f.close() #if not self.dom_verified: # _logger.warning('%s was not successfully self-verified. ' # 'CA is likely to fail as well!', self.name) # raise exceptions.Warning(_("%s was not successfully self-verified. CA is likely to fail as well!"% self.name)) #else: # _logger.info('%s was successfully self-verified', self.name) # os.unlink(challenge_file) # _logger.info("unlinked %s", challenge_file) # authzr, authzr_response = acme.poll(authzr) acme.answer_challenge(challb, response) csr = OpenSSL.crypto.load_certificate_request( OpenSSL.crypto.FILETYPE_PEM, self.dom_csr) certr, authzr = acme.poll_and_request_issuance( jose.util.ComparableX509(csr), (authzr, )) self.cert = OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, certr.body) if self.cert: expire_time = time.strptime(certr.body.get_notAfter(), "%Y%m%d%H%M%SZ") self.expires = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT, expire_time) return self.cert
import josepy as jose from acme import client as acme_client from acme import errors as acme_errors from acme import messages DIRECTORY = os.getenv('DIRECTORY', 'http://localhost:4000/directory') if len(sys.argv) != 2: print("Usage: python deactivate.py private_key.pem") sys.exit(1) data = open(sys.argv[1], "r").read() key = jose.JWKRSA( key=serialization.load_pem_private_key(data, None, default_backend())) net = acme_client.ClientNetwork(key, verify_ssl=False, user_agent="acme account deactivator") client = acme_client.Client(DIRECTORY, key=key, net=net) try: # We expect this to fail and give us a Conflict response with a Location # header pointing at the account's URL. client.register() except acme_errors.ConflictError as e: location = e.location if location is None: raise "Key was not previously registered (but now is)." client.deactivate_registration(messages.RegistrationResource(uri=location))
def lambda_handler(event, context): if 'bucket' not in event: LOG.critical("No bucket name has been provided. Exiting.") exit(1) s3_bucket = event['bucket'] if 'region' not in event.keys( ) and 'AWS_DEFAULT_REGION' not in os.environ.keys(): LOG.critical("Unable to determine AWS region code. Exiting.") exit(1) else: if 'region' not in event.keys(): LOG.warning( "Using local environment to determine AWS region code.") s3_region = os.environ['AWS_DEFAULT_REGION'] LOG.warning("Local region set to '{0}'.".format(s3_region)) else: s3_region = event['region'] if 'defaultkey' not in event: LOG.warning("No default KMS key provided, defaulting to 'AES256'.") kms_key = 'AES256' else: LOG.info("Using {0} as default KMS key.".format(event['defaultkey'])) kms_key = event['defaultkey'] if 'config' not in event: letslambda_config = 'letslambda.yml' else: letslambda_config = event['config'] LOG.info( "Retrieving configuration file from bucket '{0}' in region '{1}' ". format(s3_bucket, s3_region)) s3_client = boto3.client('s3', config=Config(signature_version='s3v4', region_name=s3_region)) conf = load_config(s3_client, s3_bucket, letslambda_config) if conf == None: LOG.critical("Cannot load letslambda configuration. Exiting.") exit(1) conf['region'] = os.environ['AWS_DEFAULT_REGION'] conf['s3_client'] = s3_client conf['s3_bucket'] = s3_bucket conf['letslambda_config'] = letslambda_config conf['kms_key'] = kms_key account_key = load_letsencrypt_account_key(conf) acme_client = client.Client(conf['directory'], account_key) for domain in conf['domains']: if 'r53_zone' not in domain.keys(): LOG.error( "Missing parameter 'r53_zone' for domain '{0}'. Skipping domain." .format(domain['name'])) continue if 'kmsKeyArn' not in domain.keys(): domain['kmsKeyArn'] = conf['kms_key'] if 'reuse_key' not in domain.keys(): domain['reuse_key'] = True if 'elb_port' not in domain.keys(): domain['elb_port'] = 443 if 'elb_region' not in domain.keys(): domain['elb_region'] = conf['region'] authorization_resource = get_authorization(acme_client, domain) challenge = get_dns_challenge(authorization_resource) res = answer_dns_challenge(conf, acme_client, domain, challenge) if res is not True: LOG.error( "An error occurred while answering the DNS challenge. Skipping domain '{0}'." .format(domain['name'])) continue (chain, certificate, key) = request_certificate(conf, domain, acme_client, authorization_resource) if key == False or certificate == False: LOG.error( "An error occurred while requesting the signed certificate. Skipping domain '{0}'." .format(domain['name'])) continue save_certificates_to_s3(conf, domain, chain, certificate) iam_cert = upload_to_iam(conf, domain, chain, certificate, key) if iam_cert is not False and iam_cert['ResponseMetadata'][ 'HTTPStatusCode'] is 200 and 'elb' in domain.keys(): update_elb_server_certificate( conf, domain, iam_cert['ServerCertificateMetadata']['Arn']) else: LOG.error( "An error occurred while saving your server certificate in IAM. Skipping domain '{0}'." .format(domain['name'])) continue