import ssl
-.. function:: match_hostname(cert, hostname)
-
- Verify that *cert* (in decoded format as returned by
- :meth:`SSLSocket.getpeercert`) matches the given *hostname*. The rules
- applied are those for checking the identity of HTTPS servers as outlined
- in :rfc:`2818`, :rfc:`5280` and :rfc:`6125`. In addition to HTTPS, this
- function should be suitable for checking the identity of servers in
- various SSL-based protocols such as FTPS, IMAPS, POPS and others.
-
- :exc:`CertificateError` is raised on failure. On success, the function
- returns nothing::
-
- >>> cert = {'subject': ((('commonName', 'example.com'),),)}
- >>> ssl.match_hostname(cert, "example.com")
- >>> ssl.match_hostname(cert, "example.org")
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "/home/py3k/Lib/ssl.py", line 130, in match_hostname
- ssl.CertificateError: hostname 'example.org' doesn't match 'example.com'
-
- .. versionadded:: 3.2
-
- .. versionchanged:: 3.3.3
- The function now follows :rfc:`6125`, section 6.4.3 and does neither
- match multiple wildcards (e.g. ``*.*.com`` or ``*a*.example.org``) nor
- a wildcard inside an internationalized domain names (IDN) fragment.
- IDN A-labels such as ``www*.xn--pthon-kva.org`` are still supported,
- but ``x*.python.org`` no longer matches ``xn--tda.python.org``.
-
- .. versionchanged:: 3.5
- Matching of IP addresses, when present in the subjectAltName field
- of the certificate, is now supported.
-
- .. versionchanged:: 3.7
- The function is no longer used to TLS connections. Hostname matching
- is now performed by OpenSSL.
-
- Allow wildcard when it is the leftmost and the only character
- in that segment. Partial wildcards like ``www*.example.com`` are no
- longer supported.
-
- .. deprecated:: 3.7
-
.. function:: cert_time_to_seconds(cert_time)
Return the time in seconds since the Epoch, given the ``cert_time``
'subjectAltName': (('DNS', '*.eff.org'), ('DNS', 'eff.org')),
'version': 3}
- .. note::
-
- To validate a certificate for a particular service, you can use the
- :func:`match_hostname` function.
-
If the ``binary_form`` parameter is :const:`True`, and a certificate was
provided, this method returns the DER-encoded form of the entire certificate
as a sequence of bytes, or :const:`None` if the peer did not provide a
:const:`None` if you used :const:`CERT_NONE` (rather than
:const:`CERT_OPTIONAL` or :const:`CERT_REQUIRED`).
+ See also :attr:`SSLContext.check_hostname`.
+
.. versionchanged:: 3.2
The returned dictionary includes additional items such as ``issuer``
and ``notBefore``.
:const:`CERT_REQUIRED`. However, it is in itself not sufficient; you also
have to check that the server certificate, which can be obtained by calling
:meth:`SSLSocket.getpeercert`, matches the desired service. For many
-protocols and applications, the service can be identified by the hostname;
-in this case, the :func:`match_hostname` function can be used. This common
-check is automatically performed when :attr:`SSLContext.check_hostname` is
-enabled.
+protocols and applications, the service can be identified by the hostname.
+This common check is automatically performed when
+:attr:`SSLContext.check_hostname` is enabled.
.. versionchanged:: 3.7
Hostname matchings is now performed by OpenSSL. Python no longer uses
"""Wrapping with a badly formatted key (syntax error)"""
self.bad_cert_test("badkey.pem")
- @ignore_deprecation
- def test_match_hostname(self):
- def ok(cert, hostname):
- ssl.match_hostname(cert, hostname)
- def fail(cert, hostname):
- self.assertRaises(ssl.CertificateError,
- ssl.match_hostname, cert, hostname)
-
- # -- Hostname matching --
-
- cert = {'subject': ((('commonName', 'example.com'),),)}
- ok(cert, 'example.com')
- ok(cert, 'ExAmple.cOm')
- fail(cert, 'www.example.com')
- fail(cert, '.example.com')
- fail(cert, 'example.org')
- fail(cert, 'exampleXcom')
-
- cert = {'subject': ((('commonName', '*.a.com'),),)}
- ok(cert, 'foo.a.com')
- fail(cert, 'bar.foo.a.com')
- fail(cert, 'a.com')
- fail(cert, 'Xa.com')
- fail(cert, '.a.com')
-
- # only match wildcards when they are the only thing
- # in left-most segment
- cert = {'subject': ((('commonName', 'f*.com'),),)}
- fail(cert, 'foo.com')
- fail(cert, 'f.com')
- fail(cert, 'bar.com')
- fail(cert, 'foo.a.com')
- fail(cert, 'bar.foo.com')
-
- # NULL bytes are bad, CVE-2013-4073
- cert = {'subject': ((('commonName',
- 'null.python.org\x00example.org'),),)}
- ok(cert, 'null.python.org\x00example.org') # or raise an error?
- fail(cert, 'example.org')
- fail(cert, 'null.python.org')
-
- # error cases with wildcards
- cert = {'subject': ((('commonName', '*.*.a.com'),),)}
- fail(cert, 'bar.foo.a.com')
- fail(cert, 'a.com')
- fail(cert, 'Xa.com')
- fail(cert, '.a.com')
-
- cert = {'subject': ((('commonName', 'a.*.com'),),)}
- fail(cert, 'a.foo.com')
- fail(cert, 'a..com')
- fail(cert, 'a.com')
-
- # wildcard doesn't match IDNA prefix 'xn--'
- idna = 'püthon.python.org'.encode("idna").decode("ascii")
- cert = {'subject': ((('commonName', idna),),)}
- ok(cert, idna)
- cert = {'subject': ((('commonName', 'x*.python.org'),),)}
- fail(cert, idna)
- cert = {'subject': ((('commonName', 'xn--p*.python.org'),),)}
- fail(cert, idna)
-
- # wildcard in first fragment and IDNA A-labels in sequent fragments
- # are supported.
- idna = 'www*.pythön.org'.encode("idna").decode("ascii")
- cert = {'subject': ((('commonName', idna),),)}
- fail(cert, 'www.pythön.org'.encode("idna").decode("ascii"))
- fail(cert, 'www1.pythön.org'.encode("idna").decode("ascii"))
- fail(cert, 'ftp.pythön.org'.encode("idna").decode("ascii"))
- fail(cert, 'pythön.org'.encode("idna").decode("ascii"))
-
- # Slightly fake real-world example
- cert = {'notAfter': 'Jun 26 21:41:46 2011 GMT',
- 'subject': ((('commonName', 'linuxfrz.org'),),),
- 'subjectAltName': (('DNS', 'linuxfr.org'),
- ('DNS', 'linuxfr.com'),
- ('othername', '<unsupported>'))}
- ok(cert, 'linuxfr.org')
- ok(cert, 'linuxfr.com')
- # Not a "DNS" entry
- fail(cert, '<unsupported>')
- # When there is a subjectAltName, commonName isn't used
- fail(cert, 'linuxfrz.org')
-
- # A pristine real-world example
- cert = {'notAfter': 'Dec 18 23:59:59 2011 GMT',
- 'subject': ((('countryName', 'US'),),
- (('stateOrProvinceName', 'California'),),
- (('localityName', 'Mountain View'),),
- (('organizationName', 'Google Inc'),),
- (('commonName', 'mail.google.com'),))}
- ok(cert, 'mail.google.com')
- fail(cert, 'gmail.com')
- # Only commonName is considered
- fail(cert, 'California')
-
- # -- IPv4 matching --
- cert = {'subject': ((('commonName', 'example.com'),),),
- 'subjectAltName': (('DNS', 'example.com'),
- ('IP Address', '10.11.12.13'),
- ('IP Address', '14.15.16.17'),
- ('IP Address', '127.0.0.1'))}
- ok(cert, '10.11.12.13')
- ok(cert, '14.15.16.17')
- # socket.inet_ntoa(socket.inet_aton('127.1')) == '127.0.0.1'
- fail(cert, '127.1')
- fail(cert, '14.15.16.17 ')
- fail(cert, '14.15.16.17 extra data')
- fail(cert, '14.15.16.18')
- fail(cert, 'example.net')
-
- # -- IPv6 matching --
- if socket_helper.IPV6_ENABLED:
- cert = {'subject': ((('commonName', 'example.com'),),),
- 'subjectAltName': (
- ('DNS', 'example.com'),
- ('IP Address', '2001:0:0:0:0:0:0:CAFE\n'),
- ('IP Address', '2003:0:0:0:0:0:0:BABA\n'))}
- ok(cert, '2001::cafe')
- ok(cert, '2003::baba')
- fail(cert, '2003::baba ')
- fail(cert, '2003::baba extra data')
- fail(cert, '2003::bebe')
- fail(cert, 'example.net')
-
- # -- Miscellaneous --
-
- # Neither commonName nor subjectAltName
- cert = {'notAfter': 'Dec 18 23:59:59 2011 GMT',
- 'subject': ((('countryName', 'US'),),
- (('stateOrProvinceName', 'California'),),
- (('localityName', 'Mountain View'),),
- (('organizationName', 'Google Inc'),))}
- fail(cert, 'mail.google.com')
-
- # No DNS entry in subjectAltName but a commonName
- cert = {'notAfter': 'Dec 18 23:59:59 2099 GMT',
- 'subject': ((('countryName', 'US'),),
- (('stateOrProvinceName', 'California'),),
- (('localityName', 'Mountain View'),),
- (('commonName', 'mail.google.com'),)),
- 'subjectAltName': (('othername', 'blabla'), )}
- ok(cert, 'mail.google.com')
-
- # No DNS entry subjectAltName and no commonName
- cert = {'notAfter': 'Dec 18 23:59:59 2099 GMT',
- 'subject': ((('countryName', 'US'),),
- (('stateOrProvinceName', 'California'),),
- (('localityName', 'Mountain View'),),
- (('organizationName', 'Google Inc'),)),
- 'subjectAltName': (('othername', 'blabla'),)}
- fail(cert, 'google.com')
-
- # Empty cert / no cert
- self.assertRaises(ValueError, ssl.match_hostname, None, 'example.com')
- self.assertRaises(ValueError, ssl.match_hostname, {}, 'example.com')
-
- # Issue #17980: avoid denials of service by refusing more than one
- # wildcard per fragment.
- cert = {'subject': ((('commonName', 'a*b.example.com'),),)}
- with self.assertRaisesRegex(
- ssl.CertificateError,
- "partial wildcards in leftmost label are not supported"):
- ssl.match_hostname(cert, 'axxb.example.com')
-
- cert = {'subject': ((('commonName', 'www.*.example.com'),),)}
- with self.assertRaisesRegex(
- ssl.CertificateError,
- "wildcard can only be present in the leftmost label"):
- ssl.match_hostname(cert, 'www.sub.example.com')
-
- cert = {'subject': ((('commonName', 'a*b*.example.com'),),)}
- with self.assertRaisesRegex(
- ssl.CertificateError,
- "too many wildcards"):
- ssl.match_hostname(cert, 'axxbxxc.example.com')
-
- cert = {'subject': ((('commonName', '*'),),)}
- with self.assertRaisesRegex(
- ssl.CertificateError,
- "sole wildcard without additional labels are not support"):
- ssl.match_hostname(cert, 'host')
-
- cert = {'subject': ((('commonName', '*.com'),),)}
- with self.assertRaisesRegex(
- ssl.CertificateError,
- r"hostname 'com' doesn't match '\*.com'"):
- ssl.match_hostname(cert, 'com')
-
- # extra checks for _inet_paton()
- for invalid in ['1', '', '1.2.3', '256.0.0.1', '127.0.0.1/24']:
- with self.assertRaises(ValueError):
- ssl._inet_paton(invalid)
- for ipaddr in ['127.0.0.1', '192.168.0.1']:
- self.assertTrue(ssl._inet_paton(ipaddr))
- if socket_helper.IPV6_ENABLED:
- for ipaddr in ['::1', '2001:db8:85a3::8a2e:370:7334']:
- self.assertTrue(ssl._inet_paton(ipaddr))
-
def test_server_side(self):
# server_hostname doesn't work for server sockets
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)