Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-acme for openSUSE:Factory 
checked in at 2021-12-21 18:40:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-acme (Old)
 and      /work/SRC/openSUSE:Factory/.python-acme.new.2520 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-acme"

Tue Dec 21 18:40:53 2021 rev:55 rq:941879 version:1.20.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-acme/python-acme.changes  2021-08-11 
11:49:27.129585202 +0200
+++ /work/SRC/openSUSE:Factory/.python-acme.new.2520/python-acme.changes        
2021-12-21 18:41:33.741923967 +0100
@@ -1,0 +2,11 @@
+Mon Dec 13 17:27:51 UTC 2021 - Ferdinand Thiessen <r...@fthiessen.de>
+
+- Update to version 1.20.0
+  * The acme library now supports requesting certificates for
+    IP addresses.
+  * Removed the dependency on chardet from the library.
+    Except for when downloading a certificate in an alternate format,
+    the acme library now assumes all server responses are UTF-8
+    encoded which is required by RFC 8555.
+
+-------------------------------------------------------------------

Old:
----
  acme-1.18.0.tar.gz
  acme-1.18.0.tar.gz.asc

New:
----
  acme-1.20.0.tar.gz
  acme-1.20.0.tar.gz.asc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-acme.spec ++++++
--- /var/tmp/diff_new_pack.65rXBD/_old  2021-12-21 18:41:34.285924455 +0100
+++ /var/tmp/diff_new_pack.65rXBD/_new  2021-12-21 18:41:34.285924455 +0100
@@ -20,7 +20,7 @@
 %define skip_python2 1
 %define libname acme
 Name:           python-%{libname}
-Version:        1.18.0
+Version:        1.20.0
 Release:        0
 Summary:        Python library for the ACME protocol
 License:        Apache-2.0
@@ -29,7 +29,7 @@
 Source1:        
https://files.pythonhosted.org/packages/source/a/%{libname}/%{libname}-%{version}.tar.gz.asc
 Source2:        %{name}.keyring
 BuildRequires:  %{python_module cryptography >= 2.1.4}
-BuildRequires:  %{python_module josepy >= 1.1.0}
+BuildRequires:  %{python_module josepy >= 1.9.0}
 BuildRequires:  %{python_module pyOpenSSL >= 17.3.0}
 BuildRequires:  %{python_module pyRFC3339}
 BuildRequires:  %{python_module pytest}
@@ -40,7 +40,7 @@
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-cryptography >= 2.1.4
-Requires:       python-josepy >= 1.1.0
+Requires:       python-josepy >= 1.9.0
 Requires:       python-pyOpenSSL >= 17.3.0
 Requires:       python-pyRFC3339
 Requires:       python-pytz

++++++ acme-1.18.0.tar.gz -> acme-1.20.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/PKG-INFO new/acme-1.20.0/PKG-INFO
--- old/acme-1.18.0/PKG-INFO    2021-08-03 22:13:30.525709000 +0200
+++ new/acme-1.20.0/PKG-INFO    2021-10-05 15:53:03.645527800 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: acme
-Version: 1.18.0
+Version: 1.20.0
 Summary: ACME protocol implementation in Python
 Home-page: https://github.com/letsencrypt/letsencrypt
 Author: Certbot Project
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme/challenges.py 
new/acme-1.20.0/acme/challenges.py
--- old/acme-1.18.0/acme/challenges.py  2021-08-03 22:12:58.000000000 +0200
+++ new/acme-1.20.0/acme/challenges.py  2021-10-05 15:52:55.000000000 +0200
@@ -7,10 +7,10 @@
 import socket
 from typing import Type
 
-from cryptography.hazmat.primitives import hashes  # type: ignore
+from cryptography.hazmat.primitives import hashes
 import josepy as jose
 from OpenSSL import crypto
-from OpenSSL import SSL  # type: ignore # 
https://github.com/python/typeshed/issues/2052
+from OpenSSL import SSL
 import requests
 
 from acme import crypto_util
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme/client.py 
new/acme-1.20.0/acme/client.py
--- old/acme-1.18.0/acme/client.py      2021-08-03 22:12:58.000000000 +0200
+++ new/acme-1.20.0/acme/client.py      2021-10-05 15:52:55.000000000 +0200
@@ -1,4 +1,7 @@
 """ACME client API."""
+# pylint: disable=too-many-lines
+# This pylint disable can be deleted once the deprecated ACMEv1 code is
+# removed.
 import base64
 import collections
 import datetime
@@ -7,7 +10,9 @@
 import http.client as http_client
 import logging
 import re
+import sys
 import time
+from types import ModuleType
 from typing import cast
 from typing import Dict
 from typing import List
@@ -250,8 +255,6 @@
             URI from which the resource will be downloaded.
 
         """
-        warnings.warn("acme.client.Client (ACMEv1) is deprecated, "
-                      "use acme.client.ClientV2 instead.", 
PendingDeprecationWarning)
         self.key = key
         if net is None:
             net = ClientNetwork(key, alg=alg, verify_ssl=verify_ssl)
@@ -655,11 +658,15 @@
         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())
@@ -830,8 +837,6 @@
     """
 
     def __init__(self, net, key, server):
-        warnings.warn("acme.client.BackwardsCompatibleClientV2 is deprecated, 
use "
-                      "acme.client.ClientV2 instead.", 
PendingDeprecationWarning)
         directory = messages.Directory.from_json(net.get(server).json())
         self.acme_version = self._acme_version_from_directory(directory)
         self.client: Union[Client, ClientV2]
@@ -1149,13 +1154,23 @@
             host, path, _err_no, err_msg = m.groups()
             raise ValueError("Requesting {0}{1}:{2}".format(host, path, 
err_msg))
 
