def _register_client(self, client, account_key): """Register this Letsencrypt client.""" 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.")
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 register(storage, client, log, agree_to_tos_url=None): existing_regr = None if not os.path.exists(storage): # Create a new registration. log("Registering a new account with Let's Encrypt.") regr = client.register() else: log("Validating existing account saved to %s." % storage) # Validate existing registration by querying for it from the server. with open(storage, 'r') as f: regr = acme.messages.RegistrationResource.json_loads(f.read()) existing_regr = regr.json_dumps() regr = client.query_registration(regr) # If this call is to agree to a terms of service agreement, update the # registration. if agree_to_tos_url: regr = client.update_registration(regr.update(body=regr.body.update(agreement=agree_to_tos_url))) # Write new or updated registration (if it changed, and hopefully json_dumps is stable). if existing_regr != regr.json_dumps(): if existing_regr is not None: log("Saving updated account information.") with open(storage, 'w') as f: f.write(regr.json_dumps_pretty()) return regr
def register(storage, client, log, agree_to_tos_url=None): existing_regr = None if not os.path.exists(storage): # Create a new registration. log("Registering a new account with Let's Encrypt.") regr = client.register() else: log("Validating existing account saved to %s." % storage) # Validate existing registration by querying for it from the server. with open(storage, 'r') as f: regr = acme.messages.RegistrationResource.json_loads(f.read()) existing_regr = regr.json_dumps() try: regr = client.query_registration(regr) except acme.messages.Error as e: if e.typ == "urn:acme:error:unauthorized": # There is a problem accessing our own account. This probably # means the stored registration information is not valid. raise AccountDataIsCorrupt(storage) raise # If this call is to agree to a terms of service agreement, update the # registration. if agree_to_tos_url: regr = client.update_registration(regr.update(body=regr.body.update(agreement=agree_to_tos_url))) # Write new or updated registration (if it changed, and hopefully json_dumps is stable). if existing_regr != regr.json_dumps(): if existing_regr is not None: log("Saving updated account information.") with open(storage, 'w') as f: f.write(regr.json_dumps_pretty()) return regr
def _register(hostname): existing_regr = None #see if hostname exists in our DB info = _recallHost(hostname) if info == None: _storeKeypair(hostname, _generateKeypair(), _generateKeypair()) info = _recallHost(hostname) key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, base64.b64decode(info['acct_privkey'])) client = acme.client.Client(CA,jose.JWKRSA(key=jose.ComparableRSAKey(key))) print client if info['reg_json'] == None: # Create a new registration. print ("Registering a new account with Let's Encrypt.") regr = client.register() else: print ("Validating existing account for hostname %s." % hostname) # Validate existing registration by querying for it from the server. regr = acme.messages.RegistrationResource.json_loads(info['reg_json']) existing_regr = regr.json_dumps() try: regr = client.query_registration(regr) except acme.messages.Error as e: if e.typ == "urn:acme:error:unauthorized": # There is a problem accessing our own account. This probably # means the stored registration information is not valid. raise AccountDataIsCorrupt(storage) raise # If this call is to agree to a terms of service agreement, update the # registration. regr = client.update_registration(regr.update(body=regr.body.update(agreement=TERMS))) # Write new or updated registration (if it changed, and hopefully json_dumps is stable). if existing_regr != regr.json_dumps(): if existing_regr is not None: print ("Saving updated account information.") _updateHost(hostname, 'reg_json', regr.json_dumps_pretty()) return regr
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')