def _create_client(self) -> None: """Create new ACME client.""" if self.path_registration_info.exists(): _LOGGER.info("Load exists ACME registration") regr = messages.RegistrationResource.json_loads( self.path_registration_info.read_text()) acme_url = urllib.parse.urlparse(self._acme_server) regr_url = urllib.parse.urlparse(regr.uri) if acme_url[0] != regr_url[0] or acme_url[1] != regr_url[1]: _LOGGER.info("Reset new ACME registration") self.path_registration_info.unlink() self.path_account_key.unlink() # Make sure that account key is loaded self._load_account_key() # Load a exists registration if self.path_registration_info.exists(): try: network = client.ClientNetwork(self._account_jwk, account=regr, user_agent=USER_AGENT) directory = messages.Directory.from_json( network.get(self._acme_server).json()) self._acme_client = client.ClientV2(directory, net=network) except errors.Error as err: _LOGGER.error("Can't connect to ACME server: %s", err) raise AcmeClientError() return # Create a new registration try: network = client.ClientNetwork(self._account_jwk, user_agent=USER_AGENT) directory = messages.Directory.from_json( network.get(self._acme_server).json()) self._acme_client = client.ClientV2(directory, net=network) except errors.Error as err: _LOGGER.error("Can't connect to ACME server: %s", err) raise AcmeClientError() try: _LOGGER.info( "Register a ACME account with TOS: %s", self._acme_client.directory.meta.terms_of_service, ) regr = self._acme_client.new_account( messages.NewRegistration.from_data( email=self._email, terms_of_service_agreed=True)) except errors.Error as err: _LOGGER.error("Can't register to ACME server: %s", err) raise AcmeClientError() # Store registration info self.path_registration_info.write_text(regr.json_dumps_pretty()) self.path_registration_info.chmod(0o600)
def answer_challenges(operation_id: int, **kwargs): db = kwargs["db"] operation = Operation.query.get(operation_id) service_instance = operation.service_instance acme_user = service_instance.acme_user time.sleep(int(config.DNS_PROPAGATION_SLEEP_TIME)) account_key = serialization.load_pem_private_key( acme_user.private_key_pem.encode(), password=None, backend=default_backend() ) wrapped_account_key = josepy.JWKRSA(key=account_key) registration = json.loads(acme_user.registration_json) net = client.ClientNetwork( wrapped_account_key, user_agent="cloud.gov external domain broker", account=registration, ) directory = messages.Directory.from_json(net.get(config.ACME_DIRECTORY).json()) client_acme = client.ClientV2(directory, net=net) for challenge in service_instance.challenges: challenge_body = messages.ChallengeBody.from_json( json.loads(challenge.body_json) ) challenge_response = challenge_body.response(wrapped_account_key) # Let the CA server know that we are ready for the challenge. client_acme.answer_challenge(challenge_body, challenge_response) challenge.answered = True db.session.add(challenge) db.session.commit()
def create_user(operation_id: int, **kwargs): db = kwargs["db"] acme_user = ACMEUser() operation = Operation.query.get(operation_id) service_instance = operation.service_instance service_instance.acme_user = acme_user key = josepy.JWKRSA( key=rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) ) private_key_pem_in_binary = key.key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption(), ) acme_user.private_key_pem = private_key_pem_in_binary.decode("utf-8") net = client.ClientNetwork(key, user_agent="cloud.gov external domain broker") directory = messages.Directory.from_json(net.get(config.ACME_DIRECTORY).json()) client_acme = client.ClientV2(directory, net=net) acme_user.email = "*****@*****.**" registration = client_acme.new_account( messages.NewRegistration.from_data( email=acme_user.email, terms_of_service_agreed=True ) ) acme_user.registration_json = registration.json_dumps() acme_user.uri = registration.uri db.session.add(operation) db.session.add(service_instance) db.session.add(acme_user) db.session.commit()
def new_account(self): """ Registers a new ACME account at the set ACME `directory` URL. By running this method, you are agreeing to the ACME servers terms of use.\n - :return [`none`]: the account and account_key properties will be updated with the new account registration.\n - :raises `InvalidDirectory`: if this object does not contain a valid ACME directory URL.\n - :raises `InvalidEmail`: if this object does not contain a valid email address to use during registration.\n\n ## Example\n ```python >>> client.new_account() ``` """ self.__validate_directory__() self.__validate_email__() # Generate a new RSA2048 account key rsa_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=(default_backend())) self.account_key = jose.JWKRSA(key=rsa_key) # Initialize our ACME client object self.__net__ = client.ClientNetwork(self.account_key, user_agent='simple_acme_dns/1.0.0') self.__directory__ = messages.Directory.from_json(self.__net__.get(self.directory).json()) self.__client__ = client.ClientV2(self.__directory__, net=self.__net__) # Complete registration registration = messages.NewRegistration.from_data(email=self.email, terms_of_service_agreed=True) self.account = self.__client__.new_account(registration)
def load_account(json_data): """ Loads an existing account from a JSON data string created by the `export_account()` method.\n - :param `json_data` [`str`]: the JSON account data string.\n - :return [`ACMEClient`]: the loaded ACMEClient object.\n\n ## Example\n ```python >>> client = simple_acme_dns.ACMEClient.load_account('{"account": {"body": {"key": {"n": "vtByzpW..."}}}}') ``` """ acct_data = json.loads(json_data) obj = ACMEClient() # Format the serialized data back into the object obj.directory = acct_data.get('directory', None) obj.domains = acct_data.get('domains', []) obj.certificate = acct_data.get('certificate', '').encode() obj.private_key = acct_data.get('private_key', '').encode() obj.email = acct_data['account']['body']['contact'][0].replace('mailto:', '') obj.account = messages.RegistrationResource.json_loads(json.dumps(acct_data['account'])) obj.account_key = jose.JWKRSA.json_loads(acct_data['account_key']) # Re-initialize the ACME client and registration obj.__net__ = client.ClientNetwork(obj.account_key, user_agent='simple_acme_dns/1.0.0') obj.__directory__ = messages.Directory.from_json(obj.__net__.get(obj.directory).json()) obj.__client__ = client.ClientV2(obj.__directory__, net=obj.__net__) obj.account = obj.__client__.query_registration(obj.account) return obj
def test_revoke_by_privkey(): client = chisel2.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 = chisel2.make_csr(domains) order = client.new_order(csr_pem) cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup() # Create a new client with the JWK as the cert private key jwk = josepy.JWKRSA(key=key) net = acme_client.ClientNetwork(key, user_agent="Boulder integration tester") directory = Directory.from_json(net.get(chisel2.DIRECTORY_V2).json()) new_client = acme_client.ClientV2(directory, net) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) reset_akamai_purges() client.revoke(josepy.ComparableX509(cert), 0) cert_file_pem = os.path.join(tempdir, "revokeme.pem") with open(cert_file_pem, "w") as f: f.write( OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert).decode()) ee_ocsp_url = "http://localhost:4002" verify_ocsp(cert_file_pem, "test/test-ca2.pem", ee_ocsp_url, "revoked") verify_akamai_purge()
def generateCertificate(domain, notification_email, directory_url=DIRECTORY_URL_PROD, cert_dir=None, debug=None): # Create account key acc_key = jose.JWKRSA( key=rsa.generate_private_key(public_exponent=65537, key_size=ACC_KEY_BITS, backend=default_backend())) # Create client configured to use our ACME server and trust our root cert net = client.ClientNetwork(acc_key, user_agent=USER_AGENT) if debug == True: directory_url = DIRECTORY_URL_STAGING directory = messages.Directory.from_json(net.get(directory_url).json()) client_acme = client.ClientV2(directory, net=net) # Register account and accept TOS regr = client_acme.new_account( messages.NewRegistration.from_data(email=notification_email, terms_of_service_agreed=True)) # Create domain private key and CSR pkey_pem, csr_pem = new_csr(domain) # Issue certificate orderr = client_acme.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_acme, challb, orderr) # Store the certificates if cert_dir is None: cert_dir = CERT_DIR cert_domain_dir = "{}/{}".format(cert_dir, domain) if not os.path.exists(cert_domain_dir): os.makedirs(cert_domain_dir) key_file = "{}/dsiprouter.key".format(cert_domain_dir) cert_file = "{}/dsiprouter.crt".format(cert_domain_dir) with os.fdopen(os.open(key_file, os.O_WRONLY | os.O_CREAT, 0o640), 'w') as keyfile: keyfile.write(pkey_pem.decode('utf-8')) with os.fdopen(os.open(cert_file, os.O_WRONLY | os.O_CREAT, 0o640), 'w') as certfile: certfile.write(fullchain_pem) # Change owner to root:kamailio so that Kamailio can load the configurations change_owner(cert_domain_dir, "root", "kamailio") change_owner(key_file, "root", "kamailio") change_owner(cert_file, "root", "kamailio") return pkey_pem.decode('utf-8'), fullchain_pem
def test_revoke_by_privkey(): client = chisel2.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 = chisel2.make_csr(domains) order = client.new_order(csr_pem) cleanup = chisel2.do_http_challenges(client, order.authorizations) try: order = client.poll_and_finalize(order) finally: cleanup() # Create a new client with the JWK as the cert private key jwk = josepy.JWKRSA(key=key) net = acme_client.ClientNetwork(key, user_agent="Boulder integration tester") directory = Directory.from_json(net.get(chisel2.DIRECTORY_V2).json()) new_client = acme_client.ClientV2(directory, net) cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, order.fullchain_pem) client.revoke(josepy.ComparableX509(cert), 0)
def create_v2_client(directory_url, accnt_key): """Creates an ACME v2 Client for making requests to Let's Encrypt with""" accnt_key = josepy.JWKRSA(key=accnt_key) net = client.ClientNetwork(accnt_key, user_agent="GlobaLeaks Let's Encrypt Client") directory = messages.Directory.from_json(net.get(directory_url).json()) return client.ClientV2(directory, net)
def uninitialized_client(key=None): if key is None: key = josepy.JWKRSA( key=rsa.generate_private_key(65537, 2048, default_backend())) net = acme_client.ClientNetwork(key, user_agent="Boulder integration tester") directory = messages.Directory.from_json(net.get(DIRECTORY_V2).json()) return acme_client.ClientV2(directory, net)
def create_acme_client(self, email=None): key = josepy.JWKRSA(key=rsa.generate_private_key(65537, 2048, default_backend())) net = acme_client.ClientNetwork(key, user_agent="Callback Catcher Client") directory = messages.Directory.from_json(net.get(LETSENCRYPTDIRECTORY).json()) client = acme_client.ClientV2(directory, net) tos = client.directory.meta.terms_of_service client.net.account = client.new_account(messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) return client
def _create_acme_client(self) -> acme_client.ClientV2: net = acme_client.ClientNetwork(key=josepy.JWKRSA( key=rsa.generate_private_key( public_exponent=ACME_RSA_PUBLIC_EXPONENT, key_size=ACME_RSA_KEY_SIZE, backend=default_backend(), ))) directory = acme_messages.Directory.from_json( net.get(self.options.acme_directory_url).json()) return acme_client.ClientV2(directory=directory, net=net)
def enroll(self, csr): """ enroll certificate """ self.logger.debug('CAhandler.enroll()') csr_pem = '-----BEGIN CERTIFICATE REQUEST-----\n{0}\n-----END CERTIFICATE REQUEST-----\n'.format(textwrap.fill(str(b64_url_recode(self.logger, csr)), 64)) cert_bundle = None error = None cert_raw = None poll_indentifier = None key = None try: self.logger.debug('CAhandler.enroll() opening key') with open(self.keyfile, "r") as keyf: key = josepy.JWKRSA.json_loads(keyf.read()) net = client.ClientNetwork(key) directory = messages.Directory.from_json(net.get(self.url).json()) acmeclient = client.ClientV2(directory, net=net) reg = messages.Registration.from_data(key=key, terms_of_service_agreed=True) regr = messages.RegistrationResource(uri="{}/account/{}".format(self.url, self.account), body=reg) self.logger.debug('CAhandler.enroll() checking remote registration status') regr = acmeclient.query_registration(regr) if regr.body.status != "valid": raise Exception("Bad ACME account: " + str(regr.body.error)) self.logger.debug('CAhandler.enroll() issuing signing order') self.logger.debug('CAhandler.enroll() CSR: ' + str(csr_pem)) order = acmeclient.new_order(csr_pem) self.logger.debug('CAhandler.enroll() polling for certificate') order = acmeclient.poll_and_finalize(order) if not order.fullchain_pem: raise Exception("Error getting certificate: " + str(order.error)) self.logger.debug('CAhandler.enroll() successful') cert_bundle = str(order.fullchain_pem) cert_raw = str(base64.b64encode(crypto.dump_certificate(crypto.FILETYPE_ASN1, crypto.load_certificate(crypto.FILETYPE_PEM, cert_bundle))), 'utf-8') except Exception as e: self.logger.error(str(e)) error = str(e) finally: del key self.logger.debug('Certificate.enroll() ended') return(error, cert_bundle, cert_raw, poll_indentifier)
def wildcard_revoke(cert_pem,account): #Check if registrar exists if account not in os.listdir(REG_DIRECTORY): print "This account does not exists, register it first with new_account.py" sys.exit(1) #Load files from disk with open(REG_DIRECTORY + "/" + account + "/private.key", "rb") as key_file: privkey = serialization.load_pem_private_key( key_file.read(), password=None, backend=default_backend() ) with open(REG_DIRECTORY + "/" + account + "/reguri.txt", "r") as reguri_file: reg_uri = reguri_file.read() #Compose registration resource (regr) key = jose.JWKRSA(key=privkey) regr = messages.RegistrationResource( body=messages.Registration( key=key.public_key()), uri = reg_uri) #Init ACME net = ClientNetwork(key) directory = net.get(DIRECTORY_URL).json() acme = client.ClientV2(directory, net) #Check if registration is valid if acme.query_registration(regr).body.status == u'valid': print "Registration valid" else: print "Registration invalid" sys.exit(1) #Deserialize key from variable cert = jose.ComparableX509(OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)) #Try to revoke cert, return false on error or revoked-already state try: revokation = acme.revoke(cert,1) except messages.Error,acme_exc: if str(acme_exc) == str("urn:ietf:params:acme:error:alreadyRevoked :: Certificate already revoked"): return ["Certificate already revoked",False] else: return [acme_exc, False]
def createAccount(user, dir_url, email): acc_key = jose.JWKRSA(key=rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) ) # Register account and accept TOS net = client.ClientNetwork(acc_key, user_agent=user) directory = messages.Directory.from_json(net.get(dir_url).json()) client_acme = client.ClientV2(directory, net=net) #Terms of Service URL is in client_acme.directory.meta.terms_of_service # Registration Resource: regr # Creates account with contact information. email = (email) regr = client_acme.new_account(messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) return client_acme
def create_registration(): global privkey, regr privkey = rsa.generate_private_key(public_exponent=65537, key_size=BITS, backend=default_backend()) key = jose.JWKRSA(key=privkey) net = ClientNetwork(key) directory = net.get(DIRECTORY_URL).json() acme = client.ClientV2(directory, net) regbody = dict( messages.Registration(contact=('mailto:[email protected]', ), terms_of_service_agreed=True, key=key.public_key())) #NEED TO SAVE REGBODY VARIABLE TO FILE regr = acme.new_account(messages.NewRegistration(**regbody))
def get_client(): global __cached_client_acme if __cached_client_acme: return __cached_client_acme account_pkey = loadFile("le/account.pem") account_data = None if account_pkey: account_data = loadFile("le/account.json") acc_key_pkey = None if account_pkey: acc_key_pkey = serialization.load_pem_private_key( data=account_pkey, password=None, backend=default_backend()) else: acc_key_pkey = rsa.generate_private_key(public_exponent=65537, key_size=KEY_BITS, backend=default_backend()) account_pkey = acc_key_pkey.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption()) acc_key = jose.JWKRSA(key=acc_key_pkey) net = client.ClientNetwork(acc_key, user_agent=USER_AGENT) directory = messages.Directory.from_json(net.get(DIRECTORY_URL).json()) client_acme = client.ClientV2(directory, net=net) if account_data != None: client_acme.net.account = messages.RegistrationResource.json_loads( account_data) try: if not client_acme.net.account: email = ('*****@*****.**') regr = client_acme.new_account( messages.NewRegistration.from_data( email=email, terms_of_service_agreed=True)) storeFile("le/account.json", regr.json_dumps().encode()) storeFile("le/account.pem", account_pkey) except errors.ConflictError: raise # TODO: Maybe handle? This happens when an account has been made with a key already __cached_client_acme = client_acme return client_acme
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") directory = messages.Directory.from_json(net.get(DIRECTORY).json()) client = acme_client.ClientV2(directory, net) tos = client.directory.meta.terms_of_service if tos == ACCEPTABLE_TOS: net.account = client.new_account( messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) else: raise Exception("Unrecognized terms of service URL %s" % tos) return client
def revoke(self, _cert, _rev_reason, _rev_date): """ revoke certificate """ self.logger.debug('CAhandler.revoke()') certpem = '-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n'.format(textwrap.fill(str(b64_url_recode(self.logger, _cert)), 64)) cert = josepy.ComparableX509(crypto.load_certificate(crypto.FILETYPE_PEM, certpem)) code = 200 message = None detail = None try: self.logger.debug('CAhandler.revoke() opening key') with open(self.keyfile, "r") as keyf: key = josepy.JWKRSA.json_loads(keyf.read()) net = client.ClientNetwork(key) directory = messages.Directory.from_json(net.get(self.url).json()) acmeclient = client.ClientV2(directory, net=net) reg = messages.Registration.from_data(key=key, terms_of_service_agreed=True) regr = messages.RegistrationResource(uri="{}/account/{}".format(self.url, self.account), body=reg) self.logger.debug('CAhandler.revoke() checking remote registration status') regr = acmeclient.query_registration(regr) if regr.body.status != "valid": raise Exception("Bad ACME account: " + str(regr.body.error)) self.logger.debug('CAhandler.revoke() issuing revocation order') acmeclient.revoke(cert, 1) self.logger.debug('CAhandler.revoke() successfull') except Exception as e: self.logger.error(str(e)) code = 500 message = 'urn:ietf:params:acme:error:serverInternal' detail = str(e) finally: del key self.logger.debug('Certificate.revoke() ended') return(code, message, detail)
def get_acme_client(conf, domain_conf): """ ACME Client """ account_key = load_letsencrypt_account_key(conf, domain_conf['account_uri']) a_key = jose.JWKRSA(key=account_key) net = client.ClientNetwork(a_key) directory_acme = messages.Directory.from_json( net.get(domain_conf['directory']).json()) client_acme = client.ClientV2(directory_acme, net) if not domain_conf['account_uri']: LOG.debug("Registering with ACME server with the new account key") new_reg = messages.NewRegistration.from_data( email=(', '.join(domain_conf['contact'])), terms_of_service_agreed=True) registration_resource = client_acme.new_account(new_reg) domain_conf['account_uri'] = registration_resource.uri LOG.debug("Write Account URI '%s' into Config file ", domain_conf['account_uri']) new_domain_conf = yaml.dump(domain_conf, default_flow_style=False) save_to_s3(conf, conf['config_file'], new_domain_conf) else: registration = messages.Registration(key=a_key, contact=tuple( domain_conf['contact'])) registration_resource = messages.RegistrationResource( body=registration, uri=domain_conf['account_uri']) LOG.debug( "Update the regristration: {0}".format(registration_resource)) registration_resource = client_acme.query_registration( registration_resource) net.account = registration_resource return client_acme
def initiate_challenges(operation_id: int, **kwargs): db = kwargs["db"] operation = Operation.query.get(operation_id) service_instance = operation.service_instance acme_user = service_instance.acme_user account_key = serialization.load_pem_private_key( acme_user.private_key_pem.encode(), password=None, backend=default_backend() ) wrapped_account_key = josepy.JWKRSA(key=account_key) registration = json.loads(acme_user.registration_json) net = client.ClientNetwork( wrapped_account_key, user_agent="cloud.gov external domain broker", account=registration, ) directory = messages.Directory.from_json(net.get(config.ACME_DIRECTORY).json()) client_acme = client.ClientV2(directory, net=net) order = client_acme.new_order(service_instance.csr_pem.encode()) service_instance.order_json = json.dumps(order.to_json()) for domain in service_instance.domain_names: challenge_body = dns_challenge(order, domain) ( challenge_response, challenge_validation_contents, ) = challenge_body.response_and_validation(wrapped_account_key) challenge = Challenge() challenge.body_json = challenge_body.json_dumps() challenge.domain = domain challenge.service_instance = service_instance challenge.validation_domain = challenge_body.validation_domain_name(domain) challenge.validation_contents = challenge_validation_contents db.session.add(challenge) db.session.commit()
def account(): # Store account information in an JWKRSA formated json file reg_file = settings.LETSENCRYPT_STATE_FOLDER + '/regr.json' key_file = settings.LETSENCRYPT_STATE_FOLDER + '/private_key.json' try: # Read existing account data and private key with open(reg_file, 'r') as f: regr = messages.RegistrationResource.json_loads(f.read()) with open(key_file, 'r') as f: key = jose.JWK.json_loads(f.read()) except IOError as error: # Generate new private key, as we expect that the account doesn't exist private_key = rsa.generate_private_key( public_exponent = 65537, key_size = settings.LETSENCRYPT_ACCT_KEY_BITS, backend = default_backend() ) key = jose.JWKRSA(key=private_key) # Prepare ACME client connection with account private key net = client.ClientNetwork(key) directory = messages.Directory.from_json( net.get(settings.LETSENCRYPT_ACME_SERVER).json() ) client_acme = client.ClientV2(directory, net=net) # Generate a new account and store account information locally email = getattr(settings, 'KUMQUAT_EMAIL', None) regr = client_acme.new_account( messages.NewRegistration.from_data( email = email, terms_of_service_agreed = True ) ) # Store private key as json format with open(key_file, 'w') as f: f.write(key.json_dumps()) # Store regr information as json format with open(reg_file, 'w') as f: f.write(regr.json_dumps()) return key, regr
def _get_or_create_account(self): """Register account if not already done, else just return ACME client updated with existing account""" logger.info("Registering account...") ca_key = serialization.load_pem_private_key( data=self.account_key.read_bytes(), password=None, backend=default_backend(), ) acc_key = jose.JWKRSA(key=ca_key) # Register account and accept TOS client_network = client.ClientNetwork(acc_key, user_agent=USER_AGENT) directory = messages.Directory.from_json(client_network.get(self.ca_api + '/directory').json()) client_acme = client.ClientV2(directory=directory, net=client_network) try: # Creates account with contact information. # Terms of Service URL is in client_acme.directory.meta.terms_of_service client_acme.new_account( messages.NewRegistration.from_data( email=self.contact_email, terms_of_service_agreed=True, ) ) logger.info("New account successfully created!") except errors.ConflictError: logger.info("Account already registered!") # retrieve existing account information account_registration = messages.NewRegistration(key=acc_key.public_key(), only_return_existing=True) # TODO migrate to public API when available in ACME module # reproduce code of client_acme.new_account but without throwing exception when account already exists response = client_acme._post(directory['newAccount'], account_registration) account_info = client_acme._regr_from_response(response) # assign registration information to existing client client_acme.net.account = account_info return client_acme
def _register_account(): # Register account and accept TOS if config.staging: directory_url = STAG_API_URL else: directory_url = PROD_API_URL le_acc_key = _create_acc_key() net = client.ClientNetwork(le_acc_key, user_agent=USER_AGENT) directory = messages.Directory.from_json(net.get(directory_url).json()) client_acme = client.ClientV2(directory, net=net) # Terms of Service URL is in client_acme.directory.meta.terms_of_service # Registration Resource: registration # Creates account with contact information. email = (config.contact_email) registration = client_acme.new_account( messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) jobj = registration.fields_to_partial_json() logger.info(f"LE Registration: {jobj}")
def __init__( self, server, email=None, verify_ssl=True, account_dir=None, **_kwargs ): if account_dir: self.base = account_dir else: self.base = os.path.join( __opts__["cachedir"], "acme", hashlib.sha256(server.encode()).hexdigest(), ) logging.info("ACME account directory: %s", self.base) logging.info("Using ACME server at %s", server) if not os.path.exists(self.base): os.makedirs(self.base, exist_ok=True) self.net = client.ClientNetwork(key=self._private_key(), verify_ssl=verify_ssl) self.directory = messages.Directory.from_json(self.net.get(server).json()) self.client = client.ClientV2(self.directory, self.net) self.net.account = self._registration(email)
def get_acme_client_and_key(self, acme_directory_uri, tos=False): data = self.middleware.call_sync('acme.registration.query', [['directory', '=', acme_directory_uri]]) if not data: data = self.middleware.call_sync( 'acme.registration.create', {'tos': tos, 'acme_directory_uri': acme_directory_uri} ) else: data = data[0] # Making key now key = jose.JWKRSA.fields_from_json(json.loads(data['body']['key'])) key_dict = key.fields_to_partial_json() # Making registration resource now registration = messages.RegistrationResource.from_json({ 'uri': data['uri'], 'terms_of_service': data['tos'], 'body': { 'contact': [data['body']['contact']], 'status': data['body']['status'], 'key': { 'e': key_dict['e'], 'kty': 'RSA', 'n': key_dict['n'] } } }) return client.ClientV2( messages.Directory({ 'newAccount': data['new_account_uri'], 'newNonce': data['new_nonce_uri'], 'newOrder': data['new_order_uri'], 'revokeCert': data['revoke_cert_uri'] }), client.ClientNetwork(key, account=registration) ), key
def example_http(): """This example executes the whole process of fulfilling a HTTP-01 challenge for one specific domain. The workflow consists of: (Account creation) - Create account key - Register account and accept TOS (Certificate actions) - Select HTTP-01 within offered challenges by the CA server - Set up http challenge resource - Set up standalone web server - Create domain private key and CSR - Issue certificate - Renew certificate - Revoke certificate (Account update actions) - Change contact information - Deactivate Account """ # Create account key acc_key = jose.JWKRSA( key=rsa.generate_private_key(public_exponent=65537, key_size=ACC_KEY_BITS, backend=default_backend())) # Register account and accept TOS net = client.ClientNetwork(acc_key, user_agent=USER_AGENT) directory = messages.Directory.from_json(net.get(DIRECTORY_URL).json()) client_acme = client.ClientV2(directory, net=net) # Terms of Service URL is in client_acme.directory.meta.terms_of_service # Registration Resource: regr # Creates account with contact information. email = ('*****@*****.**') regr = client_acme.new_account( messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) # Create domain private key and CSR pkey_pem, csr_pem = new_csr_comp(DOMAIN) # Issue certificate orderr = client_acme.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_acme, challb, orderr) # Renew certificate _, csr_pem = new_csr_comp(DOMAIN, pkey_pem) orderr = client_acme.new_order(csr_pem) challb = select_http01_chall(orderr) # Performing challenge fullchain_pem = perform_http01(client_acme, challb, orderr) # Revoke certificate fullchain_com = jose.ComparableX509( OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, fullchain_pem)) try: client_acme.revoke(fullchain_com, 0) # revocation reason = 0 except errors.ConflictError: # Certificate already revoked. pass # Query registration status. client_acme.net.account = regr try: regr = client_acme.query_registration(regr) except errors.Error as err: if err.typ == messages.OLD_ERROR_PREFIX + 'unauthorized' \ or err.typ == messages.ERROR_PREFIX + 'unauthorized': # Status is deactivated. pass raise # Change contact information email = '*****@*****.**' regr = client_acme.update_registration( regr.update(body=regr.body.update(contact=('mailto:' + email, )))) # Deactivate account/registration regr = client_acme.deactivate_registration(regr)
def revoke(self, _cert, _rev_reason, _rev_date): """ revoke certificate """ self.logger.debug('CAhandler.revoke()') user_key = None code = 500 message = 'urn:ietf:params:acme:error:serverInternal' detail = None try: certpem = '-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n'.format( textwrap.fill(str(b64_url_recode(self.logger, _cert)), 64)) cert = josepy.ComparableX509( crypto.load_certificate(crypto.FILETYPE_PEM, certpem)) if os.path.exists(self.keyfile): user_key = self._user_key_load() net = client.ClientNetwork(user_key) if user_key: directory = messages.Directory.from_json( net.get('{0}{1}'.format( self.url, self.path_dic['directory_path'])).json()) acmeclient = client.ClientV2(directory, net=net) reg = messages.NewRegistration.from_data( key=user_key, email=self.email, terms_of_service_agreed=True, only_return_existing=True) if not self.account: self._account_lookup(acmeclient, reg, directory) if self.account: regr = messages.RegistrationResource( uri="{0}{1}{2}".format(self.url, self.path_dic['acct_path'], self.account), body=reg) self.logger.debug( 'CAhandler.revoke() checking remote registration status' ) regr = acmeclient.query_registration(regr) if regr.body.status == "valid": self.logger.debug( 'CAhandler.revoke() issuing revocation order') acmeclient.revoke(cert, 1) self.logger.debug('CAhandler.revoke() successfull') code = 200 message = None else: self.logger.error( 'CAhandler.enroll: Bad ACME account: {0}'.format( regr.body.error)) detail = 'Bad ACME account: {0}'.format( regr.body.error) else: self.logger.error( 'CAhandler.revoke(): could not find account key and lookup at acme-endpoint failed.' ) detail = 'account lookup failed' else: self.logger.error( 'CAhandler.revoke(): could not load user_key {0}'.format( self.keyfile)) detail = 'Internal Error' except Exception as err: self.logger.error('CAhandler.enroll: error: {0}'.format(err)) detail = str(err) finally: del user_key self.logger.debug('Certificate.revoke() ended') return (code, message, detail)
def enroll(self, csr): """ enroll certificate """ # pylint: disable=R0915 self.logger.debug('CAhandler.enroll()') csr_pem = '-----BEGIN CERTIFICATE REQUEST-----\n{0}\n-----END CERTIFICATE REQUEST-----\n'.format( textwrap.fill(str(b64_url_recode(self.logger, csr)), 64)) cert_bundle = None error = None cert_raw = None poll_indentifier = None user_key = None # check CN and SAN against black/whitlist result = self._csr_check(csr) if result: try: user_key = self._user_key_load() net = client.ClientNetwork(user_key) directory = messages.Directory.from_json( net.get('{0}{1}'.format( self.url, self.path_dic['directory_path'])).json()) acmeclient = client.ClientV2(directory, net=net) reg = messages.Registration.from_data( key=user_key, terms_of_service_agreed=True) if self.account: regr = messages.RegistrationResource( uri="{0}{1}{2}".format(self.url, self.path_dic['acct_path'], self.account), body=reg) self.logger.debug( 'CAhandler.enroll(): checking remote registration status' ) regr = acmeclient.query_registration(regr) else: # new account or existing account with missing account id regr = self._account_register(acmeclient, user_key, directory) if regr.body.status == "valid": self.logger.debug( 'CAhandler.enroll() issuing signing order') self.logger.debug('CAhandler.enroll() CSR: ' + str(csr_pem)) order = acmeclient.new_order(csr_pem) # query challenges for authzr in list(order.authorizations): (challenge_name, challenge_content, challenge) = self._http_challenge_info( authzr, user_key) if challenge_name and challenge_content: # store challenge in database to allow challenge validation self._challenge_store(challenge_name, challenge_content) _auth_response = acmeclient.answer_challenge( challenge, challenge.chall.response(user_key) ) # lgtm [py/unused-local-variable] self.logger.debug( 'CAhandler.enroll() polling for certificate') order = acmeclient.poll_and_finalize(order) if order.fullchain_pem: self.logger.debug('CAhandler.enroll() successful') cert_bundle = str(order.fullchain_pem) cert_raw = str( base64.b64encode( crypto.dump_certificate( crypto.FILETYPE_ASN1, crypto.load_certificate( crypto.FILETYPE_PEM, cert_bundle))), 'utf-8') else: # raise Exception("Error getting certificate: " + str(order.error)) self.logger.error( 'CAhandler.enroll: Error getting certificate: {0}'. format(order.error)) error = 'Error getting certificate: {0}'.format( order.error) else: self.logger.error( 'CAhandler.enroll: Bad ACME account: {0}'.format( regr.body.error)) error = 'Bad ACME account: {0}'.format(regr.body.error) # raise Exception("Bad ACME account: " + str(regr.body.error)) except Exception as err: self.logger.error('CAhandler.enroll: error: {0}'.format(err)) error = str(err) finally: del user_key else: error = 'CSR rejected. Either CN or SANs are not allowed by policy' self.logger.error( 'CAhandler.enroll: CSR rejected. Either CN or SANs are not allowed by policy.' ) self.logger.debug('Certificate.enroll() ended') return (error, cert_bundle, cert_raw, poll_indentifier)
def do_create(self, data): """ Register with ACME Server Create a regisration for a specific ACME Server registering root user with it `acme_directory_uri` is a directory endpoint for any ACME Server .. examples(websocket):: Register with ACME Server :::javascript { "id": "6841f242-840a-11e6-a437-00e04d680384", "msg": "method", "method": "acme.registration.create", "params": [{ "tos": true, "acme_directory_uri": "https://acme-staging-v02.api.letsencrypt.org/directory" "JWK_create": { "key_size": 2048, "public_exponent": 65537 } }] } """ # STEPS FOR CREATION # 1) CREATE KEY # 2) REGISTER CLIENT # 3) SAVE REGISTRATION OBJECT # 4) SAVE REGISTRATION BODY verrors = ValidationErrors() directory = self.get_directory(data['acme_directory_uri']) if not isinstance(directory, messages.Directory): verrors.add( 'acme_registration_create.acme_directory_uri', f'System was unable to retrieve the directory with the specified acme_directory_uri: {directory}' ) # Normalizing uri after directory call as let's encrypt staging api # does not accept a trailing slash right now data['acme_directory_uri'] += '/' if data['acme_directory_uri'][ -1] != '/' else '' if not data['tos']: verrors.add('acme_registration_create.tos', 'Please agree to the terms of service') # For now we assume that only root is responsible for certs issued under ACME protocol email = (self.middleware.call_sync('user.query', [['uid', '=', 0]]))[0]['email'] if not email: raise CallError( 'Please specify root email address which will be used with the ACME server' ) if self.middleware.call_sync( 'acme.registration.query', [['directory', '=', data['acme_directory_uri']]]): verrors.add( 'acme_registration_create.acme_directory_uri', 'A registration with the specified directory uri already exists' ) if verrors: raise verrors key = jose.JWKRSA(key=rsa.generate_private_key( public_exponent=data['JWK_create']['public_exponent'], key_size=data['JWK_create']['key_size'], backend=default_backend())) acme_client = client.ClientV2(directory, client.ClientNetwork(key)) register = acme_client.new_account( messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) # We have registered with the acme server # Save registration object registration_id = self.middleware.call_sync( 'datastore.insert', self._config.datastore, { 'uri': register.uri, 'tos': register.terms_of_service, 'new_account_uri': directory.newAccount, 'new_nonce_uri': directory.newNonce, 'new_order_uri': directory.newOrder, 'revoke_cert_uri': directory.revokeCert, 'directory': data['acme_directory_uri'] }) # Save registration body self.middleware.call_sync( 'datastore.insert', 'system.acmeregistrationbody', { 'contact': register.body.contact[0], 'status': register.body.status, 'key': key.json_dumps(), 'acme': registration_id }) return self.middleware.call_sync( f'{self._config.namespace}._get_instance', registration_id)