Example #1
0
    def _regr_from_response(cls, response, uri=None, terms_of_service=None):
        if 'terms-of-service' in response.links:
            terms_of_service = response.links['terms-of-service']['url']

        return messages.RegistrationResource(
            body=messages.Registration.from_json(response.json()),
            uri=response.headers.get('Location', uri),
            terms_of_service=terms_of_service)
Example #2
0
    def setUp(self):
        self.response = mock.MagicMock(
            ok=True, status_code=http_client.OK, headers={}, links={})
        self.net = mock.MagicMock()
        self.net.post.return_value = self.response
        self.net.get.return_value = self.response

        self.directory = messages.Directory({
            messages.NewRegistration:
                'https://www.letsencrypt-demo.org/acme/new-reg',
            messages.Revocation:
                'https://www.letsencrypt-demo.org/acme/revoke-cert',
            messages.NewAuthorization:
                'https://www.letsencrypt-demo.org/acme/new-authz',
        })

        from acme.client import Client
        self.client = Client(
            directory=self.directory, key=KEY, alg=jose.RS256, net=self.net)

        self.identifier = messages.Identifier(
            typ=messages.IDENTIFIER_FQDN, value='example.com')

        # Registration
        self.contact = ('mailto:[email protected]', 'tel:+12025551212')
        reg = messages.Registration(
            contact=self.contact, key=KEY.public_key())
        self.new_reg = messages.NewRegistration(**dict(reg))
        self.regr = messages.RegistrationResource(
            body=reg, uri='https://www.letsencrypt-demo.org/acme/reg/1',
            new_authzr_uri='https://www.letsencrypt-demo.org/acme/new-reg',
            terms_of_service='https://www.letsencrypt-demo.org/tos')

        # Authorization
        authzr_uri = 'https://www.letsencrypt-demo.org/acme/authz/1'
        challb = messages.ChallengeBody(
            uri=(authzr_uri + '/1'), status=messages.STATUS_VALID,
            chall=challenges.DNS(token=jose.b64decode(
                'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA')))
        self.challr = messages.ChallengeResource(
            body=challb, authzr_uri=authzr_uri)
        self.authz = messages.Authorization(
            identifier=messages.Identifier(
                typ=messages.IDENTIFIER_FQDN, value='example.com'),
            challenges=(challb,), combinations=None)
        self.authzr = messages.AuthorizationResource(
            body=self.authz, uri=authzr_uri,
            new_cert_uri='https://www.letsencrypt-demo.org/acme/new-cert')

        # Request issuance
        self.certr = messages.CertificateResource(
            body=messages_test.CERT, authzrs=(self.authzr,),
            uri='https://www.letsencrypt-demo.org/acme/cert/1',
            cert_chain_uri='https://www.letsencrypt-demo.org/ca')

        # Reason code for revocation
        self.rsn = 1
Example #3
0
    def setUp(self):
        self.verify_ssl = mock.MagicMock()
        self.wrap_in_jws = mock.MagicMock(return_value=mock.sentinel.wrapped)

        from acme.client import Client
        self.net = Client(
            new_reg_uri='https://www.letsencrypt-demo.org/acme/new-reg',
            key=KEY,
            alg=jose.RS256,
            verify_ssl=self.verify_ssl)
        self.nonce = jose.b64encode('Nonce')
        self.net._nonces.add(self.nonce)  # pylint: disable=protected-access

        self.response = mock.MagicMock(ok=True, status_code=httplib.OK)
        self.response.headers = {}
        self.response.links = {}

        self.post = mock.MagicMock(return_value=self.response)
        self.get = mock.MagicMock(return_value=self.response)

        self.identifier = messages.Identifier(typ=messages.IDENTIFIER_FQDN,
                                              value='example.com')

        # Registration
        self.contact = ('mailto:[email protected]', 'tel:+12025551212')
        reg = messages.Registration(contact=self.contact,
                                    key=KEY.public(),
                                    recovery_token='t')
        self.regr = messages.RegistrationResource(
            body=reg,
            uri='https://www.letsencrypt-demo.org/acme/reg/1',
            new_authzr_uri='https://www.letsencrypt-demo.org/acme/new-reg',
            terms_of_service='https://www.letsencrypt-demo.org/tos')

        # Authorization
        authzr_uri = 'https://www.letsencrypt-demo.org/acme/authz/1'
        challb = messages.ChallengeBody(uri=(authzr_uri + '/1'),
                                        status=messages.STATUS_VALID,
                                        chall=challenges.DNS(token='foo'))
        self.challr = messages.ChallengeResource(body=challb,
                                                 authzr_uri=authzr_uri)
        self.authz = messages.Authorization(identifier=messages.Identifier(
            typ=messages.IDENTIFIER_FQDN, value='example.com'),
                                            challenges=(challb, ),
                                            combinations=None)
        self.authzr = messages.AuthorizationResource(
            body=self.authz,
            uri=authzr_uri,
            new_cert_uri='https://www.letsencrypt-demo.org/acme/new-cert')

        # Request issuance
        self.certr = messages.CertificateResource(
            body=messages_test.CERT,
            authzrs=(self.authzr, ),
            uri='https://www.letsencrypt-demo.org/acme/cert/1',
            cert_chain_uri='https://www.letsencrypt-demo.org/ca')
