def _get_authorization_resource(self, client, main_domain, domains): """Get acme authorization_resource.""" domain_key = self._get_key("%s.key" % main_domain) _logger.info("Making CSR for the following domains: %s", domains) csr = acme.crypto_util.make_csr(private_key_pem=domain_key, domains=domains) return client.new_order(csr)
def request_new_certificate(hostname, accnt_key, priv_key, tmp_chall_dict, directory_url): """Runs the entire process of ACME registration and certificate request""" client = create_v2_client(directory_url, accnt_key) try: client.net.account = client.new_account( messages.NewRegistration.from_data(terms_of_service_agreed=True)) except errors.ConflictError as error: existing_reg = messages.RegistrationResource(uri=error.location) existing_reg = client.query_registration(existing_reg) client.update_registration(existing_reg) csr = crypto_util.make_csr(priv_key, [hostname], False) order = client.new_order(csr) log.info('Created a new order for the issuance of a certificate for %s', hostname) challb = select_http01_chall(order) _, chall_tok = challb.response_and_validation(client.net.key) v = challb.chall.encode("token") log.info('Exposing challenge on %s', v) tmp_chall_dict.set(v, ChallTok(chall_tok)) cr = client.answer_challenge(challb, challb.response(client.net.key)) log.debug('Acme CA responded to challenge request with: %s', cr) order = client.poll_and_finalize(order) return split_certificate_chain(order.fullchain_pem)
def perform( certificate: Certificate, storage: "StorageProtocol", acme_account_email: str, acme_directory_url: str, authenticators: typing.Sequence[AuthenticatorProtocol], ) -> None: client = setup_client( storage=storage, directory_url=acme_directory_url, account_email=acme_account_email, ) orderr = client.new_order( crypto_util.make_csr(certificate.private_key, certificate.domains)) auth_challs = select_challs(orderr, authenticators) for authenticator, challs in auth_challs: account_key = client.net.key authenticator.perform(challs, account_key) for challb, _ in challs: client.answer_challenge(challb, challb.response(account_key)) try: finalized_orderr = client.poll_and_finalize(orderr) fullchain_pem = finalized_orderr.fullchain_pem.encode("utf8") certificate.set_fullchain(fullchain_pem) storage.save_certificate(certificate) finally: for authenticator, challs in auth_challs: authenticator.cleanup(challs, account_key)
def run_acme_reg_to_finish(domain, accnt_key, priv_key, hostname, tmp_chall_dict, directory_url): """Runs the entire process of ACME registeration""" client = create_v2_client(directory_url, accnt_key) # First we need to create a registration with the email address provided # and accept the terms of service log.info("Using boulder server %s", directory_url) client.net.account = client.new_account( messages.NewRegistration.from_data( terms_of_service_agreed=True ) ) # Now we need to open an order and request our certificate # NOTE: We'll let ACME generate a CSR for our private key as there's # a lot of utility code it uses to generate the CSR in a specific # fashion. Better to use what LE provides than to roll our own as we # we doing with the v1 code # # This will also let us support multi-domain certificat requests in the # future, as well as mandate OCSP-Must-Staple if/when GL's HTTPS server # supports it csr = crypto_util.make_csr(priv_key, [hostname], False) order = client.new_order(csr) authzr = order.authorizations log.info('Created a new order for %s', hostname) # authrz is a list of Authorization resources, we need to find the # HTTP-01 challenge and use it for auth_req in authzr: # pylint: disable=not-an-iterable for chall_body in auth_req.body.challenges: if isinstance(chall_body.chall, challenges.HTTP01): challb = chall_body break if challb is None: raise Exception("HTTP01 challenge unavailable!") response, chall_tok = challb.response_and_validation(client.net.key) v = chall_body.chall.encode("token") log.info('Exposing challenge on %s', v) tmp_chall_dict.set(v, ChallTok(chall_tok)) cr = client.answer_challenge(challb, challb.response(client.net.key)) log.debug('Acme CA responded to challenge request with: %s', cr) order = client.poll_and_finalize(order) return split_certificate_chain(order.fullchain_pem)
def run_acme_reg_to_finish(domain, accnt_key, priv_key, hostname, tmp_chall_dict, directory_url): """Runs the entire process of ACME registeration""" client = create_v2_client(directory_url, accnt_key) # First we need to create a registration with the email address provided # and accept the terms of service log.info("Using boulder server %s", directory_url) client.net.account = client.new_account( messages.NewRegistration.from_data( terms_of_service_agreed=True ) ) # Now we need to open an order and request our certificate # NOTE: We'll let ACME generate a CSR for our private key as there's # a lot of utility code it uses to generate the CSR in a specific # fashion. Better to use what LE provides than to roll our own as we # we doing with the v1 code # # This will also let us support multi-domain certificat requests in the # future, as well as mandate OCSP-Must-Staple if/when GL's HTTPS server # supports it csr = crypto_util.make_csr(priv_key, [hostname], False) order = client.new_order(csr) authzr = order.authorizations log.info('Created a new order for %s', hostname) # authrz is a list of Authorization resources, we need to find the # HTTP-01 challenge and use it for auth_req in authzr: # pylint: disable=not-an-iterable for chall_body in auth_req.body.challenges: if isinstance(chall_body.chall, challenges.HTTP01): challb = chall_body break if challb is None: raise Exception("HTTP01 challenge unavailable!") _, chall_tok = challb.response_and_validation(client.net.key) v = chall_body.chall.encode("token") log.info('Exposing challenge on %s', v) tmp_chall_dict.set(v, ChallTok(chall_tok)) cr = client.answer_challenge(challb, challb.response(client.net.key)) log.debug('Acme CA responded to challenge request with: %s', cr) order = client.poll_and_finalize(order) return split_certificate_chain(order.fullchain_pem)
def getCert(client, domain): # Create domain private key and CSR pkey_pem, csr_pem = new_csr_comp(domain) # Issue certificate orderr = client.new_order(csr_pem) # Select HTTP-01 within offered challenges by the CA server challb = select_http01_chall(orderr) # The certificate is ready to be used in the variable "fullchain_pem". fullchain_pem = perform_http01(client, challb, orderr) return pkey_pem, fullchain_pem
def _cron(self): ir_config_parameter = self.env['ir.config_parameter'] domains = self._get_altnames() domain = domains[0] cert_file = os.path.join(_get_data_dir(), 'domain.crt') domains = self._cascade_domains(domains) for dom in domains: self._validate_domain(dom) if not self._should_run(cert_file, domains): return account_key = josepy.JWKRSA.load(self._get_key('account.key')) domain_key = self._get_key('domain.key') client = self._create_client(account_key) new_reg = acme.messages.NewRegistration(key=account_key.public_key(), terms_of_service_agreed=True) try: client.new_account(new_reg) _logger.info("Successfully registered.") except acme.errors.ConflictError as err: reg = acme.messages.Registration(key=account_key.public_key()) reg_res = acme.messages.RegistrationResource(body=reg, uri=err.location) client.query_registration(reg_res) _logger.info("Reusing existing account.") _logger.info('Making CSR for the following domains: %s', domains) csr = acme.crypto_util.make_csr(private_key_pem=domain_key, domains=domains) authzr = client.new_order(csr) # For each requested domain name we receive a list of challenges. # We only have to do one from each list. # HTTP challenges are the easiest, so do one of those if possible. # We can do DNS challenges too. There are other types that we don't # support. pending_responses = [] prefer_dns = (self.env["ir.config_parameter"].get_param( "letsencrypt.prefer_dns") == "True") for authorizations in authzr.authorizations: http_challenges = [ challenge for challenge in authorizations.body.challenges if challenge.chall.typ == TYPE_CHALLENGE_HTTP ] other_challenges = [ challenge for challenge in authorizations.body.challenges if challenge.chall.typ != TYPE_CHALLENGE_HTTP ] if prefer_dns: ordered_challenges = other_challenges + http_challenges else: ordered_challenges = http_challenges + other_challenges for challenge in ordered_challenges: if challenge.chall.typ == TYPE_CHALLENGE_HTTP: self._respond_challenge_http(challenge, account_key) client.answer_challenge(challenge, acme.challenges.HTTP01Response()) break elif challenge.chall.typ == TYPE_CHALLENGE_DNS: domain = authorizations.body.identifier.value token = challenge.validation(account_key) self._respond_challenge_dns(domain, token) # We delay this because we wait for each domain. # That takes less time if they've all already been changed. pending_responses.append( DNSUpdate(challenge=challenge, domain=domain, token=token)) break else: raise UserError( _('Could not respond to letsencrypt challenges.')) if pending_responses: for update in pending_responses: self._wait_for_record(update.domain, update.token) # 1 minute was not always enough during testing, even once records # were visible locally _logger.info( "All TXT records found, waiting 5 minutes more to make sure.") time.sleep(300) for update in pending_responses: client.answer_challenge(update.challenge, acme.challenges.DNSResponse()) # let them know we are done and they should check backoff = int(ir_config_parameter.get_param('letsencrypt.backoff', 3)) deadline = datetime.now() + timedelta(minutes=backoff) try: order_resource = client.poll_and_finalize(authzr, deadline) except acme.errors.ValidationError as error: _logger.error("Let's Encrypt validation failed!") for authz in error.failed_authzrs: for challenge in authz.body.challenges: _logger.error(str(challenge.error)) raise with open(cert_file, 'w') as crt: crt.write(order_resource.fullchain_pem) _logger.info('SUCCESS: Certificate saved: %s', cert_file) reload_cmd = ir_config_parameter.get_param( 'letsencrypt.reload_command', '') if reload_cmd.strip(): self._call_cmdline(reload_cmd) else: _logger.warning("No reload command defined.")
def create_or_update_cert( kv_cert_name, *domains, use_prod=False, keyvault_url='https://ponti-certs-kvjwxwal2p6n.vault.azure.net/', dns_zone_resource_group='damienpontifex.com-rg', dns_zone_name='damienpontifex.com', registration_email='*****@*****.**', dns_subscription_id="fa2cbf67-1293-4f9c-8884-a0379a9e0c64"): # Get directory if use_prod: directory_url = 'https://acme-v02.api.letsencrypt.org/directory' user_key_name = 'acme' issuance_period_months = 3 else: directory_url = 'https://acme-staging-v02.api.letsencrypt.org/directory' user_key_name = 'acme-staging' issuance_period_months = 1 credential = DefaultAzureCredential() challenge_handler = functools.partial( dns_challenge_handler, credential=credential, subscription_id=dns_subscription_id, dns_zone_resource_group=dns_zone_resource_group, dns_zone_name=dns_zone_name) cert_client = CertificateClient(vault_url=keyvault_url, credential=credential) #%% key = KeyVaultRSAKey(credential, keyvault_url, user_key_name) account_key = josepy.JWKRSA(key=key) client_network = acme.client.ClientNetwork(account_key) directory = messages.Directory.from_json( client_network.get(directory_url).json()) client = acme.client.ClientV2(directory, client_network) new_regr = acme.messages.Registration.from_data( key=account_key, email=registration_email, terms_of_service_agreed=True) # Register or fetch account try: regr = client.new_account(new_regr) logger.info('Created new account') except acme.errors.ConflictError as e: regr = acme.messages.RegistrationResource(uri=e.location, body=new_regr) regr = client.query_registration(regr) logger.info('Got existing account') cert_policy = CertificatePolicy( issuer_name='Unknown', subject_name=f'CN={domains[0]}', exportable=True, key_type=KeyType.rsa, key_size=2048, content_type=CertificateContentType.pkcs12, san_dns_names=domains[1:] if len(domains) > 1 else [], validity_in_months=issuance_period_months) try: # Check an existing certificate operation isn't in progress cert_op = cert_client.get_certificate_operation( certificate_name=kv_cert_name) logger.info('Existing cert operation in progress') except ResourceNotFoundError: cert_op = cert_client.begin_create_certificate( certificate_name=kv_cert_name, policy=cert_policy) logger.info('New cert operation') # cert_op = kvclient.create_certificate(KEYVAULT_URL, certificate_name=kv_cert_name, certificate_policy=cert_policy) cert_op_res = cert_op.result() cert_op_r = cert_client.get_certificate_operation(kv_cert_name) logger.info('Created certificate request in key vault') # Wrap with header and footer for pem to show certificate request csr_pem = "-----BEGIN CERTIFICATE REQUEST-----\n" + base64.b64encode( cert_op_r.csr).decode() + "\n-----END CERTIFICATE REQUEST-----\n" # Submit order order_resource = client.new_order(csr_pem) logger.info('Submitted order') # Challenges from order # Respond to challenges challenges_to_respond_to = list( challenge_handler(authorizations=order_resource.authorizations, account_key=account_key)) for dns_challenge in challenges_to_respond_to: # Perform challenge auth_response = client.answer_challenge( dns_challenge, dns_challenge.chall.response(account_key)) logger.info('Answered challenges') # Poll for status # Finalize order # Download certificate final_order = client.poll_and_finalize(order_resource) logger.info('Finalised order') # Strip header and footer of BEGIN/END CERTIFICATE # with open('cert.pem', 'w') as f: # f.write(final_order.fullchain_pem) certificate_vals = [ val.replace('\n', '').encode() for val in final_order.fullchain_pem.split('-----') if 'CERTIFICATE' not in val and len(val.replace('\n', '')) != 0 ] cert_client.merge_certificate(name=kv_cert_name, x509_certificates=certificate_vals) logger.info('Merged certificate back to key vault')
def run_acme_reg_to_finish(domain, accnt_key, priv_key, hostname, tmp_chall_dict, directory_url): '''Runs the entire process of ACME registeration''' client = create_v2_client(directory_url, accnt_key) # First we need to create a registration with the email address provided # and accept the terms of service log.info("Using boulder server %s", directory_url) client.net.account = client.new_account( messages.NewRegistration.from_data(terms_of_service_agreed=True)) # Now we need to open an order and request our certificate # NOTE: We'll let ACME generate a CSR for our private key as there's # a lot of utility code it uses to generate the CSR in a specific # fashion. Better to use what LE provides than to roll our own as we # we doing with the v1 code # # This will also let us support multi-domain certificat requests in the # future, as well as mandate OCSP-Must-Staple if/when GL's HTTPS server # supports it csr = crypto_util.make_csr(priv_key, [hostname], False) order = client.new_order(csr) authzr = order.authorizations log.info('Created a new order for %s', hostname) # authrz is a list of Authorization resources, we need to find the # HTTP-01 challenge and use it for auth_req in authzr: # pylint: disable=not-an-iterable for chall_body in auth_req.body.challenges: if isinstance(chall_body.chall, challenges.HTTP01): challb = chall_body break if challb is None: raise Exception("HTTP01 challenge unavailable!") response, chall_tok = challb.response_and_validation(client.net.key) v = chall_body.chall.encode("token") log.info('Exposing challenge on %s', v) tmp_chall_dict.set(v, ChallTok(chall_tok)) cr = client.answer_challenge(challb, challb.response(client.net.key)) log.debug('Acme CA responded to challenge request with: %s', cr) # 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. try: order = client.poll_and_finalize(order) except messages.Error as error: log.err("Failed in request issuance step %s", error) raise # ACME V2 returns a full chain certificate, and ACME doesn't ship with # helper functions out of the box. Fortunately, searching through cerbot # this is easily enough to do with pyOpenSSL cert = load_certificate(FILETYPE_PEM, order.fullchain_pem) cert_str = dump_certificate(FILETYPE_PEM, cert).decode() chain_str = order.fullchain_pem[len(cert_str):].lstrip() # pylint: disable=no-member expr_date = convert_asn1_date(cert.get_notAfter()) log.info('Retrieved cert using ACME that expires on %s', expr_date) return cert_str, chain_str