From d7c9cbb2785accded09de60a811f75486af3181d Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 7 Feb 2018 16:53:11 -0800 Subject: [PATCH 1/3] Update documentation. --- acme/acme/client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index c33fe96e9..2a8839adb 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -661,8 +661,8 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes """Initialize. - :param key: Account private key - :param messages.Registration account: Account object. Required if you are + :param josepy.JWK key: Account private key + :param messages.RegistrationResource account: Account object. Required if you are planning to use .post() with acme_version=2. :param josepy.JWASignature alg: Algoritm to use in signing JWS. :param bool verify_ssl: Whether to verify certificates on SSL connections. @@ -694,10 +694,10 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes .. todo:: Implement ``acmePath``. - :param .JSONDeSerializable obj: + :param josepy.JSONDeSerializable obj: :param str url: The URL to which this object will be POSTed :param bytes nonce: - :rtype: `.JWS` + :rtype: `josepy.JWS` """ jobj = obj.json_dumps(indent=2).encode() From 105ec41257f651a167c6ac6d4639a6f4219c03bf Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 7 Feb 2018 17:03:23 -0800 Subject: [PATCH 2/3] lint --- acme/acme/client.py | 35 ++--------------------------------- acme/acme/messages.py | 1 - 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index 2a8839adb..915404993 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -10,13 +10,13 @@ import time import six from six.moves import http_client # pylint: disable=import-error -import crypto_util import josepy as jose import OpenSSL import re import requests import sys +from acme import crypto_util from acme import errors from acme import jws from acme import messages @@ -574,37 +574,6 @@ class ClientV2(ClientBase): order_response = self._order_resource_from_response(response, csr=wrapped_csr) return order_response - def poll_order_and_request_issuance(self, orderr): - """Poll Order Resource for status. - - :param orderr: Order Resource - :type orderr: `.OrderResource` - - :returns: Updated Order Resource - - :rtype: (`.OrderResource`) - - """ - while True: - response = self.net.get(orderr.uri) - latest = self._order_resource_from_response(response, uri=orderr.uri) - if any([a.body.status == messages.STATUS_PENDING for a in latest.authorizations]): - time.sleep(1) - else: - break - for authz in latest.authorizations: - if authz.body.status != messages.STATUS_VALID: - for chall in authz.body.challenges: - if chall.error != None: - raise Exception("failed challenge for %s: %s" % - (authz.body.identifier.value, chall.error)) - raise Exception("failed authorization: %s" % authz.body) - while latest.fullchain_pem is None: - time.sleep(1) - response = self.net.get(orderr.uri) - latest = self._order_resource_from_response(response, uri=orderr.uri) - return latest - def _order_resource_from_response(self, response, uri=None, csr=None): body = messages.Order.from_json(response.json()) authorizations = [] @@ -646,7 +615,7 @@ class ClientV2(ClientBase): time.sleep(1) latest = self._order_resource_from_response(self.net.get(orderr.uri), uri=orderr.uri) if latest.fullchain_pem is not None: - return latest + return latest return None class ClientNetwork(object): # pylint: disable=too-many-instance-attributes diff --git a/acme/acme/messages.py b/acme/acme/messages.py index d6f836fae..b976addef 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -1,6 +1,5 @@ """ACME protocol messages.""" import collections -import re import six import josepy as jose From 94f0d150d2ef5f68ab3d219dbed1d0d04463e2db Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 7 Feb 2018 17:31:48 -0800 Subject: [PATCH 3/3] Use pycryptography. --- acme/acme/client.py | 25 +++++++++++++++++-------- acme/acme/messages.py | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index 915404993..38efdbb7d 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -1,6 +1,7 @@ """ACME client API.""" import base64 import collections +import cryptography import datetime from email.utils import parsedate_tz import heapq @@ -16,7 +17,6 @@ import re import requests import sys -from acme import crypto_util from acme import errors from acme import jws from acme import messages @@ -563,18 +563,23 @@ class ClientV2(ClientBase): :returns: List of Authorization Resources. :rtype: `list` of `.AuthorizationResource` """ - csr = OpenSSL.crypto.load_certificate_request(OpenSSL.crypto.FILETYPE_PEM, csr_pem) - wrapped_csr = jose.ComparableX509(csr) + 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 crypto_util._pyopenssl_cert_or_req_san(csr): + 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) - order_response = self._order_resource_from_response(response, csr=wrapped_csr) + order_response = self._order_resource_from_response( + response, csr_pem=csr_pem) return order_response - def _order_resource_from_response(self, response, uri=None, csr=None): + def _order_resource_from_response(self, response, uri=None, csr_pem=None): body = messages.Order.from_json(response.json()) authorizations = [] for url in body.authorizations: @@ -589,7 +594,7 @@ class ClientV2(ClientBase): uri=response.headers.get('Location', uri), fullchain_pem=fullchain_pem, authorizations=authorizations, - csr=csr) + csr_pem=csr_pem) def poll_order_and_request_issuance(self, orderr, max_time=datetime.timedelta(seconds=90)): """Poll Order Resource for status.""" @@ -610,7 +615,11 @@ class ClientV2(ClientBase): (authzr.body.identifier.value, chall.error)) raise Exception("failed authorization: %s" % authzr.body) latest = self._order_resource_from_response(self.net.get(orderr.uri), uri=orderr.uri) - self.net.post(latest.body.finalize, messages.CertificateRequest(csr=orderr.csr)) + + csr = OpenSSL.crypto.load_certificate_request( + OpenSSL.crypto.FILETYPE_PEM, orderr.csr_pem) + wrapped_csr = messages.CertificateRequest(csr=jose.ComparableX509(csr)) + self.net.post(latest.body.finalize, wrapped_csr) while datetime.datetime.now() < deadline: time.sleep(1) latest = self._order_resource_from_response(self.net.get(orderr.uri), uri=orderr.uri) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index b976addef..37cd3b7ab 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -509,7 +509,7 @@ class OrderResource(ResourceWithURI): """ body = jose.Field('body', decoder=Order.from_json) - csr = jose.Field('csr', omitempty=True) + csr_pem = jose.Field('csr_pem', omitempty=True) authorizations = jose.Field('authorizations') fullchain_pem = jose.Field('fullchain_pem', omitempty=True)