Ejemplo n.º 1
0
    def test_obtain_certificate_from_csr(self, unused_mock_get_utility,
                                         mock_logger):
        self._mock_obtain_certificate()
        test_csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        auth_handler = self.client.auth_handler

        authzr = auth_handler.get_authorizations(self.eg_domains, False)
        self.assertEqual(
            (mock.sentinel.certr, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(self.eg_domains,
                                                    test_csr,
                                                    authzr=authzr))
        # and that the cert was obtained correctly
        self._check_obtain_certificate()

        # Test for authzr=None
        self.assertEqual(
            (mock.sentinel.certr, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(self.eg_domains,
                                                    test_csr,
                                                    authzr=None))
        auth_handler.get_authorizations.assert_called_with(self.eg_domains)

        # Test for no auth_handler
        self.client.auth_handler = None
        self.assertRaises(errors.Error,
                          self.client.obtain_certificate_from_csr,
                          self.eg_domains, test_csr)
        mock_logger.warning.assert_called_once_with(mock.ANY)
Ejemplo n.º 2
0
    def test_obtain_certificate(self, unused_mock_get_utility,
                                mock_crypto_util):
        self._mock_obtain_certificate()

        csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        mock_crypto_util.init_save_csr.return_value = csr
        mock_crypto_util.init_save_key.return_value = mock.sentinel.key
        domains = ["example.com", "www.example.com"]

        # return_value is essentially set to (None, None) in
        # _mock_obtain_certificate(), which breaks this test.
        # Thus fixed by the next line.

        authzr = []

        # domain ordering should not be affected by authorization order
        for domain in reversed(domains):
            authzr.append(
                mock.MagicMock(body=mock.MagicMock(identifier=mock.MagicMock(
                    value=domain))))

        self.client.auth_handler.get_authorizations.return_value = authzr

        self.assertEqual(
            self.client.obtain_certificate(domains),
            (mock.sentinel.certr, mock.sentinel.chain, mock.sentinel.key, csr))

        mock_crypto_util.init_save_key.assert_called_once_with(
            self.config.rsa_key_size, self.config.key_dir)
        mock_crypto_util.init_save_csr.assert_called_once_with(
            mock.sentinel.key, domains, self.config.csr_dir)
        self._check_obtain_certificate()
Ejemplo n.º 3
0
def init_save_csr(privkey, names, path):
    """Initialize a CSR with the given private key.

    :param privkey: Key to include in the CSR
    :type privkey: :class:`certbot.util.Key`

    :param set names: `str` names to include in the CSR

    :param str path: Certificate save directory.

    :returns: CSR
    :rtype: :class:`certbot.util.CSR`

    """
    config = zope.component.getUtility(interfaces.IConfig)

    csr_pem = acme_crypto_util.make_csr(privkey.pem,
                                        names,
                                        must_staple=config.must_staple)

    # Save CSR
    util.make_or_verify_dir(path, 0o755, compat.os_geteuid(),
                            config.strict_permissions)
    csr_f, csr_filename = util.unique_file(
        os.path.join(path, "csr-certbot.pem"), 0o644, "wb")
    with csr_f:
        csr_f.write(csr_pem)
    logger.debug("Creating CSR: %s", csr_filename)

    return util.CSR(csr_filename, csr_pem, "pem")
Ejemplo n.º 4
0
def import_csr_file(csrfile, data):
    """Import a CSR file, which can be either PEM or DER.

    :param str csrfile: CSR filename
    :param str data: contents of the CSR file

    :returns: (`crypto.FILETYPE_PEM`,
               util.CSR object representing the CSR,
               list of domains requested in the CSR)
    :rtype: tuple

    """
    PEM = crypto.FILETYPE_PEM
    load = crypto.load_certificate_request
    try:
        # Try to parse as DER first, then fall back to PEM.
        csr = load(crypto.FILETYPE_ASN1, data)
    except crypto.Error:
        try:
            csr = load(PEM, data)
        except crypto.Error:
            raise errors.Error("Failed to parse CSR file: {0}".format(csrfile))

    domains = _get_names_from_loaded_cert_or_req(csr)
    # Internally we always use PEM, so re-encode as PEM before returning.
    data_pem = crypto.dump_certificate_request(PEM, csr)
    return PEM, util.CSR(file=csrfile, data=data_pem, form="pem"), domains
Ejemplo n.º 5
0
def import_csr_file(csrfile, data):
    """Import a CSR file, which can be either PEM or DER.

    :param str csrfile: CSR filename
    :param str data: contents of the CSR file

    :returns: (`OpenSSL.crypto.FILETYPE_PEM` or `OpenSSL.crypto.FILETYPE_ASN1`,
               util.CSR object representing the CSR,
               list of domains requested in the CSR)
    :rtype: tuple

    """
    for form, typ in (
        (
            "der",
            OpenSSL.crypto.FILETYPE_ASN1,
        ),
        (
            "pem",
            OpenSSL.crypto.FILETYPE_PEM,
        ),
    ):
        try:
            domains = get_names_from_csr(data, typ)
        except OpenSSL.crypto.Error:
            logger.debug("CSR parse error (form=%s, typ=%s):", form, typ)
            logger.debug(traceback.format_exc())
            continue
        return typ, util.CSR(file=csrfile, data=data, form=form), domains
    raise errors.Error("Failed to parse CSR file: {0}".format(csrfile))
Ejemplo n.º 6
0
    def test_obtain_certificate_from_csr(self, unused_mock_get_utility,
                                         mock_logger, mock_crypto_util):
        self._mock_obtain_certificate()
        test_csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        auth_handler = self.client.auth_handler
        self._set_mock_from_fullchain(
            mock_crypto_util.cert_and_chain_from_fullchain)

        orderr = self.acme.new_order(test_csr.data)
        auth_handler.handle_authorizations(orderr, False)
        self.assertEqual(
            (mock.sentinel.cert, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(test_csr, orderr=orderr))
        # and that the cert was obtained correctly
        self._check_obtain_certificate()

        # Test for orderr=None
        self.assertEqual((mock.sentinel.cert, mock.sentinel.chain),
                         self.client.obtain_certificate_from_csr(test_csr,
                                                                 orderr=None))
        auth_handler.handle_authorizations.assert_called_with(
            self.eg_order, False)

        # Test for no auth_handler
        self.client.auth_handler = None
        self.assertRaises(errors.Error,
                          self.client.obtain_certificate_from_csr, test_csr)
        mock_logger.warning.assert_called_once_with(mock.ANY)
Ejemplo n.º 7
0
    def test_obtain_certificate_dry_run(self, mock_acme_crypto, mock_crypto):
        csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        mock_acme_crypto.make_csr.return_value = CSR_SAN
        mock_crypto.make_key.return_value = mock.sentinel.key_pem
        key = util.Key(file=None, pem=mock.sentinel.key_pem)
        self._set_mock_from_fullchain(
            mock_crypto.cert_and_chain_from_fullchain)

        self.client.config.dry_run = True
        self._test_obtain_certificate_common(key, csr)

        mock_crypto.make_key.assert_called_once_with({
            "type":
            self.config.key_type,
            "curve":
            self.config.elliptic_curve,
            "size":
            self.config.rsa_key_size,
        })
        mock_acme_crypto.make_csr.assert_called_once_with(
            mock.sentinel.key_pem, self.eg_domains, self.config.must_staple)
        mock_crypto.init_save_key.assert_not_called()
        mock_crypto.init_save_csr.assert_not_called()
        self.assertEqual(mock_crypto.cert_and_chain_from_fullchain.call_count,
                         1)
Ejemplo n.º 8
0
    def test_der_csr(self):
        csrfile = test_util.vector_path('csr.der')
        data = test_util.load_vector('csr.der')

        self.assertEqual((
            OpenSSL.crypto.FILETYPE_ASN1,
            util.CSR(file=csrfile, data=data, form="der"),
            ["example.com"],
        ), self._call(csrfile, data))
Ejemplo n.º 9
0
    def test_pem_csr(self):
        csrfile = test_util.vector_path('csr_512.pem')
        data = test_util.load_vector('csr_512.pem')

        self.assertEqual((
            OpenSSL.crypto.FILETYPE_PEM,
            util.CSR(file=csrfile, data=data, form="pem"),
            ["Example.com"],
        ), self._call(csrfile, data))
Ejemplo n.º 10
0
    def test_obtain_certificate_from_csr(self, mock_logger, mock_crypto_util):
        self._mock_obtain_certificate()
        test_csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        auth_handler = self.client.auth_handler
        self._set_mock_from_fullchain(
            mock_crypto_util.cert_and_chain_from_fullchain)

        orderr = self.acme.new_order(test_csr.data)
        auth_handler.handle_authorizations(orderr, self.config, False)
        self.assertEqual(
            (mock.sentinel.cert, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(test_csr, orderr=orderr))
        mock_crypto_util.find_chain_with_issuer.assert_not_called()
        # and that the cert was obtained correctly
        self._check_obtain_certificate()

        # Test that --preferred-chain results in chain selection
        self.config.preferred_chain = "some issuer"
        self.assertEqual(
            (mock.sentinel.cert, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(test_csr, orderr=orderr))
        mock_crypto_util.find_chain_with_issuer.assert_called_once_with(
            [orderr.fullchain_pem] + orderr.alternative_fullchains_pem,
            "some issuer", True)
        self.config.preferred_chain = None

        # Test for default issuance_timeout
        expected_deadline = \
            datetime.datetime.now() + datetime.timedelta(
                seconds=constants.CLI_DEFAULTS["issuance_timeout"])
        self.client.obtain_certificate_from_csr(test_csr, orderr=orderr)
        ((_, deadline), _) = self.client.acme.finalize_order.call_args
        self.assertTrue(
            abs(expected_deadline - deadline) <= datetime.timedelta(seconds=1))

        # Test for specific issuance_timeout (300 seconds)
        expected_deadline = \
            datetime.datetime.now() + datetime.timedelta(seconds=300)
        self.config.issuance_timeout = 300
        self.client.obtain_certificate_from_csr(test_csr, orderr=orderr)
        ((_, deadline), _) = self.client.acme.finalize_order.call_args
        self.assertTrue(
            abs(expected_deadline - deadline) <= datetime.timedelta(seconds=1))

        # Test for orderr=None
        self.assertEqual((mock.sentinel.cert, mock.sentinel.chain),
                         self.client.obtain_certificate_from_csr(test_csr,
                                                                 orderr=None))
        auth_handler.handle_authorizations.assert_called_with(
            self.eg_order, self.config, False)

        # Test for no auth_handler
        self.client.auth_handler = None
        self.assertRaises(errors.Error,
                          self.client.obtain_certificate_from_csr, test_csr)
        mock_logger.error.assert_called_once_with(mock.ANY)
Ejemplo n.º 11
0
def init_save_csr(privkey, email, config, usage):
    path = config.csr_dir
    csr_pem = make(privkey.pem, email, usage)
    util.make_or_verify_dir(path, 0o755, config.strict_permissions)
    csr_f, csr_filename = util.unique_file(
        os.path.join(path, 'csr-certbot.pem'), 0o644, "wb")
    with csr_f:
        csr_f.write(csr_pem)
    logger.debug("Creating CSR: %s", csr_filename)
    return util.CSR(csr_filename, csr_pem, "pem")
Ejemplo n.º 12
0
    def test_obtain_certificate_from_csr_retry_failed(self, mock_get_utility):
        self._mock_obtain_certificate()
        self.acme.fetch_chain.side_effect = acme_errors.Error
        test_csr = util.CSR(form="der", file=None, data=CSR_SAN)

        orderr = self.acme.new_order(test_csr.data)
        self.assertRaises(acme_errors.Error,
                          self.client.obtain_certificate_from_csr,
                          test_csr,
                          orderr=orderr)
        self.assertEqual(1, mock_get_utility().notification.call_count)
Ejemplo n.º 13
0
    def test_obtain_certificate(self, mock_crypto_util):
        csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        mock_crypto_util.init_save_csr.return_value = csr
        mock_crypto_util.init_save_key.return_value = mock.sentinel.key

        self._test_obtain_certificate_common(mock.sentinel.key, csr)

        mock_crypto_util.init_save_key.assert_called_once_with(
            self.config.rsa_key_size, self.config.key_dir)
        mock_crypto_util.init_save_csr.assert_called_once_with(
            mock.sentinel.key, self.eg_domains, self.config.csr_dir)
Ejemplo n.º 14
0
    def test_obtain_certificate_partial_success(self, mock_remove,
                                                mock_crypto_util):
        csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
        key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
        mock_crypto_util.init_save_csr.return_value = csr
        mock_crypto_util.init_save_key.return_value = key
        self._set_mock_from_fullchain(
            mock_crypto_util.cert_and_chain_from_fullchain)

        authzr = self._authzr_from_domains(["example.com"])
        self.config.allow_subset_of_names = True
        self._test_obtain_certificate_common(key,
                                             csr,
                                             authzr_ret=authzr,
                                             auth_count=2)

        self.assertEqual(mock_crypto_util.init_save_key.call_count, 2)
        self.assertEqual(mock_crypto_util.init_save_csr.call_count, 2)
        self.assertEqual(mock_remove.call_count, 2)
        self.assertEqual(
            mock_crypto_util.cert_and_chain_from_fullchain.call_count, 1)
Ejemplo n.º 15
0
    def test_obtain_certificate_from_csr_retry_succeeded(
            self, mock_get_utility):
        self._mock_obtain_certificate()
        self.acme.fetch_chain.side_effect = [
            acme_errors.Error, mock.sentinel.chain
        ]
        test_csr = util.CSR(form="der", file=None, data=CSR_SAN)

        orderr = self.acme.new_order(test_csr.data)
        self.assertEqual(
            (mock.sentinel.certr, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(test_csr, orderr=orderr))
        self.assertEqual(1, mock_get_utility().notification.call_count)
Ejemplo n.º 16
0
    def test_obtain_certificate_from_csr_retry_failed(self, mock_get_utility):
        self._mock_obtain_certificate()
        self.acme.fetch_chain.side_effect = acme_errors.Error
        test_csr = util.CSR(form="der", file=None, data=CSR_SAN)
        auth_handler = self.client.auth_handler

        authzr = auth_handler.get_authorizations(self.eg_domains, False)
        self.assertRaises(acme_errors.Error,
                          self.client.obtain_certificate_from_csr,
                          self.eg_domains,
                          test_csr,
                          authzr=authzr)
        self.assertEqual(1, mock_get_utility().notification.call_count)
Ejemplo n.º 17
0
    def test_obtain_certificate_get_order_rejected_identifier_no_subproblems(self, mock_crypto_util):
        from acme import messages
        csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
        key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
        mock_crypto_util.generate_csr.return_value = csr
        mock_crypto_util.generate_key.return_value = key
        self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain)

        self._mock_obtain_certificate()
        authzr = self._authzr_from_domains(self.eg_domains)
        self.eg_order.authorizations = authzr
        self.client.auth_handler.handle_authorizations.return_value = authzr

        error = messages.Error.with_code('caa', detail='foo', title='title')
        self.client.acme.new_order.side_effect = error

        self.config.allow_subset_of_names = True

        self.assertRaises(messages.Error, self.client.obtain_certificate, self.eg_domains)
        self.assertEqual(self.client.auth_handler.handle_authorizations.call_count, 0)
        self.assertEqual(self.acme.new_order.call_count, 1)
        self.assertEqual(mock_crypto_util.generate_key.call_count, 1)
        self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 0)
Ejemplo n.º 18
0
    def test_obtain_certificate_dry_run(self, mock_acme_crypto, mock_crypto):
        csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        mock_acme_crypto.make_csr.return_value = CSR_SAN
        mock_crypto.make_key.return_value = mock.sentinel.key_pem
        key = util.Key(file=None, pem=mock.sentinel.key_pem)

        self.client.config.dry_run = True
        self._test_obtain_certificate_common(key, csr)

        mock_crypto.make_key.assert_called_once_with(self.config.rsa_key_size)
        mock_acme_crypto.make_csr.assert_called_once_with(
            mock.sentinel.key_pem, self.eg_domains, self.config.must_staple)
        mock_crypto.init_save_key.assert_not_called()
        mock_crypto.init_save_csr.assert_not_called()
Ejemplo n.º 19
0
    def test_obtain_certificate_get_order_partial_success(self, mock_remove, mock_crypto_util):
        from acme import messages
        csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN)
        key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN)
        mock_crypto_util.generate_csr.return_value = csr
        mock_crypto_util.generate_key.return_value = key
        self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain)

        self._mock_obtain_certificate()
        authzr = self._authzr_from_domains(self.eg_domains)
        self.eg_order.authorizations = authzr
        self.client.auth_handler.handle_authorizations.return_value = authzr

        identifier = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='example.com')
        subproblem = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier)
        error_with_subproblems = messages.Error.with_code('malformed', detail='foo', title='title', subproblems=[subproblem])
        self.client.acme.new_order.side_effect = [error_with_subproblems, mock.DEFAULT]

        self.config.allow_subset_of_names = True

        with test_util.patch_display_util():
            result = self.client.obtain_certificate(self.eg_domains)

        self.assertEqual(
            result,
            (mock.sentinel.cert, mock.sentinel.chain, key, csr))
        self.assertEqual(self.client.auth_handler.handle_authorizations.call_count, 1)
        self.assertEqual(self.acme.new_order.call_count, 2)

        successful_domains = [d for d in self.eg_domains if d != 'example.com']
        self.assertEqual(mock_crypto_util.generate_key.call_count, 2)
        mock_crypto_util.generate_csr.assert_has_calls([
            mock.call(key, self.eg_domains, self.config.csr_dir, self.config.must_staple, self.config.strict_permissions),
            mock.call(key, successful_domains, self.config.csr_dir, self.config.must_staple, self.config.strict_permissions)])
        self.assertEqual(mock_remove.call_count, 2)
        self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 1)