Example #4
0
    def setUp(self):
        zope.component.provideUtility(display_util.FileDisplay(sys.stdout))

        self.accounts_dir = tempfile.mkdtemp("accounts")
        self.account_keys_dir = os.path.join(self.accounts_dir, "keys")
        os.makedirs(self.account_keys_dir, 0o700)

        self.config = mock.MagicMock(
            accounts_dir=self.accounts_dir,
            account_keys_dir=self.account_keys_dir,
            server="certbot-demo.org")
        self.key = KEY

        self.acc1 = account.Account(messages.RegistrationResource(
            uri=None, new_authzr_uri=None, body=messages.Registration.from_data(
                email="*****@*****.**")), self.key)
        self.acc2 = account.Account(messages.RegistrationResource(
            uri=None, new_authzr_uri=None, body=messages.Registration.from_data(
                email="*****@*****.**", phone="phone")), self.key)
Example #5
0
    def setUp(self):
        self.tmp = tempfile.mkdtemp()
        self.config = mock.MagicMock(
            accounts_dir=os.path.join(self.tmp, "accounts"))
        from certbot.account import AccountFileStorage
        self.storage = AccountFileStorage(self.config)

        from certbot.account import Account
        self.acc = Account(regr=messages.RegistrationResource(
            uri=None, body=messages.Registration()),
                           key=KEY)
Example #6
0
    def setUp(self):
        super().setUp()

        display_obj.set_display(display_obj.FileDisplay(sys.stdout, False))

        self.account_keys_dir = os.path.join(self.tempdir, "keys")
        filesystem.makedirs(self.account_keys_dir, 0o700)

        self.config = mock.MagicMock(
            accounts_dir=self.tempdir,
            account_keys_dir=self.account_keys_dir,
            server="certbot-demo.org")
        self.key = KEY

        self.acc1 = account.Account(messages.RegistrationResource(
            uri=None, body=messages.Registration.from_data(
                email="*****@*****.**")), self.key)
        self.acc2 = account.Account(messages.RegistrationResource(
            uri=None, body=messages.Registration.from_data(
                email="*****@*****.**", phone="phone")), self.key)
Example #7
0
 def setUp(self):
     super(SubscriptionTest, self).setUp()
     self.account = account.Account(
         regr=messages.RegistrationResource(
             uri=None, body=messages.Registration(),
             new_authzr_uri='hi'),
         key=_KEY,
         meta=account.Account.Meta(
             creation_host='test.certbot.org',
             creation_dt=datetime.datetime(
                 2015, 7, 4, 14, 4, 10, tzinfo=pytz.UTC)))
     self.config.email = '*****@*****.**'
     self.config.eff_email = None
Example #8
0
 def _cb_parse_registration_response(self, response, uri=None):
     """
     Parse a new or update registration response from the server.
     """
     links = _parse_header_links(response)
     terms_of_service = None
     if u'terms-of-service' in links:
         terms_of_service = links[u'terms-of-service'][u'url']
     return (response.json().addCallback(
         lambda body: messages.RegistrationResource(
             body=messages.Registration.from_json(body),
             uri=self._maybe_location(response, uri),
             terms_of_service=terms_of_service)))
    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)
