Example #1
0
    def test_obtain_certificate_get_order_no_retryable_domains(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

        identifier1 = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='example.com')
        identifier2 = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='www.example.com')
        subproblem1 = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier1)
        subproblem2 = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier2)
        error_with_subproblems = messages.Error.with_code('malformed', detail='foo', title='title', subproblems=[subproblem1, subproblem2])
        self.client.acme.new_order.side_effect = error_with_subproblems

        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)
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.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')

        # 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)
Example #3
0
    def new_order(self, csr_pem):
        """Request a new Order object from the server.

        :param str csr_pem: A CSR in PEM format.

        :returns: The newly created order.
        :rtype: OrderResource
        """
        csr = OpenSSL.crypto.load_certificate_request(
            OpenSSL.crypto.FILETYPE_PEM, csr_pem)
        # pylint: disable=protected-access
        dnsNames = crypto_util._pyopenssl_cert_or_req_all_names(csr)
        ipNames = crypto_util._pyopenssl_cert_or_req_san_ip(csr)
        # ipNames is now []string
        identifiers = []
        for name in dnsNames:
            identifiers.append(
                messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=name))
        for ips in ipNames:
            identifiers.append(
                messages.Identifier(typ=messages.IDENTIFIER_IP, value=ips))
        order = messages.NewOrder(identifiers=identifiers)
        response = self._post(self.directory['newOrder'], order)
        body = messages.Order.from_json(response.json())
        authorizations = []
        # pylint has trouble understanding our josepy based objects which use
        # things like custom metaclass logic. body.authorizations should be a
        # list of strings containing URLs so let's disable this check here.
        for url in body.authorizations:  # pylint: disable=not-an-iterable
            authorizations.append(
                self._authzr_from_response(self._post_as_get(url), uri=url))
        return messages.OrderResource(body=body,
                                      uri=response.headers.get('Location'),
                                      authorizations=authorizations,
                                      csr_pem=csr_pem)
Example #4
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',
        })

        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')
Example #5
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 #6
0
    def test_identifier(self):
        """Test the identifier property."""

        self.assertEqual(
            self.auth1.identifier,
            messages.Identifier(typ=messages.IDENTIFIER_FQDN,
                                value=self.auth1.value))
        self.assertEqual(
            self.auth2.identifier,
            messages.Identifier(typ=messages.IDENTIFIER_FQDN,
                                value=self.auth2.value))
Example #7
0
    def setUp(self):
        super(ClientV2Test, self).setUp()

        self.directory = DIRECTORY_V2

        from acme.client import ClientV2
        self.client = ClientV2(self.directory, self.net)

        self.new_reg = self.new_reg.update(terms_of_service_agreed=True)

        self.authzr_uri2 = 'https://www.letsencrypt-demo.org/acme/authz/2'
        self.authz2 = self.authz.update(identifier=messages.Identifier(
            typ=messages.IDENTIFIER_FQDN, value='www.example.com'),
            status=messages.STATUS_PENDING)
        self.authzr2 = messages.AuthorizationResource(
            body=self.authz2, uri=self.authzr_uri2)

        self.order = messages.Order(
            identifiers=(self.authz.identifier, self.authz2.identifier),
            status=messages.STATUS_PENDING,
            authorizations=(self.authzr.uri, self.authzr_uri2),
            finalize='https://www.letsencrypt-demo.org/acme/acct/1/order/1/finalize')
        self.orderr = messages.OrderResource(
            body=self.order,
            uri='https://www.letsencrypt-demo.org/acme/acct/1/order/1',
            authorizations=[self.authzr, self.authzr2], csr_pem=CSR_SAN_PEM)
Example #8
0
    def new_order(self, csr_pem):
        """Request a new Order object from the server.

        :param str csr_pem: A CSR in PEM format.

        :returns: The newly created order.
        :rtype: OrderResource
        """
        csr = OpenSSL.crypto.load_certificate_request(
            OpenSSL.crypto.FILETYPE_PEM, csr_pem)
        # pylint: disable=protected-access
        dnsNames = crypto_util._pyopenssl_cert_or_req_all_names(csr)

        identifiers = []
        for name in dnsNames:
            identifiers.append(
                messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=name))
        order = messages.NewOrder(identifiers=identifiers)
        response = self._post(self.directory['newOrder'], order)
        body = messages.Order.from_json(response.json())
        authorizations = []
        for url in body.authorizations:
            authorizations.append(
                self._authzr_from_response(self._post_as_get(url), uri=url))
        return messages.OrderResource(body=body,
                                      uri=response.headers.get('Location'),
                                      authorizations=authorizations,
                                      csr_pem=csr_pem)
