We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Certbot fails to retrieve domains of previous certificate when SAN is a critical extension
## My operating system is (include version):
CentOS 6 and CentOS 7

## I installed Certbot with (certbot-auto, OS package manager, pip, etc):
For CentOS6 (certbot-auto):  https://certbot.eff.org/all-instructions/#centos-6-none-of-the-above
For CentOS7 (yum):  https://certbot.eff.org/all-instructions/#centos-rhel-7-none-of-the-above

Currently using certbot 0.14.1 on CentOS 7 and certbot-auto 0.16.0 on CentOS 6.

## I ran this command and it produced this output:
certbot certonly --manual--cert-name cubs --domains chicago-cubs.venafi.example,cubs.venafi.example

_(note: non-Let's Encrypt ACME server specified in /etc/letsencrypt/cli.ini)_

```
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Starting new HTTPS connection (1): tpp.venafi.example

-------------------------------------------------------------------------------
Confirm that you intend to update certificate cubs to include domains
[u'chicago-cubs.venafi.example', u'cubs.venafi.example']. Note that it
previously contained domains [].
-------------------------------------------------------------------------------
```

## Certbot's behavior differed from what I expected because:
It appears that certbot is unable to parse domains from the previous certificate when it has no CN and the DNS SANs are a critical extension.  Symptom is the unexpected prompt shown above (applicable [previous_cert_pem.txt](https://github.com/certbot/certbot/files/1137352/previous_cert_pem.txt)).


## Background
Venafi has introduced an ACME server interface in Trust Protection Platform 17.2 and the common application involves using certbot to obtain certificates from an enterprise PKI.  Microsoft's Active Directory Certifiate Services (ADCS) is a very popular enterprise PKI and when it enrolls a certificate request that lacks a Subject DN (has only SANs) the subjectAltName (2.5.29.17) extension is always critical.  This behavior is consistent with [Section 4.2.1.6 of RFC 5280](https://tools.ietf.org/html/rfc5280#section-4.2.1.6) which states "If the subject field contains an empty sequence, then the issuing CA MUST include a subjectAltName extension that is marked as critical."


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `acme/acme/crypto_util.py`
Content:
```
1 """Crypto utilities."""
2 import binascii
3 import contextlib
4 import logging
5 import re
6 import socket
7 import sys
8 
9 import OpenSSL
10 
11 from acme import errors
12 
13 
14 logger = logging.getLogger(__name__)
15 
16 # TLSSNI01 certificate serving and probing is not affected by SSL
17 # vulnerabilities: prober needs to check certificate for expected
18 # contents anyway. Working SNI is the only thing that's necessary for
19 # the challenge and thus scoping down SSL/TLS method (version) would
20 # cause interoperability issues: TLSv1_METHOD is only compatible with
21 # TLSv1_METHOD, while SSLv23_METHOD is compatible with all other
22 # methods, including TLSv2_METHOD (read more at
23 # https://www.openssl.org/docs/ssl/SSLv23_method.html). _serve_sni
24 # should be changed to use "set_options" to disable SSLv2 and SSLv3,
25 # in case it's used for things other than probing/serving!
26 _DEFAULT_TLSSNI01_SSL_METHOD = OpenSSL.SSL.SSLv23_METHOD  # type: ignore
27 
28 
29 class SSLSocket(object):  # pylint: disable=too-few-public-methods
30     """SSL wrapper for sockets.
31 
32     :ivar socket sock: Original wrapped socket.
33     :ivar dict certs: Mapping from domain names (`bytes`) to
34         `OpenSSL.crypto.X509`.
35     :ivar method: See `OpenSSL.SSL.Context` for allowed values.
36 
37     """
38     def __init__(self, sock, certs, method=_DEFAULT_TLSSNI01_SSL_METHOD):
39         self.sock = sock
40         self.certs = certs
41         self.method = method
42 
43     def __getattr__(self, name):
44         return getattr(self.sock, name)
45 
46     def _pick_certificate_cb(self, connection):
47         """SNI certificate callback.
48 
49         This method will set a new OpenSSL context object for this
50         connection when an incoming connection provides an SNI name
51         (in order to serve the appropriate certificate, if any).
52 
53         :param connection: The TLS connection object on which the SNI
54             extension was received.
55         :type connection: :class:`OpenSSL.Connection`
56 
57         """
58         server_name = connection.get_servername()
59         try:
60             key, cert = self.certs[server_name]
61         except KeyError:
62             logger.debug("Server name (%s) not recognized, dropping SSL",
63                          server_name)
64             return
65         new_context = OpenSSL.SSL.Context(self.method)
66         new_context.set_options(OpenSSL.SSL.OP_NO_SSLv2)
67         new_context.set_options(OpenSSL.SSL.OP_NO_SSLv3)
68         new_context.use_privatekey(key)
69         new_context.use_certificate(cert)
70         connection.set_context(new_context)
71 
72     class FakeConnection(object):
73         """Fake OpenSSL.SSL.Connection."""
74 
75         # pylint: disable=too-few-public-methods,missing-docstring
76 
77         def __init__(self, connection):
78             self._wrapped = connection
79 
80         def __getattr__(self, name):
81             return getattr(self._wrapped, name)
82 
83         def shutdown(self, *unused_args):
84             # OpenSSL.SSL.Connection.shutdown doesn't accept any args
85             return self._wrapped.shutdown()
86 
87     def accept(self):  # pylint: disable=missing-docstring
88         sock, addr = self.sock.accept()
89 
90         context = OpenSSL.SSL.Context(self.method)
91         context.set_options(OpenSSL.SSL.OP_NO_SSLv2)
92         context.set_options(OpenSSL.SSL.OP_NO_SSLv3)
93         context.set_tlsext_servername_callback(self._pick_certificate_cb)
94 
95         ssl_sock = self.FakeConnection(OpenSSL.SSL.Connection(context, sock))
96         ssl_sock.set_accept_state()
97 
98         logger.debug("Performing handshake with %s", addr)
99         try:
100             ssl_sock.do_handshake()
101         except OpenSSL.SSL.Error as error:
102             # _pick_certificate_cb might have returned without
103             # creating SSL context (wrong server name)
104             raise socket.error(error)
105 
106         return ssl_sock, addr
107 
108 
109 def probe_sni(name, host, port=443, timeout=300,
110               method=_DEFAULT_TLSSNI01_SSL_METHOD, source_address=('', 0)):
111     """Probe SNI server for SSL certificate.
112 
113     :param bytes name: Byte string to send as the server name in the
114         client hello message.
115     :param bytes host: Host to connect to.
116     :param int port: Port to connect to.
117     :param int timeout: Timeout in seconds.
118     :param method: See `OpenSSL.SSL.Context` for allowed values.
119     :param tuple source_address: Enables multi-path probing (selection
120         of source interface). See `socket.creation_connection` for more
121         info. Available only in Python 2.7+.
122 
123     :raises acme.errors.Error: In case of any problems.
124 
125     :returns: SSL certificate presented by the server.
126     :rtype: OpenSSL.crypto.X509
127 
128     """
129     context = OpenSSL.SSL.Context(method)
130     context.set_timeout(timeout)
131 
132     socket_kwargs = {} if sys.version_info < (2, 7) else {
133         'source_address': source_address}
134 
135     host_protocol_agnostic = None if host == '::' or host == '0' else host
136 
137     try:
138         # pylint: disable=star-args
139         logger.debug("Attempting to connect to %s:%d%s.", host_protocol_agnostic, port,
140             " from {0}:{1}".format(source_address[0], source_address[1]) if \
141             socket_kwargs else "")
142         sock = socket.create_connection((host_protocol_agnostic, port), **socket_kwargs)
143     except socket.error as error:
144         raise errors.Error(error)
145 
146     with contextlib.closing(sock) as client:
147         client_ssl = OpenSSL.SSL.Connection(context, client)
148         client_ssl.set_connect_state()
149         client_ssl.set_tlsext_host_name(name)  # pyOpenSSL>=0.13
150         try:
151             client_ssl.do_handshake()
152             client_ssl.shutdown()
153         except OpenSSL.SSL.Error as error:
154             raise errors.Error(error)
155     return client_ssl.get_peer_certificate()
156 
157 def make_csr(private_key_pem, domains, must_staple=False):
158     """Generate a CSR containing a list of domains as subjectAltNames.
159 
160     :param buffer private_key_pem: Private key, in PEM PKCS#8 format.
161     :param list domains: List of DNS names to include in subjectAltNames of CSR.
162     :param bool must_staple: Whether to include the TLS Feature extension (aka
163         OCSP Must Staple: https://tools.ietf.org/html/rfc7633).
164     :returns: buffer PEM-encoded Certificate Signing Request.
165     """
166     private_key = OpenSSL.crypto.load_privatekey(
167         OpenSSL.crypto.FILETYPE_PEM, private_key_pem)
168     csr = OpenSSL.crypto.X509Req()
169     extensions = [
170         OpenSSL.crypto.X509Extension(
171             b'subjectAltName',
172             critical=False,
173             value=', '.join('DNS:' + d for d in domains).encode('ascii')
174         ),
175     ]
176     if must_staple:
177         extensions.append(OpenSSL.crypto.X509Extension(
178             b"1.3.6.1.5.5.7.1.24",
179             critical=False,
180             value=b"DER:30:03:02:01:05"))
181     csr.add_extensions(extensions)
182     csr.set_pubkey(private_key)
183     csr.set_version(2)
184     csr.sign(private_key, 'sha256')
185     return OpenSSL.crypto.dump_certificate_request(
186         OpenSSL.crypto.FILETYPE_PEM, csr)
187 
188 def _pyopenssl_cert_or_req_san(cert_or_req):
189     """Get Subject Alternative Names from certificate or CSR using pyOpenSSL.
190 
191     .. todo:: Implement directly in PyOpenSSL!
192 
193     .. note:: Although this is `acme` internal API, it is used by
194         `letsencrypt`.
195 
196     :param cert_or_req: Certificate or CSR.
197     :type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
198 
199     :returns: A list of Subject Alternative Names.
200     :rtype: `list` of `unicode`
201 
202     """
203     # This function finds SANs by dumping the certificate/CSR to text and
204     # searching for "X509v3 Subject Alternative Name" in the text. This method
205     # is used to support PyOpenSSL version 0.13 where the
206     # `_subjectAltNameString` and `get_extensions` methods are not available
207     # for CSRs.
208 
209     # constants based on PyOpenSSL certificate/CSR text dump
210     part_separator = ":"
211     parts_separator = ", "
212     prefix = "DNS" + part_separator
213 
214     if isinstance(cert_or_req, OpenSSL.crypto.X509):
215         func = OpenSSL.crypto.dump_certificate
216     else:
217         func = OpenSSL.crypto.dump_certificate_request
218     text = func(OpenSSL.crypto.FILETYPE_TEXT, cert_or_req).decode("utf-8")
219     # WARNING: this function does not support multiple SANs extensions.
220     # Multiple X509v3 extensions of the same type is disallowed by RFC 5280.
221     match = re.search(r"X509v3 Subject Alternative Name:\s*(.*)", text)
222     # WARNING: this function assumes that no SAN can include
223     # parts_separator, hence the split!
224     sans_parts = [] if match is None else match.group(1).split(parts_separator)
225 
226     return [part.split(part_separator)[1]
227             for part in sans_parts if part.startswith(prefix)]
228 
229 
230 def gen_ss_cert(key, domains, not_before=None,
231                 validity=(7 * 24 * 60 * 60), force_san=True):
232     """Generate new self-signed certificate.
233 
234     :type domains: `list` of `unicode`
235     :param OpenSSL.crypto.PKey key:
236     :param bool force_san:
237 
238     If more than one domain is provided, all of the domains are put into
239     ``subjectAltName`` X.509 extension and first domain is set as the
240     subject CN. If only one domain is provided no ``subjectAltName``
241     extension is used, unless `force_san` is ``True``.
242 
243     """
244     assert domains, "Must provide one or more hostnames for the cert."
245     cert = OpenSSL.crypto.X509()
246     cert.set_serial_number(int(binascii.hexlify(OpenSSL.rand.bytes(16)), 16))
247     cert.set_version(2)
248 
249     extensions = [
250         OpenSSL.crypto.X509Extension(
251             b"basicConstraints", True, b"CA:TRUE, pathlen:0"),
252     ]
253 
254     cert.get_subject().CN = domains[0]
255     # TODO: what to put into cert.get_subject()?
256     cert.set_issuer(cert.get_subject())
257 
258     if force_san or len(domains) > 1:
259         extensions.append(OpenSSL.crypto.X509Extension(
260             b"subjectAltName",
261             critical=False,
262             value=b", ".join(b"DNS:" + d.encode() for d in domains)
263         ))
264 
265     cert.add_extensions(extensions)
266 
267     cert.gmtime_adj_notBefore(0 if not_before is None else not_before)
268     cert.gmtime_adj_notAfter(validity)
269 
270     cert.set_pubkey(key)
271     cert.sign(key, "sha256")
272     return cert
273
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/acme/acme/crypto_util.py b/acme/acme/crypto_util.py
--- a/acme/acme/crypto_util.py
+++ b/acme/acme/crypto_util.py
@@ -218,7 +218,7 @@
     text = func(OpenSSL.crypto.FILETYPE_TEXT, cert_or_req).decode("utf-8")
     # WARNING: this function does not support multiple SANs extensions.
     # Multiple X509v3 extensions of the same type is disallowed by RFC 5280.
-    match = re.search(r"X509v3 Subject Alternative Name:\s*(.*)", text)
+    match = re.search(r"X509v3 Subject Alternative Name:(?: critical)?\s*(.*)", text)
     # WARNING: this function assumes that no SAN can include
     # parts_separator, hence the split!
     sans_parts = [] if match is None else match.group(1).split(parts_separator)

{"golden_diff": "diff --git a/acme/acme/crypto_util.py b/acme/acme/crypto_util.py\n--- a/acme/acme/crypto_util.py\n+++ b/acme/acme/crypto_util.py\n@@ -218,7 +218,7 @@\n     text = func(OpenSSL.crypto.FILETYPE_TEXT, cert_or_req).decode(\"utf-8\")\n     # WARNING: this function does not support multiple SANs extensions.\n     # Multiple X509v3 extensions of the same type is disallowed by RFC 5280.\n-    match = re.search(r\"X509v3 Subject Alternative Name:\\s*(.*)\", text)\n+    match = re.search(r\"X509v3 Subject Alternative Name:(?: critical)?\\s*(.*)\", text)\n     # WARNING: this function assumes that no SAN can include\n     # parts_separator, hence the split!\n     sans_parts = [] if match is None else match.group(1).split(parts_separator)\n", "issue": "Certbot fails to retrieve domains of previous certificate when SAN is a critical extension\n## My operating system is (include version):\r\nCentOS 6 and CentOS 7\r\n\r\n## I installed Certbot with (certbot-auto, OS package manager, pip, etc):\r\nFor CentOS6 (certbot-auto):  https://certbot.eff.org/all-instructions/#centos-6-none-of-the-above\r\nFor CentOS7 (yum):  https://certbot.eff.org/all-instructions/#centos-rhel-7-none-of-the-above\r\n\r\nCurrently using certbot 0.14.1 on CentOS 7 and certbot-auto 0.16.0 on CentOS 6.\r\n\r\n## I ran this command and it produced this output:\r\ncertbot certonly --manual--cert-name cubs --domains chicago-cubs.venafi.example,cubs.venafi.example\r\n\r\n_(note: non-Let's Encrypt ACME server specified in /etc/letsencrypt/cli.ini)_\r\n\r\n```\r\nSaving debug log to /var/log/letsencrypt/letsencrypt.log\r\nStarting new HTTPS connection (1): tpp.venafi.example\r\n\r\n-------------------------------------------------------------------------------\r\nConfirm that you intend to update certificate cubs to include domains\r\n[u'chicago-cubs.venafi.example', u'cubs.venafi.example']. Note that it\r\npreviously contained domains [].\r\n-------------------------------------------------------------------------------\r\n```\r\n\r\n## Certbot's behavior differed from what I expected because:\r\nIt appears that certbot is unable to parse domains from the previous certificate when it has no CN and the DNS SANs are a critical extension.  Symptom is the unexpected prompt shown above (applicable [previous_cert_pem.txt](https://github.com/certbot/certbot/files/1137352/previous_cert_pem.txt)).\r\n\r\n\r\n## Background\r\nVenafi has introduced an ACME server interface in Trust Protection Platform 17.2 and the common application involves using certbot to obtain certificates from an enterprise PKI.  Microsoft's Active Directory Certifiate Services (ADCS) is a very popular enterprise PKI and when it enrolls a certificate request that lacks a Subject DN (has only SANs) the subjectAltName (2.5.29.17) extension is always critical.  This behavior is consistent with [Section 4.2.1.6 of RFC 5280](https://tools.ietf.org/html/rfc5280#section-4.2.1.6) which states \"If the subject field contains an empty sequence, then the issuing CA MUST include a subjectAltName extension that is marked as critical.\"\r\n\n", "before_files": [{"content": "\"\"\"Crypto utilities.\"\"\"\nimport binascii\nimport contextlib\nimport logging\nimport re\nimport socket\nimport sys\n\nimport OpenSSL\n\nfrom acme import errors\n\n\nlogger = logging.getLogger(__name__)\n\n# TLSSNI01 certificate serving and probing is not affected by SSL\n# vulnerabilities: prober needs to check certificate for expected\n# contents anyway. Working SNI is the only thing that's necessary for\n# the challenge and thus scoping down SSL/TLS method (version) would\n# cause interoperability issues: TLSv1_METHOD is only compatible with\n# TLSv1_METHOD, while SSLv23_METHOD is compatible with all other\n# methods, including TLSv2_METHOD (read more at\n# https://www.openssl.org/docs/ssl/SSLv23_method.html). _serve_sni\n# should be changed to use \"set_options\" to disable SSLv2 and SSLv3,\n# in case it's used for things other than probing/serving!\n_DEFAULT_TLSSNI01_SSL_METHOD = OpenSSL.SSL.SSLv23_METHOD  # type: ignore\n\n\nclass SSLSocket(object):  # pylint: disable=too-few-public-methods\n    \"\"\"SSL wrapper for sockets.\n\n    :ivar socket sock: Original wrapped socket.\n    :ivar dict certs: Mapping from domain names (`bytes`) to\n        `OpenSSL.crypto.X509`.\n    :ivar method: See `OpenSSL.SSL.Context` for allowed values.\n\n    \"\"\"\n    def __init__(self, sock, certs, method=_DEFAULT_TLSSNI01_SSL_METHOD):\n        self.sock = sock\n        self.certs = certs\n        self.method = method\n\n    def __getattr__(self, name):\n        return getattr(self.sock, name)\n\n    def _pick_certificate_cb(self, connection):\n        \"\"\"SNI certificate callback.\n\n        This method will set a new OpenSSL context object for this\n        connection when an incoming connection provides an SNI name\n        (in order to serve the appropriate certificate, if any).\n\n        :param connection: The TLS connection object on which the SNI\n            extension was received.\n        :type connection: :class:`OpenSSL.Connection`\n\n        \"\"\"\n        server_name = connection.get_servername()\n        try:\n            key, cert = self.certs[server_name]\n        except KeyError:\n            logger.debug(\"Server name (%s) not recognized, dropping SSL\",\n                         server_name)\n            return\n        new_context = OpenSSL.SSL.Context(self.method)\n        new_context.set_options(OpenSSL.SSL.OP_NO_SSLv2)\n        new_context.set_options(OpenSSL.SSL.OP_NO_SSLv3)\n        new_context.use_privatekey(key)\n        new_context.use_certificate(cert)\n        connection.set_context(new_context)\n\n    class FakeConnection(object):\n        \"\"\"Fake OpenSSL.SSL.Connection.\"\"\"\n\n        # pylint: disable=too-few-public-methods,missing-docstring\n\n        def __init__(self, connection):\n            self._wrapped = connection\n\n        def __getattr__(self, name):\n            return getattr(self._wrapped, name)\n\n        def shutdown(self, *unused_args):\n            # OpenSSL.SSL.Connection.shutdown doesn't accept any args\n            return self._wrapped.shutdown()\n\n    def accept(self):  # pylint: disable=missing-docstring\n        sock, addr = self.sock.accept()\n\n        context = OpenSSL.SSL.Context(self.method)\n        context.set_options(OpenSSL.SSL.OP_NO_SSLv2)\n        context.set_options(OpenSSL.SSL.OP_NO_SSLv3)\n        context.set_tlsext_servername_callback(self._pick_certificate_cb)\n\n        ssl_sock = self.FakeConnection(OpenSSL.SSL.Connection(context, sock))\n        ssl_sock.set_accept_state()\n\n        logger.debug(\"Performing handshake with %s\", addr)\n        try:\n            ssl_sock.do_handshake()\n        except OpenSSL.SSL.Error as error:\n            # _pick_certificate_cb might have returned without\n            # creating SSL context (wrong server name)\n            raise socket.error(error)\n\n        return ssl_sock, addr\n\n\ndef probe_sni(name, host, port=443, timeout=300,\n              method=_DEFAULT_TLSSNI01_SSL_METHOD, source_address=('', 0)):\n    \"\"\"Probe SNI server for SSL certificate.\n\n    :param bytes name: Byte string to send as the server name in the\n        client hello message.\n    :param bytes host: Host to connect to.\n    :param int port: Port to connect to.\n    :param int timeout: Timeout in seconds.\n    :param method: See `OpenSSL.SSL.Context` for allowed values.\n    :param tuple source_address: Enables multi-path probing (selection\n        of source interface). See `socket.creation_connection` for more\n        info. Available only in Python 2.7+.\n\n    :raises acme.errors.Error: In case of any problems.\n\n    :returns: SSL certificate presented by the server.\n    :rtype: OpenSSL.crypto.X509\n\n    \"\"\"\n    context = OpenSSL.SSL.Context(method)\n    context.set_timeout(timeout)\n\n    socket_kwargs = {} if sys.version_info < (2, 7) else {\n        'source_address': source_address}\n\n    host_protocol_agnostic = None if host == '::' or host == '0' else host\n\n    try:\n        # pylint: disable=star-args\n        logger.debug(\"Attempting to connect to %s:%d%s.\", host_protocol_agnostic, port,\n            \" from {0}:{1}\".format(source_address[0], source_address[1]) if \\\n            socket_kwargs else \"\")\n        sock = socket.create_connection((host_protocol_agnostic, port), **socket_kwargs)\n    except socket.error as error:\n        raise errors.Error(error)\n\n    with contextlib.closing(sock) as client:\n        client_ssl = OpenSSL.SSL.Connection(context, client)\n        client_ssl.set_connect_state()\n        client_ssl.set_tlsext_host_name(name)  # pyOpenSSL>=0.13\n        try:\n            client_ssl.do_handshake()\n            client_ssl.shutdown()\n        except OpenSSL.SSL.Error as error:\n            raise errors.Error(error)\n    return client_ssl.get_peer_certificate()\n\ndef make_csr(private_key_pem, domains, must_staple=False):\n    \"\"\"Generate a CSR containing a list of domains as subjectAltNames.\n\n    :param buffer private_key_pem: Private key, in PEM PKCS#8 format.\n    :param list domains: List of DNS names to include in subjectAltNames of CSR.\n    :param bool must_staple: Whether to include the TLS Feature extension (aka\n        OCSP Must Staple: https://tools.ietf.org/html/rfc7633).\n    :returns: buffer PEM-encoded Certificate Signing Request.\n    \"\"\"\n    private_key = OpenSSL.crypto.load_privatekey(\n        OpenSSL.crypto.FILETYPE_PEM, private_key_pem)\n    csr = OpenSSL.crypto.X509Req()\n    extensions = [\n        OpenSSL.crypto.X509Extension(\n            b'subjectAltName',\n            critical=False,\n            value=', '.join('DNS:' + d for d in domains).encode('ascii')\n        ),\n    ]\n    if must_staple:\n        extensions.append(OpenSSL.crypto.X509Extension(\n            b\"1.3.6.1.5.5.7.1.24\",\n            critical=False,\n            value=b\"DER:30:03:02:01:05\"))\n    csr.add_extensions(extensions)\n    csr.set_pubkey(private_key)\n    csr.set_version(2)\n    csr.sign(private_key, 'sha256')\n    return OpenSSL.crypto.dump_certificate_request(\n        OpenSSL.crypto.FILETYPE_PEM, csr)\n\ndef _pyopenssl_cert_or_req_san(cert_or_req):\n    \"\"\"Get Subject Alternative Names from certificate or CSR using pyOpenSSL.\n\n    .. todo:: Implement directly in PyOpenSSL!\n\n    .. note:: Although this is `acme` internal API, it is used by\n        `letsencrypt`.\n\n    :param cert_or_req: Certificate or CSR.\n    :type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.\n\n    :returns: A list of Subject Alternative Names.\n    :rtype: `list` of `unicode`\n\n    \"\"\"\n    # This function finds SANs by dumping the certificate/CSR to text and\n    # searching for \"X509v3 Subject Alternative Name\" in the text. This method\n    # is used to support PyOpenSSL version 0.13 where the\n    # `_subjectAltNameString` and `get_extensions` methods are not available\n    # for CSRs.\n\n    # constants based on PyOpenSSL certificate/CSR text dump\n    part_separator = \":\"\n    parts_separator = \", \"\n    prefix = \"DNS\" + part_separator\n\n    if isinstance(cert_or_req, OpenSSL.crypto.X509):\n        func = OpenSSL.crypto.dump_certificate\n    else:\n        func = OpenSSL.crypto.dump_certificate_request\n    text = func(OpenSSL.crypto.FILETYPE_TEXT, cert_or_req).decode(\"utf-8\")\n    # WARNING: this function does not support multiple SANs extensions.\n    # Multiple X509v3 extensions of the same type is disallowed by RFC 5280.\n    match = re.search(r\"X509v3 Subject Alternative Name:\\s*(.*)\", text)\n    # WARNING: this function assumes that no SAN can include\n    # parts_separator, hence the split!\n    sans_parts = [] if match is None else match.group(1).split(parts_separator)\n\n    return [part.split(part_separator)[1]\n            for part in sans_parts if part.startswith(prefix)]\n\n\ndef gen_ss_cert(key, domains, not_before=None,\n                validity=(7 * 24 * 60 * 60), force_san=True):\n    \"\"\"Generate new self-signed certificate.\n\n    :type domains: `list` of `unicode`\n    :param OpenSSL.crypto.PKey key:\n    :param bool force_san:\n\n    If more than one domain is provided, all of the domains are put into\n    ``subjectAltName`` X.509 extension and first domain is set as the\n    subject CN. If only one domain is provided no ``subjectAltName``\n    extension is used, unless `force_san` is ``True``.\n\n    \"\"\"\n    assert domains, \"Must provide one or more hostnames for the cert.\"\n    cert = OpenSSL.crypto.X509()\n    cert.set_serial_number(int(binascii.hexlify(OpenSSL.rand.bytes(16)), 16))\n    cert.set_version(2)\n\n    extensions = [\n        OpenSSL.crypto.X509Extension(\n            b\"basicConstraints\", True, b\"CA:TRUE, pathlen:0\"),\n    ]\n\n    cert.get_subject().CN = domains[0]\n    # TODO: what to put into cert.get_subject()?\n    cert.set_issuer(cert.get_subject())\n\n    if force_san or len(domains) > 1:\n        extensions.append(OpenSSL.crypto.X509Extension(\n            b\"subjectAltName\",\n            critical=False,\n            value=b\", \".join(b\"DNS:\" + d.encode() for d in domains)\n        ))\n\n    cert.add_extensions(extensions)\n\n    cert.gmtime_adj_notBefore(0 if not_before is None else not_before)\n    cert.gmtime_adj_notAfter(validity)\n\n    cert.set_pubkey(key)\n    cert.sign(key, \"sha256\")\n    return cert\n", "path": "acme/acme/crypto_util.py"}], "after_files": [{"content": "\"\"\"Crypto utilities.\"\"\"\nimport binascii\nimport contextlib\nimport logging\nimport re\nimport socket\nimport sys\n\nimport OpenSSL\n\nfrom acme import errors\n\n\nlogger = logging.getLogger(__name__)\n\n# TLSSNI01 certificate serving and probing is not affected by SSL\n# vulnerabilities: prober needs to check certificate for expected\n# contents anyway. Working SNI is the only thing that's necessary for\n# the challenge and thus scoping down SSL/TLS method (version) would\n# cause interoperability issues: TLSv1_METHOD is only compatible with\n# TLSv1_METHOD, while SSLv23_METHOD is compatible with all other\n# methods, including TLSv2_METHOD (read more at\n# https://www.openssl.org/docs/ssl/SSLv23_method.html). _serve_sni\n# should be changed to use \"set_options\" to disable SSLv2 and SSLv3,\n# in case it's used for things other than probing/serving!\n_DEFAULT_TLSSNI01_SSL_METHOD = OpenSSL.SSL.SSLv23_METHOD  # type: ignore\n\n\nclass SSLSocket(object):  # pylint: disable=too-few-public-methods\n    \"\"\"SSL wrapper for sockets.\n\n    :ivar socket sock: Original wrapped socket.\n    :ivar dict certs: Mapping from domain names (`bytes`) to\n        `OpenSSL.crypto.X509`.\n    :ivar method: See `OpenSSL.SSL.Context` for allowed values.\n\n    \"\"\"\n    def __init__(self, sock, certs, method=_DEFAULT_TLSSNI01_SSL_METHOD):\n        self.sock = sock\n        self.certs = certs\n        self.method = method\n\n    def __getattr__(self, name):\n        return getattr(self.sock, name)\n\n    def _pick_certificate_cb(self, connection):\n        \"\"\"SNI certificate callback.\n\n        This method will set a new OpenSSL context object for this\n        connection when an incoming connection provides an SNI name\n        (in order to serve the appropriate certificate, if any).\n\n        :param connection: The TLS connection object on which the SNI\n            extension was received.\n        :type connection: :class:`OpenSSL.Connection`\n\n        \"\"\"\n        server_name = connection.get_servername()\n        try:\n            key, cert = self.certs[server_name]\n        except KeyError:\n            logger.debug(\"Server name (%s) not recognized, dropping SSL\",\n                         server_name)\n            return\n        new_context = OpenSSL.SSL.Context(self.method)\n        new_context.set_options(OpenSSL.SSL.OP_NO_SSLv2)\n        new_context.set_options(OpenSSL.SSL.OP_NO_SSLv3)\n        new_context.use_privatekey(key)\n        new_context.use_certificate(cert)\n        connection.set_context(new_context)\n\n    class FakeConnection(object):\n        \"\"\"Fake OpenSSL.SSL.Connection.\"\"\"\n\n        # pylint: disable=too-few-public-methods,missing-docstring\n\n        def __init__(self, connection):\n            self._wrapped = connection\n\n        def __getattr__(self, name):\n            return getattr(self._wrapped, name)\n\n        def shutdown(self, *unused_args):\n            # OpenSSL.SSL.Connection.shutdown doesn't accept any args\n            return self._wrapped.shutdown()\n\n    def accept(self):  # pylint: disable=missing-docstring\n        sock, addr = self.sock.accept()\n\n        context = OpenSSL.SSL.Context(self.method)\n        context.set_options(OpenSSL.SSL.OP_NO_SSLv2)\n        context.set_options(OpenSSL.SSL.OP_NO_SSLv3)\n        context.set_tlsext_servername_callback(self._pick_certificate_cb)\n\n        ssl_sock = self.FakeConnection(OpenSSL.SSL.Connection(context, sock))\n        ssl_sock.set_accept_state()\n\n        logger.debug(\"Performing handshake with %s\", addr)\n        try:\n            ssl_sock.do_handshake()\n        except OpenSSL.SSL.Error as error:\n            # _pick_certificate_cb might have returned without\n            # creating SSL context (wrong server name)\n            raise socket.error(error)\n\n        return ssl_sock, addr\n\n\ndef probe_sni(name, host, port=443, timeout=300,\n              method=_DEFAULT_TLSSNI01_SSL_METHOD, source_address=('', 0)):\n    \"\"\"Probe SNI server for SSL certificate.\n\n    :param bytes name: Byte string to send as the server name in the\n        client hello message.\n    :param bytes host: Host to connect to.\n    :param int port: Port to connect to.\n    :param int timeout: Timeout in seconds.\n    :param method: See `OpenSSL.SSL.Context` for allowed values.\n    :param tuple source_address: Enables multi-path probing (selection\n        of source interface). See `socket.creation_connection` for more\n        info. Available only in Python 2.7+.\n\n    :raises acme.errors.Error: In case of any problems.\n\n    :returns: SSL certificate presented by the server.\n    :rtype: OpenSSL.crypto.X509\n\n    \"\"\"\n    context = OpenSSL.SSL.Context(method)\n    context.set_timeout(timeout)\n\n    socket_kwargs = {} if sys.version_info < (2, 7) else {\n        'source_address': source_address}\n\n    host_protocol_agnostic = None if host == '::' or host == '0' else host\n\n    try:\n        # pylint: disable=star-args\n        logger.debug(\"Attempting to connect to %s:%d%s.\", host_protocol_agnostic, port,\n            \" from {0}:{1}\".format(source_address[0], source_address[1]) if \\\n            socket_kwargs else \"\")\n        sock = socket.create_connection((host_protocol_agnostic, port), **socket_kwargs)\n    except socket.error as error:\n        raise errors.Error(error)\n\n    with contextlib.closing(sock) as client:\n        client_ssl = OpenSSL.SSL.Connection(context, client)\n        client_ssl.set_connect_state()\n        client_ssl.set_tlsext_host_name(name)  # pyOpenSSL>=0.13\n        try:\n            client_ssl.do_handshake()\n            client_ssl.shutdown()\n        except OpenSSL.SSL.Error as error:\n            raise errors.Error(error)\n    return client_ssl.get_peer_certificate()\n\ndef make_csr(private_key_pem, domains, must_staple=False):\n    \"\"\"Generate a CSR containing a list of domains as subjectAltNames.\n\n    :param buffer private_key_pem: Private key, in PEM PKCS#8 format.\n    :param list domains: List of DNS names to include in subjectAltNames of CSR.\n    :param bool must_staple: Whether to include the TLS Feature extension (aka\n        OCSP Must Staple: https://tools.ietf.org/html/rfc7633).\n    :returns: buffer PEM-encoded Certificate Signing Request.\n    \"\"\"\n    private_key = OpenSSL.crypto.load_privatekey(\n        OpenSSL.crypto.FILETYPE_PEM, private_key_pem)\n    csr = OpenSSL.crypto.X509Req()\n    extensions = [\n        OpenSSL.crypto.X509Extension(\n            b'subjectAltName',\n            critical=False,\n            value=', '.join('DNS:' + d for d in domains).encode('ascii')\n        ),\n    ]\n    if must_staple:\n        extensions.append(OpenSSL.crypto.X509Extension(\n            b\"1.3.6.1.5.5.7.1.24\",\n            critical=False,\n            value=b\"DER:30:03:02:01:05\"))\n    csr.add_extensions(extensions)\n    csr.set_pubkey(private_key)\n    csr.set_version(2)\n    csr.sign(private_key, 'sha256')\n    return OpenSSL.crypto.dump_certificate_request(\n        OpenSSL.crypto.FILETYPE_PEM, csr)\n\ndef _pyopenssl_cert_or_req_san(cert_or_req):\n    \"\"\"Get Subject Alternative Names from certificate or CSR using pyOpenSSL.\n\n    .. todo:: Implement directly in PyOpenSSL!\n\n    .. note:: Although this is `acme` internal API, it is used by\n        `letsencrypt`.\n\n    :param cert_or_req: Certificate or CSR.\n    :type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.\n\n    :returns: A list of Subject Alternative Names.\n    :rtype: `list` of `unicode`\n\n    \"\"\"\n    # This function finds SANs by dumping the certificate/CSR to text and\n    # searching for \"X509v3 Subject Alternative Name\" in the text. This method\n    # is used to support PyOpenSSL version 0.13 where the\n    # `_subjectAltNameString` and `get_extensions` methods are not available\n    # for CSRs.\n\n    # constants based on PyOpenSSL certificate/CSR text dump\n    part_separator = \":\"\n    parts_separator = \", \"\n    prefix = \"DNS\" + part_separator\n\n    if isinstance(cert_or_req, OpenSSL.crypto.X509):\n        func = OpenSSL.crypto.dump_certificate\n    else:\n        func = OpenSSL.crypto.dump_certificate_request\n    text = func(OpenSSL.crypto.FILETYPE_TEXT, cert_or_req).decode(\"utf-8\")\n    # WARNING: this function does not support multiple SANs extensions.\n    # Multiple X509v3 extensions of the same type is disallowed by RFC 5280.\n    match = re.search(r\"X509v3 Subject Alternative Name:(?: critical)?\\s*(.*)\", text)\n    # WARNING: this function assumes that no SAN can include\n    # parts_separator, hence the split!\n    sans_parts = [] if match is None else match.group(1).split(parts_separator)\n\n    return [part.split(part_separator)[1]\n            for part in sans_parts if part.startswith(prefix)]\n\n\ndef gen_ss_cert(key, domains, not_before=None,\n                validity=(7 * 24 * 60 * 60), force_san=True):\n    \"\"\"Generate new self-signed certificate.\n\n    :type domains: `list` of `unicode`\n    :param OpenSSL.crypto.PKey key:\n    :param bool force_san:\n\n    If more than one domain is provided, all of the domains are put into\n    ``subjectAltName`` X.509 extension and first domain is set as the\n    subject CN. If only one domain is provided no ``subjectAltName``\n    extension is used, unless `force_san` is ``True``.\n\n    \"\"\"\n    assert domains, \"Must provide one or more hostnames for the cert.\"\n    cert = OpenSSL.crypto.X509()\n    cert.set_serial_number(int(binascii.hexlify(OpenSSL.rand.bytes(16)), 16))\n    cert.set_version(2)\n\n    extensions = [\n        OpenSSL.crypto.X509Extension(\n            b\"basicConstraints\", True, b\"CA:TRUE, pathlen:0\"),\n    ]\n\n    cert.get_subject().CN = domains[0]\n    # TODO: what to put into cert.get_subject()?\n    cert.set_issuer(cert.get_subject())\n\n    if force_san or len(domains) > 1:\n        extensions.append(OpenSSL.crypto.X509Extension(\n            b\"subjectAltName\",\n            critical=False,\n            value=b\", \".join(b\"DNS:\" + d.encode() for d in domains)\n        ))\n\n    cert.add_extensions(extensions)\n\n    cert.gmtime_adj_notBefore(0 if not_before is None else not_before)\n    cert.gmtime_adj_notAfter(validity)\n\n    cert.set_pubkey(key)\n    cert.sign(key, \"sha256\")\n    return cert\n", "path": "acme/acme/crypto_util.py"}]}