Example #10
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]
Example #11
0
    def setUp(self):
        super(AccountFileStorageTest, self).setUp()

        from certbot._internal.account import AccountFileStorage
        self.storage = AccountFileStorage(self.config)

        from certbot._internal.account import Account
        new_authzr_uri = "hi"
        self.acc = Account(regr=messages.RegistrationResource(
            uri=None,
            body=messages.Registration(),
            new_authzr_uri=new_authzr_uri),
                           key=KEY)
        self.mock_client = mock.MagicMock()
        self.mock_client.directory.new_authz = new_authzr_uri
Example #12
0
    def _regr_from_response(cls, response, uri=None, new_authzr_uri=None,
                            terms_of_service=None):
        if 'terms-of-service' in response.links:
            terms_of_service = response.links['terms-of-service']['url']
        if 'next' in response.links:
            new_authzr_uri = response.links['next']['url']

        if new_authzr_uri is None:
            raise errors.ClientError('"next" link missing')

        return messages.RegistrationResource(
            body=messages.Registration.from_json(response.json()),
            uri=response.headers.get('Location', uri),
            new_authzr_uri=new_authzr_uri,
            terms_of_service=terms_of_service)
Example #13
0
 def _update_regr(self, account, acme, dir_path):
     # type: (Account, ClientBase, str) -> None
     with open(self._regr_path(dir_path), "w") as regr_file:
         regr = account.regr
         # If we have a value for new-authz, save it for forwards
         # compatibility with older versions of Certbot. If we don't
         # have a value for new-authz, this is an ACMEv2 directory where
         # an older version of Certbot won't work anyway.
         if hasattr(acme.directory, "new-authz"):
             regr = RegistrationResourceWithNewAuthzrURI(
                 new_authzr_uri=acme.directory.new_authz,
                 body={},
                 uri=regr.uri)
         else:
             regr = messages.RegistrationResource(body={}, uri=regr.uri)
         regr_file.write(regr.json_dumps())
Example #14
0
    def setUp(self):
        self.tmp = tempfile.mkdtemp()
        self.config = mock.MagicMock(
            accounts_dir=os.path.join(self.tmp, "accounts"))
        from certbot.account import AccountFileStorage
        self.storage = AccountFileStorage(self.config)

        from certbot.account import Account
        new_authzr_uri = "hi"
        self.acc = Account(
            regr=messages.RegistrationResource(
                uri=None, body=messages.Registration(),
                new_authzr_uri=new_authzr_uri),
            key=KEY)
        self.mock_client = mock.MagicMock()
        self.mock_client.directory.new_authz = new_authzr_uri
Example #15
0
    def create(cls, email, base_path=BASEPATH, directory_url=DIRECTORY_URL):
        """Creates a new ACME Account using the specified email as point of contact"""
        ret = ACMEAccount(base_path=base_path, directory_url=directory_url)
        new_reg = messages.NewRegistration.from_data(
            email=email, terms_of_service_agreed=True)
        acme = cls._get_acme_client(ret.jkey, directory_url=directory_url)
        try:
            regr = acme.new_account(new_reg)
        except errors.Error as account_error:
            raise ACMEError('Unable to create ACME account') from account_error
        except requests.exceptions.RequestException as request_error:
            raise ACMETransportError(
                'Unable to create ACME account') from request_error

        ret.regr = messages.RegistrationResource(body=regr.body, uri=regr.uri)

        return ret
Example #16
0
    def test_new_account_v2(self):
        directory = messages.Directory({
            "newAccount":
            'https://www.letsencrypt-demo.org/acme/new-account',
        })
        from acme.client import ClientV2
        client = ClientV2(directory, self.net)
        self.response.status_code = http_client.CREATED
        self.response.json.return_value = self.regr.body.to_json()
        self.response.headers['Location'] = self.regr.uri

        self.regr = messages.RegistrationResource(
            body=messages.Registration(contact=self.contact,
                                       key=KEY.public_key()),
            uri='https://www.letsencrypt-demo.org/acme/reg/1')

        self.assertEqual(self.regr, client.new_account(self.regr))
    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)