-        # If content is DER, log the base64 of it instead of raw bytes, to keep
-        # binary data out of the logs.
+        # If the Content-Type is DER or an Accept header was sent in the
+        # request, the response may not be UTF-8 encoded. In this case, we
+        # don't set response.encoding and log the base64 response instead of
+        # raw bytes to keep binary data out of the logs. This code can be
+        # simplified to only check for an Accept header in the request when
+        # ACMEv1 support is dropped.
         debug_content: Union[bytes, str]
-        if response.headers.get("Content-Type") == DER_CONTENT_TYPE:
+        if (response.headers.get("Content-Type") == DER_CONTENT_TYPE or
+                "Accept" in kwargs["headers"]):
             debug_content = base64.b64encode(response.content)
         else:
-            debug_content = response.content.decode("utf-8")
+            # We set response.encoding so response.text knows the response is
+            # UTF-8 encoded instead of trying to guess the encoding that was
+            # used which is error prone. This setting affects all future
+            # accesses of .text made on the returned response object as well.
+            response.encoding = "utf-8"
+            debug_content = response.text
         logger.debug('Received response:\nHTTP %d\n%s\n\n%s',
                      response.status_code,
                      "\n".join("{0}: {1}".format(k, v)
@@ -1225,3 +1240,35 @@
         response = self._check_response(response, content_type=content_type)
         self._add_nonce(response)
         return response
+
+
+# This class takes a similar approach to the cryptography project to deprecate 
attributes
+# in public modules. See the _ModuleWithDeprecation class here:
+# 
https://github.com/pyca/cryptography/blob/91105952739442a74582d3e62b3d2111365b0dc7/src/cryptography/utils.py#L129
+class _ClientDeprecationModule:
+    """
+    Internal class delegating to a module, and displaying warnings when 
attributes
+    related to deprecated attributes in the acme.client module.
+    """
+    def __init__(self, module):
+        self.__dict__['_module'] = module
+
+    def __getattr__(self, attr):
+        if attr in ('Client', 'BackwardsCompatibleClientV2'):
+            warnings.warn('The {0} attribute in acme.client is deprecated '
+                          'and will be removed soon.'.format(attr),
+                          DeprecationWarning, stacklevel=2)
+        return getattr(self._module, attr)
+
+    def __setattr__(self, attr, value):  # pragma: no cover
+        setattr(self._module, attr, value)
+
+    def __delattr__(self, attr):  # pragma: no cover
+        delattr(self._module, attr)
+
+    def __dir__(self):  # pragma: no cover
+        return ['_module'] + dir(self._module)
+
+
+# Patching ourselves to warn about deprecation and planned removal of some 
elements in the module.
+sys.modules[__name__] = cast(ModuleType, 
_ClientDeprecationModule(sys.modules[__name__]))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme/crypto_util.py 
new/acme-1.20.0/acme/crypto_util.py
--- old/acme-1.18.0/acme/crypto_util.py 2021-08-03 22:12:58.000000000 +0200
+++ new/acme-1.20.0/acme/crypto_util.py 2021-10-05 15:52:55.000000000 +0200
@@ -11,7 +11,7 @@
 
 import josepy as jose
 from OpenSSL import crypto
-from OpenSSL import SSL  # type: ignore # 
https://github.com/python/typeshed/issues/2052
+from OpenSSL import SSL
 
 from acme import errors
 
@@ -24,7 +24,7 @@
 # https://www.openssl.org/docs/ssl/SSLv23_method.html). _serve_sni
 # should be changed to use "set_options" to disable SSLv2 and SSLv3,
 # in case it's used for things other than probing/serving!
-_DEFAULT_SSL_METHOD = SSL.SSLv23_METHOD  # type: ignore
+_DEFAULT_SSL_METHOD = SSL.SSLv23_METHOD
 
 
 class _DefaultCertSelection:
@@ -169,7 +169,7 @@
             ) if any(source_address) else ""
         )
         socket_tuple: Tuple[str, int] = (host, port)
-        sock = socket.create_connection(socket_tuple, **socket_kwargs)  # 
type: ignore
+        sock = socket.create_connection(socket_tuple, **socket_kwargs)
     except socket.error as error:
         raise errors.Error(error)
 
@@ -187,23 +187,42 @@
     return client_ssl.get_peer_certificate()
 
 
-def make_csr(private_key_pem, domains, must_staple=False):
-    """Generate a CSR containing a list of domains as subjectAltNames.
+def make_csr(private_key_pem, domains=None, must_staple=False, ipaddrs=None):
+    """Generate a CSR containing domains or IPs as subjectAltNames.
 
     :param buffer private_key_pem: Private key, in PEM PKCS#8 format.
     :param list domains: List of DNS names to include in subjectAltNames of 
