예제 #1
0
    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)
예제 #2
0
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()
예제 #3
0
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()
예제 #4
0
    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)
예제 #5
0
    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
예제 #6
0
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()
예제 #7
0
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
예제 #8
0
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)
예제 #9
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)
예제 #10
0
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)
예제 #14
0
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]
예제 #15
0
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
예제 #16
0
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))
예제 #17
0
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
예제 #18
0
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)
예제 #20
0
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
예제 #21
0
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()
예제 #22
0
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
예제 #23
0
    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
예제 #24
0
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}")
예제 #25
0
파일: acme.py 프로젝트: jgraichen/salt-acme
    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)
예제 #26
0
    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
예제 #27
0
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)
예제 #28
0
    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)
예제 #29
0
    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)
예제 #30
0
    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)