Ejemplo n.º 20
0
    def obtain_certificate(self, domains):
        """Obtains a certificate from the ACME server.

        `.register` must be called before `.obtain_certificate`

        :param list domains: domains to get a certificate

        :returns: certificate as PEM string, chain as PEM string,
            newly generated private key (`.util.Key`), and DER-encoded
            Certificate Signing Request (`.util.CSR`).
        :rtype: tuple

        """
        # Create CSR from names
        if self.config.dry_run:
            key = util.Key(file=None,
                           pem=crypto_util.make_key(self.config.rsa_key_size))
            csr = util.CSR(file=None,
                           form="pem",
                           data=acme_crypto_util.make_csr(
                               key.pem, domains, self.config.must_staple))
        else:
            key = crypto_util.init_save_key(self.config.rsa_key_size,
                                            self.config.key_dir)
            csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir)

        orderr = self._get_order_and_authorizations(
            csr.data, self.config.allow_subset_of_names)
        authzr = orderr.authorizations
        auth_domains = set(a.body.identifier.value for a in authzr)
        successful_domains = [d for d in domains if d in auth_domains]

        # allow_subset_of_names is currently disabled for wildcard
        # certificates. The reason for this and checking allow_subset_of_names
        # below is because successful_domains == domains is never true if
        # domains contains a wildcard because the ACME spec forbids identifiers
        # in authzs from containing a wildcard character.
        if self.config.allow_subset_of_names and successful_domains != domains:
            if not self.config.dry_run:
                os.remove(key.file)
                os.remove(csr.file)
            return self.obtain_certificate(successful_domains)
        else:
            cert, chain = self.obtain_certificate_from_csr(csr, orderr)

            return cert, chain, key, csr