Example #18
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
Example #19
0
 def _parse_regr_response(self,
                          response,
                          uri=None,
                          new_authzr_uri=None,
                          terms_of_service=None):
     """
     Parse a registration response from the server.
     """
     links = _parse_header_links(response)
     if u'terms-of-service' in links:
         terms_of_service = links[u'terms-of-service'][u'url']
     if u'next' in links:
         new_authzr_uri = links[u'next'][u'url']
     if new_authzr_uri is None:
         raise errors.ClientError('"next" link missing')
     return (response.json().addCallback(
         lambda body: messages.RegistrationResource(
             body=messages.Registration.from_json(body),
             uri=self._maybe_location(response, uri=uri),
             new_authzr_uri=new_authzr_uri,
             terms_of_service=terms_of_service)))
Example #20
0
    def setUp(self):
        super().setUp()

        from certbot._internal.account import AccountFileStorage
        self.storage = AccountFileStorage(self.config)

        from certbot._internal.account import Account
        new_authzr_uri = "hi"
        meta = Account.Meta(creation_host="test.example.org",
                            creation_dt=datetime.datetime(2021,
                                                          1,
                                                          5,
                                                          14,
                                                          4,
                                                          10,
                                                          tzinfo=pytz.UTC))
        self.acc = Account(regr=messages.RegistrationResource(
            uri=None,
            body=messages.Registration(),
            new_authzr_uri=new_authzr_uri),
                           key=KEY,
                           meta=meta)
        self.mock_client = mock.MagicMock()
        self.mock_client.directory.new_authz = new_authzr_uri
Example #21
0
    async def connect(cls,
                      directory: str,
                      key: crypto.PrivateKey,
                      *,
                      loop: _T_AIO_LOOP = None) -> 'AsyncmeClient':
        """
        Connect to an ACME server.

        :param directory: The URI of the ACME server's directory.
        :param key: Private key used for interacting with the ACME server.
        :param loop: Event loop.

        :return: A new instance of ``AsyncmeClient`` which has been
         registered against the given ACME server, if the key has not already
         been registered.
        """

        # Determine the JWS 'alg' Type for the Given Key
        # RSA
        if key.algorithm is crypto.KeyAlgorithmType.RSA:
            if key.size / 8 == 256:
                alg = jose.jwa.RS256
            elif key.size / 8 == 384:
                alg = jose.jwa.RS384
            elif key.size / 8 == 512:
                alg = jose.jwa.RS512
            else:
                raise ValueError("RSA key size of {} "
                                 "is unsupported".format(key.size))

        # ECDSA
        elif key.algorithm is crypto.KeyAlgorithmType.ECDSA:
            if key.size == 256:
                alg = jose.jwa.ES256
            elif key.size == 384:
                alg = jose.jwa.ES384
            elif key.size == 521:
                alg = jose.jwa.ES512
            else:
                raise ValueError("ECDSA key size of {} "
                                 "is unsupported".format(key.size))

        # Other
        else:
            raise ValueError("Unsupported private key type, must "
                             "be RSA or ECDSA")

        # Convert Key
        password = os.urandom(255)
        acme_key = jose.JWKRSA(key=serialization.load_der_private_key(
            key.to_bytes(encoding=crypto.EncodingType.DER, password=password),
            password, default_backend()))

        # Get Async. Loop
        loop = loop or asyncio.get_event_loop()

        # Instantiate Underlying Client
        # (and make the initial connection to the ACME server)
        try:
            client = await ExecutorClient.connect(
                directory=directory,
                key=acme_key,
                alg=alg,
                net=ExtendedNetwork(
                    key=acme_key,
                    alg=alg,
                    user_agent='asyncme-python-{}'.format(ASYNCME_VERSION)),
                loop=loop)
        except TypeError:
            raise ValueError("Invalid ACME directory given")

        # Client Registration
        try:
            regr = await client.register()
        except errors.Error as e:

            # The upstream client currently has no way to "recover" a lost
            # registration resource, thus we must resort to the tactics below.

            if "key is already in use" in str(e):

                LOG.debug("Key already in use: attempting to recover "
                          "registration resource.")

                # (1) Send a Post in Attempt to Register the Client
                #     and Get a 409 Back in Response with Location
                #     Header set.

                post = functools.partial(client.net.post,
                                         client.directory['new-reg'],
                                         messages.NewRegistration(),
                                         checked=False)
                response = await loop.run_in_executor(None, post)
                assert response.status_code == 409

                # (2) Manually create a new Registration Resource and
                #     send an empty registration update.
                #     (per draft-ietf-acme-acme-03#section-6.2)

                regr = messages.RegistrationResource(
                    uri=response.headers.get("location"))

                # Empty Update
                regr = await client._send_recv_regr(
                    regr, body=messages.UpdateRegistration())

            else:
                raise RuntimeError(e)

        return cls(client, regr)