Example #9
0
    def new_order(self, csr_pem):
        """Request a new Order object from the server.

        :param str csr_pem: A CSR in PEM format.

        :returns: The newly created order.
        :rtype: OrderResource
        """
        csr = cryptography.x509.load_pem_x509_csr(
            csr_pem, cryptography.hazmat.backends.default_backend())
        san_extension = next(
            ext for ext in csr.extensions if ext.oid ==
            cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
        dnsNames = san_extension.value.get_values_for_type(
            cryptography.x509.DNSName)

        identifiers = []
        for name in dnsNames:
            identifiers.append(
                messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=name))
        order = messages.NewOrder(identifiers=identifiers)
        response = self.net.post(self.directory['newOrder'], order)
        body = messages.Order.from_json(response.json())
        authorizations = []
        for url in body.authorizations:
            authorizations.append(self._authzr_from_response(
                self.net.get(url)))
        return messages.OrderResource(body=body,
                                      uri=response.headers.get('Location'),
                                      authorizations=authorizations,
                                      csr_pem=csr_pem)
Example #10
0
    def test_add_authorizations(self):
        """Test the add_authorizations method."""
        identifier = messages.Identifier(typ=messages.IDENTIFIER_FQDN,
                                         value='example.com')
        auths = self.order1.add_authorizations([identifier])
        self.assertEqual(auths[0].type, 'dns')
        self.assertEqual(auths[0].value, 'example.com')

        msg = r'^UNIQUE constraint failed: django_ca_acmeauthorization\.order_id, django_ca_acmeauthorization\.type, django_ca_acmeauthorization\.value$'  # NOQA: E501
        with transaction.atomic(), self.assertRaisesRegex(IntegrityError, msg):
            self.order1.add_authorizations([identifier])
Example #11
0
def fqdn_identifier(fqdn):
    """
    Construct an identifier from an FQDN.

    Trivial implementation, just saves on typing.

    :param str fqdn: The domain name.

    :return: The identifier.
    :rtype: `~acme.messages.Identifier`
    """
    return messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=fqdn)
Example #12
0
    def request_domain_challenges(self, domain, new_authz_uri):
        """Request challenges for domain names.

        This is simply a convenience function that wraps around
        `request_challenges`, but works with domain names instead of
        generic identifiers.

        :param str domain: Domain name to be challenged.
        :param str new_authzr_uri: new-authorization URI

        :returns: Authorization Resource.
        :rtype: `.AuthorizationResource`

        """
        return self.request_challenges(messages.Identifier(
            typ=messages.IDENTIFIER_FQDN, value=domain), new_authz_uri)
Example #13
0
    def request_domain_challenges(self, domain, new_authzr_uri=None):
        """Request challenges for domain names.

        This is simply a convenience function that wraps around
        `request_challenges`, but works with domain names instead of
        generic identifiers. See ``request_challenges`` for more
        documentation.

        :param str domain: Domain name to be challenged.
        :param str new_authzr_uri: Deprecated. Do not use.

        :returns: Authorization Resource.
        :rtype: `.AuthorizationResource`

        :raises errors.WildcardUnsupportedError: if a wildcard is requested

        """
        return self.request_challenges(messages.Identifier(
            typ=messages.IDENTIFIER_FQDN, value=domain), new_authzr_uri)