CSR.
     :param bool must_staple: Whether to include the TLS Feature extension (aka
         OCSP Must Staple: https://tools.ietf.org/html/rfc7633).
+    :param list ipaddrs: List of IPaddress(type ipaddress.IPv4Address or 
ipaddress.IPv6Address)
+    names to include in subbjectAltNames of CSR.
+    params ordered this way for backward competablity when called by 
positional argument.
     :returns: buffer PEM-encoded Certificate Signing Request.
     """
     private_key = crypto.load_privatekey(
         crypto.FILETYPE_PEM, private_key_pem)
     csr = crypto.X509Req()
+    sanlist = []
+    # if domain or ip list not supplied make it empty list so it's easier to 
iterate
+    if domains is None:
+        domains = []
+    if ipaddrs is None:
+        ipaddrs = []
+    if len(domains)+len(ipaddrs) == 0:
+        raise ValueError("At least one of domains or ipaddrs parameter need to 
be not empty")
+    for address in domains:
+        sanlist.append('DNS:' + address)
+    for ips in ipaddrs:
+        sanlist.append('IP:' + ips.exploded)
+    # make sure its ascii encoded
+    san_string = ', '.join(sanlist).encode('ascii')
+    # for IP san it's actually need to be octet-string,
+    # but somewhere downsteam thankfully handle it for us
     extensions = [
         crypto.X509Extension(
             b'subjectAltName',
             critical=False,
-            value=', '.join('DNS:' + d for d in domains).encode('ascii')
+            value=san_string
         ),
     ]
     if must_staple:
@@ -220,6 +239,7 @@
 
 
 def _pyopenssl_cert_or_req_all_names(loaded_cert_or_req):
+    # unlike its name this only outputs DNS names, other type of idents will 
ignored
     common_name = loaded_cert_or_req.get_subject().CN
     sans = _pyopenssl_cert_or_req_san(loaded_cert_or_req)
 
@@ -239,21 +259,57 @@
     :param cert_or_req: Certificate or CSR.
     :type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
 
-    :returns: A list of Subject Alternative Names.
+    :returns: A list of Subject Alternative Names that is DNS.
     :rtype: `list` of `unicode`
 
     """
-    # This function finds SANs by dumping the certificate/CSR to text and
-    # searching for "X509v3 Subject Alternative Name" in the text. This method
-    # is used to support PyOpenSSL version 0.13 where the
-    # `_subjectAltNameString` and `get_extensions` methods are not available
-    # for CSRs.
+    # This function finds SANs with dns name
 
     # constants based on PyOpenSSL certificate/CSR text dump
     part_separator = ":"
-    parts_separator = ", "
     prefix = "DNS" + part_separator
 
+    sans_parts = _pyopenssl_extract_san_list_raw(cert_or_req)
+
+    return [part.split(part_separator)[1]
+            for part in sans_parts if part.startswith(prefix)]
+
+
+def _pyopenssl_cert_or_req_san_ip(cert_or_req):
+    """Get Subject Alternative Names IPs from certificate or CSR using 
pyOpenSSL.
+
+    :param cert_or_req: Certificate or CSR.
+    :type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
+
+    :returns: A list of Subject Alternative Names that are IP Addresses.
+    :rtype: `list` of `unicode`. note that this returns as string, not 
IPaddress object
+
+    """
+
+    # constants based on PyOpenSSL certificate/CSR text dump
+    part_separator = ":"
+    prefix = "IP Address" + part_separator
+
+    sans_parts = _pyopenssl_extract_san_list_raw(cert_or_req)
+
+    return [part[len(prefix):] for part in sans_parts if 
part.startswith(prefix)]
+
+
+def _pyopenssl_extract_san_list_raw(cert_or_req):
+    """Get raw SAN string from cert or csr, parse it as UTF-8 and return.
+
+    :param cert_or_req: Certificate or CSR.
+    :type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
+
+    :returns: raw san strings, parsed byte as utf-8
+    :rtype: `list` of `unicode`
+
+    """
+    # This function finds SANs by dumping the certificate/CSR to text and
+    # searching for "X509v3 Subject Alternative Name" in the text. This method
+    # is used to because in PyOpenSSL version <0.17 `_subjectAltNameString` 
methods are
+    # not able to Parse IP Addresses in subjectAltName string.
+
     if isinstance(cert_or_req, crypto.X509):
         # pylint: disable=line-too-long
         func: Union[Callable[[int, crypto.X509Req], bytes], Callable[[int, 
crypto.X509], bytes]] = crypto.dump_certificate
@@ -262,17 +318,17 @@
     text = func(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:(?: 
critical)?\s*(.*)", text)
+    raw_san = re.search(r"X509v3 Subject Alternative Name:(?: 
critical)?\s*(.*)", text)
+
+    parts_separator = ", "
     # 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)
-
-    return [part.split(part_separator)[1]
-            for part in sans_parts if part.startswith(prefix)]
+    sans_parts = [] if raw_san is None else 
raw_san.group(1).split(parts_separator)
+    return sans_parts
 
 
-def gen_ss_cert(key, domains, not_before=None,
-                validity=(7 * 24 * 60 * 60), force_san=True, extensions=None):
+def gen_ss_cert(key, domains=None, not_before=None,
+                validity=(7 * 24 * 60 * 60), force_san=True, extensions=None, 
ips=None):
     """Generate new self-signed certificate.
 
     :type domains: `list` of `unicode`
@@ -280,6 +336,7 @@
     :param bool force_san:
     :param extensions: List of additional extensions to include in the cert.
     :type extensions: `list` of `OpenSSL.crypto.X509Extension`
+    :type ips: `list` of (`ipaddress.IPv4Address` or `ipaddress.IPv6Address`)
 
     If more than one domain is provided, all of the domains are put into
     ``subjectAltName`` X.509 extension and first domain is set as the
@@ -287,28 +344,39 @@
     extension is used, unless `force_san` is ``True``.
 
     """
-    assert domains, "Must provide one or more hostnames for the cert."
+    assert domains or ips, "Must provide one or more hostnames or IPs for the 
cert."
+
     cert = crypto.X509()
     cert.set_serial_number(int(binascii.hexlify(os.urandom(16)), 16))
     cert.set_version(2)
 
     if extensions is None:
         extensions = []
-
+    if domains is None:
+        domains = []
+    if ips is None:
+        ips = []
     extensions.append(
         crypto.X509Extension(
             b"basicConstraints", True, b"CA:TRUE, pathlen:0"),
     )
 
-    cert.get_subject().CN = domains[0]
+    if len(domains) > 0:
+        cert.get_subject().CN = domains[0]
     # TODO: what to put into cert.get_subject()?
     cert.set_issuer(cert.get_subject())
 
-    if force_san or len(domains) > 1:
+    sanlist = []
+    for address in domains:
+        sanlist.append('DNS:' + address)
+    for ip in ips:
+        sanlist.append('IP:' + ip.exploded)
+    san_string = ', '.join(sanlist).encode('ascii')
+    if force_san or len(domains) > 1 or len(ips) > 0:
         extensions.append(crypto.X509Extension(
             b"subjectAltName",
             critical=False,
-            value=b", ".join(b"DNS:" + d.encode() for d in domains)
+            value=san_string
         ))
 
     cert.add_extensions(extensions)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme/magic_typing.py 
new/acme-1.20.0/acme/magic_typing.py
--- old/acme-1.18.0/acme/magic_typing.py        2021-08-03 22:12:58.000000000 
+0200
+++ new/acme-1.20.0/acme/magic_typing.py        2021-10-05 15:52:55.000000000 
+0200
@@ -6,7 +6,7 @@
 """
 import warnings
 from typing import *  # pylint: disable=wildcard-import, unused-wildcard-import
-from typing import Collection, IO  # type: ignore
+from typing import Collection, IO
 
 warnings.warn("acme.magic_typing is deprecated and will be removed in a future 
release.",
               DeprecationWarning)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme/messages.py 
new/acme-1.20.0/acme/messages.py
--- old/acme-1.18.0/acme/messages.py    2021-08-03 22:12:58.000000000 +0200
+++ new/acme-1.20.0/acme/messages.py    2021-10-05 15:52:55.000000000 +0200
@@ -126,7 +126,7 @@
             if part is not None).decode()
 
 
-class _Constant(jose.JSONDeSerializable, Hashable):  # type: ignore
+class _Constant(jose.JSONDeSerializable, Hashable):
     """ACME constant."""
     __slots__ = ('name',)
     POSSIBLE_NAMES: Dict[str, '_Constant'] = NotImplemented
@@ -172,7 +172,9 @@
 class IdentifierType(_Constant):
     """ACME identifier type."""
     POSSIBLE_NAMES: Dict[str, 'IdentifierType'] = {}
+    # class def ends here
 IDENTIFIER_FQDN = IdentifierType('dns')  # IdentifierDNS in Boulder
+IDENTIFIER_IP = IdentifierType('ip') # IdentifierIP in pebble - not in Boulder 
yet
 
 
 class Identifier(jose.JSONObjectWithFields):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme/util.py new/acme-1.20.0/acme/util.py
--- old/acme-1.18.0/acme/util.py        2021-08-03 22:12:58.000000000 +0200
+++ new/acme-1.20.0/acme/util.py        2021-10-05 15:52:55.000000000 +0200
@@ -1,6 +1,5 @@
 """ACME utilities."""
 
-
 def map_keys(dikt, func):
     """Map dictionary keys."""
     return {func(key): value for key, value in dikt.items()}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme.egg-info/PKG-INFO 
new/acme-1.20.0/acme.egg-info/PKG-INFO
--- old/acme-1.18.0/acme.egg-info/PKG-INFO      2021-08-03 22:13:30.000000000 
+0200
+++ new/acme-1.20.0/acme.egg-info/PKG-INFO      2021-10-05 15:53:03.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: acme
-Version: 1.18.0
+Version: 1.20.0
 Summary: ACME protocol implementation in Python
 Home-page: https://github.com/letsencrypt/letsencrypt
 Author: Certbot Project
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme.egg-info/SOURCES.txt 
new/acme-1.20.0/acme.egg-info/SOURCES.txt
--- old/acme-1.18.0/acme.egg-info/SOURCES.txt   2021-08-03 22:13:30.000000000 
+0200
+++ new/acme-1.20.0/acme.egg-info/SOURCES.txt   2021-10-05 15:53:03.000000000 
+0200
@@ -39,7 +39,6 @@
 docs/api/standalone.rst
 docs/man/jws.rst
 examples/http01_example.py
-examples/standalone/README
 tests/challenges_test.py
 tests/client_test.py
 tests/crypto_util_test.py
@@ -55,6 +54,8 @@
 tests/testdata/README
 tests/testdata/cert-100sans.pem
 tests/testdata/cert-idnsans.pem
+tests/testdata/cert-ipsans.pem
+tests/testdata/cert-ipv6sans.pem
 tests/testdata/cert-nocn.der
 tests/testdata/cert-san.pem
 tests/testdata/cert.der
@@ -63,6 +64,9 @@
 tests/testdata/csr-100sans.pem
 tests/testdata/csr-6sans.pem
 tests/testdata/csr-idnsans.pem
+tests/testdata/csr-ipsans.pem
+tests/testdata/csr-ipv6sans.pem
+tests/testdata/csr-mixed.pem
 tests/testdata/csr-nosans.pem
 tests/testdata/csr-san.pem
 tests/testdata/csr.der
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/acme.egg-info/requires.txt 
new/acme-1.20.0/acme.egg-info/requires.txt
--- old/acme-1.18.0/acme.egg-info/requires.txt  2021-08-03 22:13:30.000000000 
+0200
+++ new/acme-1.20.0/acme.egg-info/requires.txt  2021-10-05 15:53:03.000000000 
+0200
@@ -1,6 +1,5 @@
-chardet
 cryptography>=2.1.4
-josepy>=1.1.0
+josepy>=1.9.0
 PyOpenSSL>=17.3.0
 pyrfc3339
 pytz
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/examples/standalone/README 
new/acme-1.20.0/examples/standalone/README
--- old/acme-1.18.0/examples/standalone/README  2021-08-03 22:12:58.000000000 
+0200
+++ new/acme-1.20.0/examples/standalone/README  1970-01-01 01:00:00.000000000 
+0100
@@ -1,2 +0,0 @@
-python -m acme.standalone -p 1234
-curl -k https://localhost:1234
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/setup.py new/acme-1.20.0/setup.py
--- old/acme-1.18.0/setup.py    2021-08-03 22:12:59.000000000 +0200
+++ new/acme-1.20.0/setup.py    2021-10-05 15:52:56.000000000 +0200
@@ -3,18 +3,11 @@
 from setuptools import find_packages
 from setuptools import setup
 
-version = '1.18.0'
+version = '1.20.0'
 
 install_requires = [
-    # This dependency just exists to ensure that chardet is installed along
-    # with requests so it will use it instead of charset_normalizer. See
-    # https://github.com/certbot/certbot/issues/8964 for more info.
-    'chardet',
     'cryptography>=2.1.4',
-    # formerly known as acme.jose:
-    # 1.1.0+ is required to avoid the warnings described at
-    # https://github.com/certbot/josepy/issues/13.
-    'josepy>=1.1.0',
+    'josepy>=1.9.0',
     'PyOpenSSL>=17.3.0',
     'pyrfc3339',
     'pytz',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/tests/client_test.py 
new/acme-1.20.0/tests/client_test.py
--- old/acme-1.18.0/tests/client_test.py        2021-08-03 22:12:58.000000000 
+0200
+++ new/acme-1.20.0/tests/client_test.py        2021-10-05 15:52:55.000000000 
+0200
@@ -3,6 +3,7 @@
 import copy
 import datetime
 import http.client as http_client
+import ipaddress
 import json
 import unittest
 from typing import Dict
@@ -23,6 +24,7 @@
 CERT_DER = test_util.load_vector('cert.der')
 CERT_SAN_PEM = test_util.load_vector('cert-san.pem')
 CSR_SAN_PEM = test_util.load_vector('csr-san.pem')
+CSR_MIXED_PEM = test_util.load_vector('csr-mixed.pem')
 KEY = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem'))
 KEY2 = jose.JWKRSA.load(test_util.load_vector('rsa256_key.pem'))
 
@@ -740,7 +742,7 @@
         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)
+            authorizations=[self.authzr, self.authzr2], csr_pem=CSR_MIXED_PEM)
 
     def test_new_account(self):
         self.response.status_code = http_client.CREATED
@@ -770,7 +772,7 @@
 
         with mock.patch('acme.client.ClientV2._post_as_get') as 
mock_post_as_get:
             mock_post_as_get.side_effect = (authz_response, authz_response2)
-            self.assertEqual(self.client.new_order(CSR_SAN_PEM), self.orderr)
+            self.assertEqual(self.client.new_order(CSR_MIXED_PEM), self.orderr)
 
     @mock.patch('acme.client.datetime')
     def test_poll_and_finalize(self, mock_datetime):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/tests/crypto_util_test.py 
new/acme-1.20.0/tests/crypto_util_test.py
--- old/acme-1.18.0/tests/crypto_util_test.py   2021-08-03 22:12:58.000000000 
+0200
+++ new/acme-1.20.0/tests/crypto_util_test.py   2021-10-05 15:52:55.000000000 
+0200
@@ -1,5 +1,6 @@
 """Tests for acme.crypto_util."""
 import itertools
+import ipaddress
 import socket
 import socketserver
 import threading
@@ -108,7 +109,6 @@
 class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
     """Test for acme.crypto_util._pyopenssl_cert_or_req_san."""
 
-
     @classmethod
     def _call(cls, loader, name):
         # pylint: disable=protected-access
@@ -174,9 +174,50 @@
                          ['chicago-cubs.venafi.example', 
'cubs.venafi.example'])
 
 
+class PyOpenSSLCertOrReqSANIPTest(unittest.TestCase):
+    """Test for acme.crypto_util._pyopenssl_cert_or_req_san_ip."""
+
+    @classmethod
+    def _call(cls, loader, name):
+        # pylint: disable=protected-access
+        from acme.crypto_util import _pyopenssl_cert_or_req_san_ip
+        return _pyopenssl_cert_or_req_san_ip(loader(name))
+
+    def _call_cert(self, name):
+        return self._call(test_util.load_cert, name)
+
+    def _call_csr(self, name):
+        return self._call(test_util.load_csr, name)
+
+    def test_cert_no_sans(self):
+        self.assertEqual(self._call_cert('cert.pem'), [])
+
+    def test_csr_no_sans(self):
+        self.assertEqual(self._call_csr('csr-nosans.pem'), [])
+
+    def test_cert_domain_sans(self):
+        self.assertEqual(self._call_cert('cert-san.pem'), [])
+
+    def test_csr_domain_sans(self):
+        self.assertEqual(self._call_csr('csr-san.pem'), [])
+
+    def test_cert_ip_two_sans(self):
+        self.assertEqual(self._call_cert('cert-ipsans.pem'), ['192.0.2.145', 
'203.0.113.1'])
 
-class RandomSnTest(unittest.TestCase):
-    """Test for random certificate serial numbers."""
+    def test_csr_ip_two_sans(self):
+        self.assertEqual(self._call_csr('csr-ipsans.pem'), ['192.0.2.145', 
'203.0.113.1'])
+
+    def test_csr_ipv6_sans(self):
+        self.assertEqual(self._call_csr('csr-ipv6sans.pem'),
+                         ['0:0:0:0:0:0:0:1', 
'A3BE:32F3:206E:C75D:956:CEE:9858:5EC5'])
+
+    def test_cert_ipv6_sans(self):
+        self.assertEqual(self._call_cert('cert-ipv6sans.pem'),
+                         ['0:0:0:0:0:0:0:1', 
'A3BE:32F3:206E:C75D:956:CEE:9858:5EC5'])
+
+
+class GenSsCertTest(unittest.TestCase):
+    """Test for gen_ss_cert (generation of self-signed cert)."""
 
 
     def setUp(self):
@@ -187,11 +228,19 @@
 
     def test_sn_collisions(self):
         from acme.crypto_util import gen_ss_cert
-
         for _ in range(self.cert_count):
-            cert = gen_ss_cert(self.key, ['dummy'], force_san=True)
+            cert = gen_ss_cert(self.key, ['dummy'], force_san=True,
+                               ips=[ipaddress.ip_address("10.10.10.10")])
             self.serial_num.append(cert.get_serial_number())
-        self.assertGreater(len(set(self.serial_num)), 1)
+        self.assertGreaterEqual(len(set(self.serial_num)), self.cert_count)
+
+
+    def test_no_name(self):
+        from acme.crypto_util import gen_ss_cert
+        with self.assertRaises(AssertionError):
+            gen_ss_cert(self.key, ips=[ipaddress.ip_address("1.1.1.1")])
+            gen_ss_cert(self.key)
+
 
 class MakeCSRTest(unittest.TestCase):
     """Test for standalone functions."""
@@ -223,6 +272,27 @@
                 ).get_data(),
             )
 
+    def test_make_csr_ip(self):
+        csr_pem = self._call_with_key(["a.example"], False, 
[ipaddress.ip_address('127.0.0.1'), ipaddress.ip_address('::1')])
+        self.assertIn(b'--BEGIN CERTIFICATE REQUEST--' , csr_pem)
+        self.assertIn(b'--END CERTIFICATE REQUEST--' , csr_pem)
+        csr = OpenSSL.crypto.load_certificate_request(
+            OpenSSL.crypto.FILETYPE_PEM, csr_pem)
+        # In pyopenssl 0.13 (used with TOXENV=py27-oldest), csr objects don't
+        # have a get_extensions() method, so we skip this test if the method
+        # isn't available.
+        if hasattr(csr, 'get_extensions'):
+            self.assertEqual(len(csr.get_extensions()), 1)
+            self.assertEqual(csr.get_extensions()[0].get_data(),
+                             OpenSSL.crypto.X509Extension(
+                                 b'subjectAltName',
+                                 critical=False,
+                                 value=b'DNS:a.example, IP:127.0.0.1, IP:::1',
+                             ).get_data(),
+                             )
+            # for IP san it's actually need to be octet-string,
+            # but somewhere downstream thankfully handle it for us
+
     def test_make_csr_must_staple(self):
         csr_pem = self._call_with_key(["a.example"], must_staple=True)
         csr = OpenSSL.crypto.load_certificate_request(
@@ -241,6 +311,9 @@
             self.assertEqual(len(must_staple_exts), 1,
                 "Expected exactly one Must Staple extension")
 
+    def test_make_csr_without_hostname(self):
+        self.assertRaises(ValueError, self._call_with_key)
+
 
 class DumpPyopensslChainTest(unittest.TestCase):
     """Test for dump_pyopenssl_chain."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/tests/testdata/cert-ipsans.pem 
new/acme-1.20.0/tests/testdata/cert-ipsans.pem
--- old/acme-1.18.0/tests/testdata/cert-ipsans.pem      1970-01-01 
01:00:00.000000000 +0100
+++ new/acme-1.20.0/tests/testdata/cert-ipsans.pem      2021-10-05 
15:52:55.000000000 +0200
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDizCCAnOgAwIBAgIIPNBLQXwhoUkwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
+AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAxNzNiMjYwHhcNMjAwNTI5MTkxODA5
+WhcNMjUwNTI5MTkxODA5WjAWMRQwEgYDVQQDEwsxOTIuMC4yLjE0NTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALyChb+NDA26GF1AfC0nzEdfOTchKw0h
+q41xEjonvg5UXgZf/aH/ntvugIkYP0MaFifNAjebOVVsemEVEtyWcUKTfBHKZGbZ
+ukTDwFIjfTccCfo6U/B2H7ZLzJIywl8DcUw9DypadeQBm8PS0VVR2ncy73dvaqym
+crhAwlASyXU0mhLqRDMMxfg5Bn/FWpcsIcDpLmPn8Q/FvdRc2t5ryBNw/aWOlwqT
+Oy16nbfLj2T0zG1A3aPuD+eT/JFUe/o3K7R+FAx7wt+RziQO46wLVVF1SueZUrIU
+zqN04Gl8Kt1WM2SniZ0gq/rORUNcPtT0NAEsEslTQfA+Trq6j2peqyMCAwEAAaOB
+yjCBxzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
+BwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHj1mwZzP//nMIH2i58NRUl/arHn
+MB8GA1UdIwQYMBaAFF5DVAKabvIUvKFHGouscA2Qdpe6MDEGCCsGAQUFBwEBBCUw
+IzAhBggrBgEFBQcwAYYVaHR0cDovLzEyNy4wLjAuMTo0MDAyMBUGA1UdEQQOMAyH
+BMAAApGHBMsAcQEwDQYJKoZIhvcNAQELBQADggEBAHjSgDg76/UCIMSYddyhj18r
+LdNKjA7p8ovnErSkebFT4lIZ9f3Sma9moNr0w64M33NamuFyHe/KTdk90mvoW8Uu
+26aDekiRIeeMakzbAtDKn67tt2tbedKIYRATcSYVwsV46uZKbM621dZKIjjxOWpo
+IY6rZYrku8LYhoXJXOqRduV3cTRVuTm5bBa9FfVNtt6N1T5JOtKKDEhuSaF4RSug
+PDy3hQIiHrVvhPfVrXU3j6owz/8UCS5549inES9ONTFrvM9o0H1R/MsmGNXR5hF5
+iJqHKC7n8LZujhVnoFIpHu2Dsiefbfr+yRYJS4I+ezy6Nq/Ok8rc8zp0eoX+uyY=
+-----END CERTIFICATE-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/tests/testdata/cert-ipv6sans.pem 
new/acme-1.20.0/tests/testdata/cert-ipv6sans.pem
--- old/acme-1.18.0/tests/testdata/cert-ipv6sans.pem    1970-01-01 
01:00:00.000000000 +0100
+++ new/acme-1.20.0/tests/testdata/cert-ipv6sans.pem    2021-10-05 
15:52:55.000000000 +0200
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDmzCCAoOgAwIBAgIIFdxeZP+v2rgwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE
+AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSA0M2M5NTcwHhcNMjAwNTMwMDQwNzMw
+WhcNMjUwNTMwMDQwNzMwWjAOMQwwCgYDVQQDEwM6OjEwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC7VidVduJvqKtrSH0fw6PjE0cqL4Kfzo7klexWUkHG
+KVAa0fRVZFZ462jxKOt417V2U4WJQ6WHHO9PJ+3gW62d/MhCw8FRtUQS4nYFjqB6
+32+RFU21VRN7cWoQEqSwnEPbh/v/zv/KS5JhQ+swWUo79AOLm1kjnZWCKtcqh1Lc
+Ug5Tkpot6luoxTKp52MkchvXDpj0q2B/XpLJ8/pw5cqjv7mH12EDOK2HXllA+WwX
+ZpstcEhaA4FqtaHOW/OHnwTX5MUbINXE5YYHVEDR6moVM31/W/3pe9NDUMTDE7Si
+lVQnZbXM9NYbzZqlh+WhemDWwnIfGI6rtsfNEiirVEOlAgMBAAGjgeIwgd8wDgYD
+VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV
+HRMBAf8EAjAAMB0GA1UdDgQWBBS8DL+MZfDIy6AKky69Tgry2Vxq5DAfBgNVHSME
+GDAWgBRAsFqVenRRKgB1YPzWKzb9bzZ/ozAxBggrBgEFBQcBAQQlMCMwIQYIKwYB
+BQUHMAGGFWh0dHA6Ly8xMjcuMC4wLjE6NDAwMjAtBgNVHREEJjAkhxAAAAAAAAAA
+AAAAAAAAAAABhxCjvjLzIG7HXQlWDO6YWF7FMA0GCSqGSIb3DQEBCwUAA4IBAQBY
+M9UTZ3uaKMQ+He9kWR3p9jh6hTSD0FNi79ZdfkG0lgSzhhduhN7OhzQH2ihUUfa6
+rtKTw74fGbszhizCd9UB8YPKlm3si1Xbg6ZUQlA1RtoQo7RUGEa6ZbR68PKGm9Go
+hTTFIl/JS8jzxBR8jywZdyqtprUx+nnNUDiNk0hJtFLhw7OJH0AHlAUNqHsfD08m
+HXRdaV6q14HXU5g31slBat9H4D6tCU/2uqBURwW0wVdnqh4QeRfAeqiatJS9EmSF
+ctbc7n894Idy2Xce7NFoIy5cht3m6Rd42o/LmBsJopBmQcDPZT70/XzRtc2qE0cS
+CzBIGQHUJ6BfmBjrCQnp
+-----END CERTIFICATE-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/tests/testdata/csr-ipsans.pem 
new/acme-1.20.0/tests/testdata/csr-ipsans.pem
--- old/acme-1.18.0/tests/testdata/csr-ipsans.pem       1970-01-01 
01:00:00.000000000 +0100
+++ new/acme-1.20.0/tests/testdata/csr-ipsans.pem       2021-10-05 
15:52:55.000000000 +0200
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICbTCCAVUCAQIwADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKT/
+CE7Y5EYBvI4p7Frt763upIKHDHO/R5/TWMjG8Jm9qTMui8sbMgyh2Yh+lR/j/5Xd
+tQrhgC6wx10MrW2+3JtYS88HP1p6si8zU1dbK34n3NyyklR2RivW0R7dXgnYNy7t
+5YcDYLCrbRMIPINV/uHrmzIHWYUDNcZVdAfIM2AHfKYuV6Mepcn///5GR+l4GcAh
+Nkf9CW8OdAIuKdbyLCxVr0mUW/vJz1b12uxPsgUdax9sjXgZdT4pfMXADsFd1NeF
+atpsXU073inqtHru+2F9ijHTQ75TC+u/rr6eYl3BnBntac0gp/ADtDBii7/Q1JOO
+Bhq7xJNqqxIEdiyM7zcCAwEAAaAoMCYGCSqGSIb3DQEJDjEZMBcwFQYDVR0RBA4w
+DIcEwAACkYcEywBxATANBgkqhkiG9w0BAQsFAAOCAQEADG5g3zdbSCaXpZhWHkzE
+Mek3f442TUE1pB+ITRpthmM4N3zZWETYmbLCIAO624uMrRnbCCMvAoLs/L/9ETg/
+XMMFtonQC8u9i9tV8B1ceBh8lpIfa+8b9TMWH3bqnrbWQ+YIl+Yd0gXiCZWJ9vK4
+eM1Gddu/2bR6s/k4h/XAWRgEexqk57EHr1z0N+T9OoX939n3mVcNI+u9kfd5VJ0z
+VyA3R8WR6T6KlEl5P5pcWe5Kuyhi7xMmLVImXqBtvKq4O1AMfM+gQr/yn9aE8IRq
+khP7JrMBLUIub1c/qu2TfvnynNPSM/ZcOX+6PHdHmRkR3nI0Ndpv7Ntv31FTplAm
+Dw==
+-----END CERTIFICATE REQUEST-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/tests/testdata/csr-ipv6sans.pem 
new/acme-1.20.0/tests/testdata/csr-ipv6sans.pem
--- old/acme-1.18.0/tests/testdata/csr-ipv6sans.pem     1970-01-01 
01:00:00.000000000 +0100
+++ new/acme-1.20.0/tests/testdata/csr-ipv6sans.pem     2021-10-05 
15:52:55.000000000 +0200
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIChTCCAW0CAQIwADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOIc
+UAppcqJfTkSqqOFqGt1v7lIJZPOcF4bcKI3d5cHAGbOuVxbC7uMaDuObwYLzoiED
+qnvs1NaEq2phO6KsgGESB7IE2LUjJivO7OnSZjNRpL5si/9egvBiNCn/50lULaWG
+gLEuyMfk3awZy2mVAymy7Grhbx069A4TH8TqsHuq2RpKyuDL27e/jUt6yYecb3pu
+hWMiWy3segif4tI46pkOW0/I6DpxyYD2OqOvzxm/voS9RMqE2+7YJA327H7bEi3N
+lJZEZ1zy7clZ9ga5fBQaetzbg2RyxTrZ7F919NQXSFoXgxb10Eg64wIpz0L3ooCm
+GEHehsZZexa3J5ccIvMCAwEAAaBAMD4GCSqGSIb3DQEJDjExMC8wLQYDVR0RBCYw
+JIcQAAAAAAAAAAAAAAAAAAAAAYcQo74y8yBux10JVgzumFhexTANBgkqhkiG9w0B
+AQsFAAOCAQEALvwVn0A/JPTCiNzcozHFnp5M23C9PXCplWc5u4k34d4XXzpSeFDz
+fL4gy7NpYIueme2K2ppw2j3PNQUdR6vQ5a75sriegWYrosL+7Q6Joh51ZyEUZQoD
+mNl4M4S4oX85EaChR6NFGBywTfjFarYi32XBTbFE7rK8N8KM+DQkNdwL1MXqaHWz
+F1obQKpNXlLedbCBOteV5Eg4zG3565zu/Gw/NhwzzV3mQmgxUcd1sMJxAfHQz4Vl
+ImLL+xMcR03nDsH2bgtDbK2tJm7WszSxA9tC+Xp2lRewxrnQloRWPYDz177WGQ5Q
+SoGDzTTtA6uWZxG8h7CkNLOGvA8LtU2rNA==
+-----END CERTIFICATE REQUEST-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acme-1.18.0/tests/testdata/csr-mixed.pem 
new/acme-1.20.0/tests/testdata/csr-mixed.pem
--- old/acme-1.18.0/tests/testdata/csr-mixed.pem        1970-01-01 
01:00:00.000000000 +0100
+++ new/acme-1.20.0/tests/testdata/csr-mixed.pem        2021-10-05 
15:52:55.000000000 +0200
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICdjCCAV4CAQIwADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMXq
+v1y8EIcCbaUIzCtOcLkLS0MJ35oS+6DmV5WB1A0cIk6YrjsHIsY2lwMm13BWIvmw
+tY+Y6n0rr7eViNx5ZRGHpHEI/TL3Neb+VefTydL5CgvK3dd4ex2kSbTaed3fmpOx
+qMajEduwNcZPCcmoEXPkfrCP8w2vKQUkQ+JRPcdX1nTuzticeRP5B7YCmJsmxkEh
+Y0tzzZ+NIRDARoYNofefY86h3e5q66gtJxccNchmIM3YQahhg5n3Xoo8hGfM/TIc
+R7ncCBCLO6vtqo0QFva/NQODrgOmOsmgvqPkUWQFdZfWM8yIaU826dktx0CPB78t
+TudnJ1rBRvGsjHMsZikCAwEAAaAxMC8GCSqGSIb3DQEJDjEiMCAwHgYDVR0RBBcw
+FYINYS5leGVtcGxlLmNvbYcEwAACbzANBgkqhkiG9w0BAQsFAAOCAQEAdGMcRCxq
+1X09gn1TNdMt64XUv+wdJCKDaJ+AgyIJj7QvVw8H5k7dOnxS4I+a/yo4jE+LDl2/
+AuHcBLFEI4ddewdJSMrTNZjuRYuOdr3KP7fL7MffICSBi45vw5EOXg0tnjJCEiKu
+6gcJgbLSP5JMMd7Haf33Q/VWsmHofR3VwOMdrnakwAU3Ff5WTuXTNVhL1kT/uLFX
+yW1ru6BF4unwNqSR2UeulljpNfRBsiN4zJK11W6n9KT0NkBr9zY5WCM4sW7i8k9V
+TeypWGo3jBKzYAGeuxZsB97U77jZ2lrGdBLZKfbcjnTeRVqCvCRrui4El7UGYFmj
+7s6OJyWx5DSV8w==
+-----END CERTIFICATE REQUEST-----

Reply via email to