Example #22
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)
Example #23
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)
Example #24
0
import josepy as jose

from acme import client as acme_client
from acme import errors as acme_errors
from acme import messages

DIRECTORY = os.getenv('DIRECTORY', 'http://localhost:4000/directory')

if len(sys.argv) != 2:
    print("Usage: python deactivate.py private_key.pem")
    sys.exit(1)

data = open(sys.argv[1], "r").read()
key = jose.JWKRSA(
    key=serialization.load_pem_private_key(data, None, default_backend()))

net = acme_client.ClientNetwork(key,
                                verify_ssl=False,
                                user_agent="acme account deactivator")

client = acme_client.Client(DIRECTORY, key=key, net=net)
try:
    # We expect this to fail and give us a Conflict response with a Location
    # header pointing at the account's URL.
    client.register()
except acme_errors.ConflictError as e:
    location = e.location
if location is None:
    raise "Key was not previously registered (but now is)."
client.deactivate_registration(messages.RegistrationResource(uri=location))
Example #25
0
 def setUp(self):
     self.config = mock.MagicMock(config_dir="/etc/letsencrypt")
     reg = messages.Registration.from_data(email="*****@*****.**")
     reg = reg.update(recovery_token="ECCENTRIC INVISIBILITY RHINOCEROS")
     self.acc = mock.MagicMock(regr=messages.RegistrationResource(
         uri=None, new_authzr_uri=None, body=reg))
Example #26
0
def run_acme_reg_to_finish(domain, regr_uri, accnt_key, site_key, csr, tmp_chall_dict, directory_url):
    accnt_key = jose.JWKRSA(key=accnt_key)
    acme = client.Client(directory_url, accnt_key)
    msg = messages.RegistrationResource(uri=regr_uri)
    regr = acme.query_registration(msg)

    log.info('Auto-accepting TOS: %s from: %s', regr.terms_of_service, directory_url)
    acme.agree_to_tos(regr)

    authzr = acme.request_challenges(
        identifier=messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=domain))
    log.debug('Created auth client %s', authzr)

    def get_http_challenge(x, y):
        return y if isinstance(y.chall, challenges.HTTP01) else x

    challb = reduce(get_http_challenge, authzr.body.challenges, None)
    chall_tok = challb.chall.validation(accnt_key)

    v = chall_tok.split('.')[0]
    log.info('Exposing challenge on %s', v)
    tmp_chall_dict.set(v, ChallTok(chall_tok))

    test_path = 'http://localhost:8082{}'.format(challb.path)
    local_req = Request(test_path, headers={'Host': domain})
    log.debug('Testing local url path: %s', test_path)

    try:
        resp = urlopen(local_req)
        t = resp.read().decode('utf-8').strip()
        if t != chall_tok:
            raise ValueError
    except (IOError, ValueError):
        log.info('Resolving challenge locally failed. ACME request will fail. %s', test_path)
        raise

    cr = acme.answer_challenge(challb, challb.chall.response(accnt_key))
    log.debug('Acme CA responded to challenge request with: %s', cr)

    try:
        # Wrap this step and log the failure particularly here because this is
        # the expected point of failure for applications that are not reachable
        # from the public internet.
        cert_res, _ = acme.poll_and_request_issuance(jose.util.ComparableX509(csr), (authzr,))

        # NOTE pylint disabled due to spurious reporting. See docs:
        # https://letsencrypt.readthedocs.io/projects/acme/en/latest/api/jose/util.html#acme.jose.util.ComparableX509
        # pylint: disable=no-member
        cert_str = cert_res.body._dump(FILETYPE_PEM)
    except messages.Error as error:
        log.err("Failed in request issuance step %s", error)
        raise

    chain_certs = acme.fetch_chain(cert_res)

    # The chain certs returned by the LE CA will always have at least one
    # intermediate cert. Other certificate authorities that run ACME may
    # behave differently, but we aren't using them.
    chain_str = dump_certificate(FILETYPE_PEM, chain_certs[0])

    # pylint: disable=no-member
    expr_date = convert_asn1_date(cert_res.body.wrapped.get_notAfter())
    log.info('Retrieved cert using ACME that expires on %s', expr_date)

    return cert_str, chain_str