Ejemplo n.º 21
0
    def test_obtain_certificate_from_csr(self, unused_mock_get_utility,
                                         mock_logger, mock_crypto_util):
        self._mock_obtain_certificate()
        test_csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        auth_handler = self.client.auth_handler
        self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain)

        orderr = self.acme.new_order(test_csr.data)
        auth_handler.handle_authorizations(orderr, False)
        self.assertEqual(
            (mock.sentinel.cert, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(
                test_csr,
                orderr=orderr))
        mock_crypto_util.find_chain_with_issuer.assert_not_called()
        # and that the cert was obtained correctly
        self._check_obtain_certificate()

        # Test that --preferred-chain results in chain selection
        self.config.preferred_chain = "some issuer"
        self.assertEqual(
            (mock.sentinel.cert, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(
                test_csr,
                orderr=orderr))
        mock_crypto_util.find_chain_with_issuer.assert_called_once_with(
            [orderr.fullchain_pem] + orderr.alternative_fullchains_pem,
            "some issuer", True)
        self.config.preferred_chain = None

        # Test for orderr=None
        self.assertEqual(
            (mock.sentinel.cert, mock.sentinel.chain),
            self.client.obtain_certificate_from_csr(
                test_csr,
                orderr=None))
        auth_handler.handle_authorizations.assert_called_with(self.eg_order, False)

        # Test for no auth_handler
        self.client.auth_handler = None
        self.assertRaises(
            errors.Error,
            self.client.obtain_certificate_from_csr,
            test_csr)
        mock_logger.warning.assert_called_once_with(mock.ANY)
Ejemplo n.º 22
0
    def test_obtain_certificate_dry_run_authz_deactivations_failed(
            self, mock_acme_crypto, mock_crypto, mock_log):
        from acme import messages
        csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        mock_acme_crypto.make_csr.return_value = CSR_SAN
        mock_crypto.make_key.return_value = mock.sentinel.key_pem
        key = util.Key(file=None, pem=mock.sentinel.key_pem)
        self._set_mock_from_fullchain(
            mock_crypto.cert_and_chain_from_fullchain)

        self._mock_obtain_certificate()
        self.client.config.dry_run = True

        # Two authzs that are already valid and should get deactivated (dry run)
        authzrs = self._authzr_from_domains(["example.com", "www.example.com"])
        for authzr in authzrs:
            authzr.body.status = messages.STATUS_VALID

        # One deactivation succeeds, one fails
        auth_handler = self.client.auth_handler
        auth_handler.deactivate_valid_authorizations.return_value = ([
            authzrs[0]
        ], [authzrs[1]])

        # Certificate should get issued despite one failed deactivation
        self.eg_order.authorizations = authzrs
        self.client.auth_handler.handle_authorizations.return_value = authzrs
        with test_util.patch_get_utility():
            result = self.client.obtain_certificate(self.eg_domains)
        self.assertEqual(result,
                         (mock.sentinel.cert, mock.sentinel.chain, key, csr))
        self._check_obtain_certificate(1)

        # Deactivation success/failure should have been handled properly
        self.assertEqual(
            auth_handler.deactivate_valid_authorizations.call_count, 1,
            "Deactivate authorizations should be called")
        self.assertEqual(
            self.acme.new_order.call_count, 2,
            "Order should be recreated due to successfully deactivated authorizations"
        )
        mock_log.warning.assert_called_with(
            "Certbot was unable to obtain fresh authorizations for"
            " every domain. The dry run will continue, but results"
            " may not be accurate.")
Ejemplo n.º 23
0
    def test_obtain_certificate(self, mock_crypto_util):
        csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        mock_crypto_util.init_save_csr.return_value = csr
        mock_crypto_util.init_save_key.return_value = mock.sentinel.key
        self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain)

        self._test_obtain_certificate_common(mock.sentinel.key, csr)

        mock_crypto_util.init_save_key.assert_called_once_with(
            key_size=self.config.rsa_key_size,
            key_dir=self.config.key_dir,
            key_type=self.config.key_type,
            elliptic_curve=None,  # elliptic curve is not set
        )
        mock_crypto_util.init_save_csr.assert_called_once_with(
            mock.sentinel.key, self.eg_domains, self.config.csr_dir)
        mock_crypto_util.cert_and_chain_from_fullchain.assert_called_once_with(
            self.eg_order.fullchain_pem)
Ejemplo n.º 24
0
def validate_key_csr(privkey, csr=None):
    """Validate Key and CSR files.

    Verifies that the client key and csr arguments are valid and correspond to
    one another. This does not currently check the names in the CSR due to
    the inability to read SANs from CSRs in python crypto libraries.

    If csr is left as None, only the key will be validated.

    :param privkey: Key associated with CSR
    :type privkey: :class:`certbot.util.Key`

    :param .util.CSR csr: CSR

    :raises .errors.Error: when validation fails

    """
    # TODO: Handle all of these problems appropriately
    # The client can eventually do things like prompt the user
    # and allow the user to take more appropriate actions

    # Key must be readable and valid.
    if privkey.pem and not crypto_util.valid_privkey(privkey.pem):
        raise errors.Error("The provided key is not a valid key")

    if csr:
        if csr.form == "der":
            csr_obj = OpenSSL.crypto.load_certificate_request(
                OpenSSL.crypto.FILETYPE_ASN1, csr.data)
            cert_buffer = OpenSSL.crypto.dump_certificate_request(
                OpenSSL.crypto.FILETYPE_PEM, csr_obj
            )
            csr = util.CSR(csr.file, cert_buffer, "pem")

        # If CSR is provided, it must be readable and valid.
        if csr.data and not crypto_util.valid_csr(csr.data):
            raise errors.Error("The provided CSR is not a valid CSR")

        # If both CSR and key are provided, the key must be the same key used
        # in the CSR.
        if csr.data and privkey.pem:
            if not crypto_util.csr_matches_pubkey(
                    csr.data, privkey.pem):
                raise errors.Error("The key and CSR do not match")
Ejemplo n.º 25
0
    def test_obtain_certificate(self, mock_crypto_util):
        csr = util.CSR(form="pem", file=None, data=CSR_SAN)
        mock_crypto_util.generate_csr.return_value = csr
        mock_crypto_util.generate_key.return_value = mock.sentinel.key
        self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain)

        self._test_obtain_certificate_common(mock.sentinel.key, csr)

        mock_crypto_util.generate_key.assert_called_once_with(
            key_size=self.config.rsa_key_size,
            key_dir=self.config.key_dir,
            key_type=self.config.key_type,
            elliptic_curve="secp256r1",
            strict_permissions=True,
        )
        mock_crypto_util.generate_csr.assert_called_once_with(
            mock.sentinel.key, self.eg_domains, self.config.csr_dir, False, True)
        mock_crypto_util.cert_and_chain_from_fullchain.assert_called_once_with(
            self.eg_order.fullchain_pem)
