예제 #1
0
 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.")
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
    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.")
예제 #8
0
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')