Example #27
0
 def setUp(self):
     self.config = mock.MagicMock(config_dir="/etc/letsencrypt")
     reg = messages.Registration.from_data(email="*****@*****.**")
     self.acc = mock.MagicMock(regr=messages.RegistrationResource(
         uri=None, new_authzr_uri=None, body=reg))
Example #28
0
def wildcard_request(cn, account):
    def dns_check_ns1():
        recieved_data_dup = []
        recieved_data = []

        ns1_resolver = dns.resolver.Resolver()
        #ns1_resolver.nameservers = ['130.193.8.82','2a03:b780::1:1']
        ns1_resolver.nameservers = ['173.245.58.51']

        for data in validation_data:
            domainname = data[1]
            #challenge = data[0]
            answers = ns1_resolver.query(domainname, 'txt')
            for rdata in answers:
                recieved_data_dup.append(
                    [str(rdata).replace('"', ''), domainname])

        #Deduplication of ns records (in case of more cnames)
        for i in recieved_data_dup:
            if i not in recieved_data:
                recieved_data.append(i)

        # print sorted(recieved_data)
        # print sorted(validation_data)
        if sorted(validation_data) == sorted(recieved_data):
            return True
        else:
            return False

    #Check if CN is valid domain
    domain_regex = re.compile(
        "^([a-zA-Z0-9]([\-a-zA-Z0-9]{0,61}[a-zA-Z0-9])?\.)*([a-zA-Z0-9]([\-a-zA-Z0-9]{0,61}[a-zA-Z0-9])+\.)([a-zA-Z0-9]+([\-a-zA-Z0-9]{0,61}[a-zA-Z])+)$"
    )
    if not domain_regex.match(cn):
        print 'First argument is not valid CN'
        sys.exit(1)

    #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 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)

    #Generate private key for certificate
    pkey = OpenSSL.crypto.PKey()
    pkey.generate_key(OpenSSL.crypto.TYPE_RSA, BITS)

    #Serialize key for output
    pkey_printable = OpenSSL.crypto.dump_privatekey(
        OpenSSL.crypto.FILETYPE_PEM, pkey, cipher=None, passphrase=None)

    #Compose request for acme
    req = crypto_util.make_csr(
        OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, pkey),
        [cn, '*.' + cn])

    #begin order
    orderr = acme.new_order(req)

    validation_data = []

    for authr in orderr.authorizations:
        for chalr in authr.body.challenges:
            if type(chalr.chall) == type(challenges.DNS01()):
                validation_data.append([
                    str(chalr.chall.validation(key)),
                    chalr.chall.validation_domain_name(cn)
                ])
    #print validation_data
    #Now, call DNS writing function to apply challenges
    dns_apply(cn, validation_data)

    #Check if DNS is valid on our server
    sys.stdin.readline()  #DEBUG: wait for manual DNS input
    limiter = 2
    while not dns_check_ns1():
        if limiter != 0:
            print "DNS records are not correct, trying again in few seconds"
            limiter = limiter - 1
            time.sleep(5)
        else:
            print "DNS are not correct even after several tries. Aborting"
            sys.exit(1)

    for authr in orderr.authorizations:
        for chalr in authr.body.challenges:
            if type(chalr.chall) == type(challenges.DNS01()):
                try:
                    acme.answer_challenge(chalr, challenges.DNS01Response())
                except:
                    print chalr.chall.encode(
                        'token'
                    ) + " already answered (challenge failed, you have to generate new one)"

    #After filling DNS and waiting for propagation, finalize order
    try:
        res = acme.poll_and_finalize(orderr)
    finally:
        dns_remove(cn)
    #logging.info(res)

    cert = x509.load_pem_x509_certificate(str(res.fullchain_pem),
                                          default_backend())

    output_data = {
        'wildcard': {
            'cn': cn,
            'private_key': str(pkey_printable),
            'certificate': str(res.fullchain_pem),
            'expiration': cert.not_valid_after.strftime(
                "%x %X"
            )  #Locale-specific time+date representation. Edit to your need
        }
    }

    print json.dumps(output_data)