Ejemplo n.º 26
0
    def obtain_certificate(self, domains):
        """Obtains a certificate from the ACME server.

        `.register` must be called before `.obtain_certificate`

        :param list domains: domains to get a certificate

        :returns: certificate as PEM string, chain as PEM string,
            newly generated private key (`.util.Key`), and DER-encoded
            Certificate Signing Request (`.util.CSR`).
        :rtype: tuple

        """
        # Create CSR from names
        if self.config.dry_run:
            key = util.Key(file=None,
                           pem=crypto_util.make_key(self.config.rsa_key_size))
            csr = util.CSR(file=None,
                           form="pem",
                           data=acme_crypto_util.make_csr(
                               key.pem, domains, self.config.must_staple))
        else:
            key = crypto_util.init_save_key(self.config.rsa_key_size,
                                            self.config.key_dir)
            csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir)

        orderr = self.acme.new_order(csr.data)
        authzr = self.auth_handler.handle_authorizations(
            orderr, self.config.allow_subset_of_names)
        orderr = orderr.update(authorizations=authzr)
        auth_domains = set(a.body.identifier.value for a in authzr)
        successful_domains = [d for d in domains if d in auth_domains]

        if successful_domains != domains:
            if not self.config.dry_run:
                os.remove(key.file)
                os.remove(csr.file)
            return self.obtain_certificate(successful_domains)
        else:
            cert, chain = self.obtain_certificate_from_csr(csr, orderr)

            return cert, chain, key, csr