Example #14
0
def gen_authzr(authz_status: messages.Status,
               domain: str,
               challs: Iterable[challenges.Challenge],
               statuses: Iterable[messages.Status],
               combos: bool = True) -> messages.AuthorizationResource:
    """Generate an authorization resource.

    :param authz_status: Status object
    :type authz_status: :class:`acme.messages.Status`
    :param list challs: Challenge objects
    :param list statuses: status of each challenge object
    :param bool combos: Whether or not to add combinations

    """
    challbs = tuple(
        chall_to_challb(chall, status)
        for chall, status in zip(challs, statuses))
    authz_kwargs = {
        "identifier":
        messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=domain),
        "challenges":
        challbs,
    }
    if combos:
        authz_kwargs.update({"combinations": gen_combos(challbs)})
    if authz_status == messages.STATUS_VALID:
        authz_kwargs.update({
            "status":
            authz_status,
            "expires":
            datetime.datetime.now() + datetime.timedelta(days=31),
        })
    else:
        authz_kwargs.update({
            "status": authz_status,
        })

    return messages.AuthorizationResource(
        uri="https://trusted.ca/new-authz-resource",
        body=messages.Authorization(**authz_kwargs))
Example #15
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)
def request_cert(domain):
    domain = domain.lower()
  
    print("Generating user key")
    user_key = josepy.JWKRSA(
        key=rsa.generate_private_key(
            public_exponent=65537,
            key_size=KEY_SIZE,
            backend=default_backend()
        )
    )

    print("Connecting to Let's Encrypt on {}".format(DIRECTORY_URL))
    acme = client.Client(DIRECTORY_URL, user_key)
    print("Registering")
    regr = acme.register()
    print("Agreeing to ToS")
    acme.agree_to_tos(regr)

    print("Requesting challenges")
    authzr = acme.request_challenges(
        identifier=messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=domain)
    )

    print("Looking for HTTP challenge")
    challenge = get_http_challenge(authzr)

    print("You need to set up the challenge response.")
    print("URL: http://{}{}".format(domain, challenge.chall.path))
    print("Content: {}".format(challenge.chall.validation(user_key)))

    response = challenge.chall.response(user_key)
    while not response.simple_verify(challenge.chall, domain, user_key.public_key()):
        raw_input("It doesn't look like it's set up yet; press return when it is.")

    print("Authorizing -- here goes...")
    auth_response = acme.answer_challenge(challenge, challenge.chall.response(user_key))
    print("Response was {}".format(auth_response))

    print("Waiting for authorization to become valid")
    while True:
        print("Polling")
        authzr, authzr_response = acme.poll(authzr)
        challenge = get_http_challenge(authzr)
        if challenge.status.name == "valid":
            break
        print("HTTP challenge is currently {}".format(challenge))
        time.sleep(1)
    print("Auth valid")

    print("Generating CSR")
    certificate_key = crypto.PKey()
    certificate_key.generate_key(crypto.TYPE_RSA, 2048)
    csr = crypto.X509Req()
    csr.get_subject().CN = domain
    csr.set_pubkey(certificate_key)
    csr.sign(certificate_key, "sha256")

    print("Requesting certificate")
    certificate_response = acme.request_issuance(josepy.util.ComparableX509(csr), [authzr])
    print("Got it!")

    print("Fetching chain")
    chain = acme.fetch_chain(certificate_response)
    print("Done!")

    print("Here are the details:")

    print("Private key:")
    print(crypto.dump_privatekey(FILETYPE_PEM, certificate_key))

    print("Combined cert:")
    print(crypto.dump_certificate(FILETYPE_PEM, certificate_response.body.wrapped))
    for cert in chain:
        print(crypto.dump_certificate(FILETYPE_PEM, cert.wrapped))
Example #17
0
NEW_REG_URL = 'https://www.letsencrypt-demo.org/acme/new-reg'
BITS = 2048  # minimum for Boulder
DOMAIN = 'example1.com'  # example.com is ignored by Boulder

key = jose.JWKRSA.load(
    Crypto.PublicKey.RSA.generate(BITS).exportKey(format="PEM"))
acme = client.Client(NEW_REG_URL, key)

regr = acme.register(contact=())
logging.info('Auto-accepting TOS: %s', regr.terms_of_service)
acme.update_registration(
    regr.update(body=regr.body.update(agreement=regr.terms_of_service)))
logging.debug(regr)

authzr = acme.request_challenges(identifier=messages.Identifier(
    typ=messages.IDENTIFIER_FQDN, value=DOMAIN),
                                 new_authzr_uri=regr.new_authzr_uri)
logging.debug(authzr)

authzr, authzr_response = acme.poll(authzr)

