cryptography.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. """
  2. `cryptography.x509 <https://github.com/pyca/cryptography>`_-specific code.
  3. """
  4. from __future__ import absolute_import, division, print_function
  5. import warnings
  6. from cryptography.x509 import (
  7. DNSName, ExtensionOID, NameOID, OtherName, UniformResourceIdentifier,
  8. ObjectIdentifier)
  9. from cryptography.x509.extensions import ExtensionNotFound
  10. from pyasn1.codec.der.decoder import decode
  11. from pyasn1.type.char import IA5String
  12. from .exceptions import SubjectAltNameWarning
  13. from ._common import (
  14. CertificateError,
  15. DNSPattern,
  16. DNS_ID,
  17. SRVPattern,
  18. URIPattern,
  19. verify_service_identity,
  20. )
  21. __all__ = [
  22. "verify_certificate_hostname",
  23. ]
  24. def verify_certificate_hostname(certificate, hostname):
  25. """
  26. Verify whether *certificate* is valid for *hostname*.
  27. .. note:: Nothing is verified about the *authority* of the certificate;
  28. the caller must verify that the certificate chains to an appropriate
  29. trust root themselves.
  30. :param cryptography.x509.Certificate certificate: A cryptography X509
  31. certificate object.
  32. :param unicode hostname: The hostname that *certificate* should be valid
  33. for.
  34. :raises service_identity.VerificationError: If *certificate* is not valid
  35. for *hostname*.
  36. :raises service_identity.CertificateError: If *certificate* contains
  37. invalid/unexpected data.
  38. :returns: ``None``
  39. """
  40. verify_service_identity(
  41. cert_patterns=extract_ids(certificate),
  42. obligatory_ids=[DNS_ID(hostname)],
  43. optional_ids=[],
  44. )
  45. ID_ON_DNS_SRV = ObjectIdentifier('1.3.6.1.5.5.7.8.7') # id_on_dnsSRV
  46. def extract_ids(cert):
  47. """
  48. Extract all valid IDs from a certificate for service verification.
  49. If *cert* doesn't contain any identifiers, the ``CN``s are used as DNS-IDs
  50. as fallback.
  51. :param cryptography.x509.Certificate cert: The certificate to be dissected.
  52. :return: List of IDs.
  53. """
  54. ids = []
  55. try:
  56. ext = cert.extensions.get_extension_for_oid(
  57. ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
  58. except ExtensionNotFound:
  59. pass
  60. else:
  61. ids.extend([DNSPattern(name.encode('utf-8'))
  62. for name
  63. in ext.value.get_values_for_type(DNSName)])
  64. ids.extend([URIPattern(uri.encode('utf-8'))
  65. for uri
  66. in ext.value.get_values_for_type(
  67. UniformResourceIdentifier)])
  68. for other in ext.value.get_values_for_type(OtherName):
  69. if other.type_id == ID_ON_DNS_SRV:
  70. srv, _ = decode(other.value)
  71. if isinstance(srv, IA5String):
  72. ids.append(SRVPattern(srv.asOctets()))
  73. else: # pragma: nocover
  74. raise CertificateError(
  75. "Unexpected certificate content."
  76. )
  77. if not ids:
  78. # http://tools.ietf.org/search/rfc6125#section-6.4.4
  79. # A client MUST NOT seek a match for a reference identifier of CN-ID if
  80. # the presented identifiers include a DNS-ID, SRV-ID, URI-ID, or any
  81. # application-specific identifier types supported by the client.
  82. cns = [n.value
  83. for n
  84. in cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)]
  85. cn = next(iter(cns), b'<not given>')
  86. ids = [DNSPattern(n.encode('utf-8')) for n in cns]
  87. warnings.warn(
  88. "Certificate with CN {!r} has no `subjectAltName`, falling back "
  89. "to check for a `commonName` for now. This feature is being "
  90. "removed by major browsers and deprecated by RFC 2818.".format(cn),
  91. SubjectAltNameWarning
  92. )
  93. return ids