Ejemplo n.º 27
0
    def obtain_certificate(self, domains):
        """Obtains a certificate from the ACME server.

        `.register` must be called before `.obtain_certificate`

        :param list domains: domains to get a certificate

        :returns: `.CertificateResource`, certificate chain (as
            returned by `.fetch_chain`), and newly generated private key
            (`.util.Key`) and DER-encoded Certificate Signing Request
            (`.util.CSR`).
        :rtype: tuple

        """
        authzr = self.auth_handler.get_authorizations(
            domains, self.config.allow_subset_of_names)

        auth_domains = set(a.body.identifier.value for a in authzr)
        domains = [d for d in domains if d in auth_domains]

        # Create CSR from names
        if self.config.dry_run:
            key = util.Key(file=None,
                           pem=crypto_util.make_key(self.config.rsa_key_size))
            csr = util.CSR(file=None,
                           form="pem",
                           data=acme_crypto_util.make_csr(
                               key.pem, domains, self.config.must_staple))
        else:
            key = crypto_util.init_save_key(self.config.rsa_key_size,
                                            self.config.key_dir)
            csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir)

        certr, chain = self.obtain_certificate_from_csr(domains,
                                                        csr,
                                                        authzr=authzr)

        return certr, chain, key, csr
Ejemplo n.º 28
0
    def obtain_certificate(self, domains, old_keypath=None):
        """Obtains a certificate from the ACME server.

        `.register` must be called before `.obtain_certificate`

        :param list domains: domains to get a certificate

        :returns: certificate as PEM string, chain as PEM string,
            newly generated private key (`.util.Key`), and DER-encoded
            Certificate Signing Request (`.util.CSR`).
        :rtype: tuple

        """
        # We need to determine the key path, key PEM data, CSR path,
        # and CSR PEM data.  For a dry run, the paths are None because
        # they aren't permanently saved to disk.  For a lineage with
        # --reuse-key, the key path and PEM data are derived from an
        # existing file.

        if old_keypath is not None:
            # We've been asked to reuse a specific existing private key.
            # Therefore, we'll read it now and not generate a new one in
            # either case below.
            #
            # We read in bytes here because the type of `key.pem`
            # created below is also bytes.
            with open(old_keypath, "rb") as f:
                keypath = old_keypath
                keypem = f.read()
            key: Optional[util.Key] = util.Key(file=keypath, pem=keypem)
            logger.info("Reusing existing private key from %s.", old_keypath)
        else:
            # The key is set to None here but will be created below.
            key = None

        key_size = self.config.rsa_key_size
        elliptic_curve = "secp256r1"

        # key-type defaults to a list, but we are only handling 1 currently
        if isinstance(self.config.key_type, list):
            self.config.key_type = self.config.key_type[0]
        if self.config.elliptic_curve and self.config.key_type == 'ecdsa':
            elliptic_curve = self.config.elliptic_curve
            self.config.auth_chain_path = "./chain-ecdsa.pem"
            self.config.auth_cert_path = "./cert-ecdsa.pem"
            self.config.key_path = "./key-ecdsa.pem"
        elif self.config.rsa_key_size and self.config.key_type.lower(
        ) == 'rsa':
            key_size = self.config.rsa_key_size

        # Create CSR from names
        if self.config.dry_run:
            key = key or util.Key(
                file=None,
                pem=crypto_util.make_key(
                    bits=key_size,
                    elliptic_curve=elliptic_curve,
                    key_type=self.config.key_type,
                ),
            )
            csr = util.CSR(file=None,
                           form="pem",
                           data=acme_crypto_util.make_csr(
                               key.pem, domains, self.config.must_staple))
        else:
            key = key or crypto_util.generate_key(
                key_size=key_size,
                key_dir=self.config.key_dir,
                key_type=self.config.key_type,
                elliptic_curve=elliptic_curve,
                strict_permissions=self.config.strict_permissions,
            )
            csr = crypto_util.generate_csr(key, domains, self.config.csr_dir,
                                           self.config.must_staple,
                                           self.config.strict_permissions)

        orderr = self._get_order_and_authorizations(
            csr.data, self.config.allow_subset_of_names)
        authzr = orderr.authorizations
        auth_domains = set(a.body.identifier.value for a in authzr)
        successful_domains = [d for d in domains if d in auth_domains]

        # allow_subset_of_names is currently disabled for wildcard
        # certificates. The reason for this and checking allow_subset_of_names
        # below is because successful_domains == domains is never true if
        # domains contains a wildcard because the ACME spec forbids identifiers
        # in authzs from containing a wildcard character.
        if self.config.allow_subset_of_names and successful_domains != domains:
            if not self.config.dry_run:
                os.remove(key.file)
                os.remove(csr.file)
            return self.obtain_certificate(successful_domains)
        else:
            cert, chain = self.obtain_certificate_from_csr(csr, orderr)
            return cert, chain, key, csr