csr = M2Crypto.X509.load_request_string(
    pkg_resources.resource_string('acme.jose',
                                  os.path.join('testdata', 'csr.der')),
    M2Crypto.X509.FORMAT_DER)
try:
    acme.request_issuance(csr, (authzr, ))
except messages.Error as error:
    print("This script is doomed to fail as no authorization "
          "challenges are ever solved. Error from server: {0}".format(error))
Example #18
0
DOMAIN = 'example1.com'  # example.com is ignored by Boulder

# generate_private_key requires cryptography>=0.5
key = jose.JWKRSA(key=rsa.generate_private_key(
    public_exponent=65537,
    key_size=BITS,
    backend=default_backend()))
acme = client.Client(NEW_REG_URL, key)

regr = acme.register()
logging.info('Auto-accepting TOS: %s', regr.terms_of_service)
acme.update_registration(regr.update(
    body=regr.body.update(agreement=regr.terms_of_service)))
logging.debug(regr)

authzr = acme.request_challenges(
    identifier=messages.Identifier(typ=messages.IDENTIFIER_FQDN, value=DOMAIN),
    new_authzr_uri=regr.new_authzr_uri)
logging.debug(authzr)

authzr, authzr_response = acme.poll(authzr)

csr = OpenSSL.crypto.load_certificate_request(
    OpenSSL.crypto.FILETYPE_ASN1, pkg_resources.resource_string(
        'acme', os.path.join('testdata', 'csr.der')))
try:
    acme.request_issuance(csr, (authzr,))
except messages.Error as error:
    print ("This script is doomed to fail as no authorization "
           "challenges are ever solved. Error from server: {0}".format(error))
Example #19
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 #20
0
    def _update(self):
        if not self.dom_key or not self.dom_csr:
            self.state = 'cert'
            return
        if not self.key:
            self.state = 'priv_key'
            return

        k = self._deserialize_key(self.key)
        acme = client.Client(DIRECTORY_URL, k)

        authzr = acme.request_challenges(identifier=messages.Identifier(
            typ=messages.IDENTIFIER_FQDN, value=self.name))

        challb = self._supported_challb(authzr)
        if not challb:
            _logger.warning(
                _("Didn't find any http01 challenge. Just try again."))
            _logger.warning(authzr)
            raise exceptions.Warning(
                _("Didn't find any http01 challenge. Just try again."))
        response, self.challenge_validation = challb.response_and_validation(k)
        self.challenge_path = challb.path.split('/')[-1]

        challenge = Challenge()
        challenge.set_challenge(self.challenge_path, self.challenge_validation)

        _logger.info("Need to response %s on url %s",
                     self.challenge_validation, self.challenge_path)

        # write data to file, because it seems that the data is not written to the database bevor
        # the controller requests it.  So the controller loads it from the file:
        #challenge_file = os.path.join(get_challenge_dir(), self.challenge_path)
        #f = open(challenge_file, 'w')
        #f.write(self.challenge_validation)
        #_logger.info("saved %s to '%s'", self.challenge_validation, challenge_file)

        #self.dom_verified = response.simple_verify(
        #    challb.chall, self.name, acme.key.public_key())
        #f.close()

        #if not self.dom_verified:
        #    _logger.warning('%s was not successfully self-verified. '
        #                   'CA is likely to fail as well!', self.name)
        #            raise exceptions.Warning(_("%s was not successfully self-verified. CA is likely to fail as well!"% self.name))
        #else:
        #    _logger.info('%s was successfully self-verified', self.name)

        # os.unlink(challenge_file)
        # _logger.info("unlinked %s", challenge_file)

        #        authzr, authzr_response = acme.poll(authzr)

        acme.answer_challenge(challb, response)

        csr = OpenSSL.crypto.load_certificate_request(
            OpenSSL.crypto.FILETYPE_PEM, self.dom_csr)

        certr, authzr = acme.poll_and_request_issuance(
            jose.util.ComparableX509(csr), (authzr, ))

        self.cert = OpenSSL.crypto.dump_certificate(
            OpenSSL.crypto.FILETYPE_PEM, certr.body)
        if self.cert:
            expire_time = time.strptime(certr.body.get_notAfter(),
                                        "%Y%m%d%H%M%SZ")
            self.expires = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT,
                                         expire_time)

        return self.cert