Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pyopenssl.py 4.9KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. """
  2. `pyOpenSSL <https://github.com/pyca/pyopenssl>`_-specific code.
  3. """
  4. from __future__ import annotations
  5. import contextlib
  6. import warnings
  7. from typing import Sequence
  8. from pyasn1.codec.der.decoder import decode
  9. from pyasn1.type.char import IA5String
  10. from pyasn1.type.univ import ObjectIdentifier
  11. from pyasn1_modules.rfc2459 import GeneralNames
  12. from .exceptions import CertificateError
  13. from .hazmat import (
  14. DNS_ID,
  15. CertificatePattern,
  16. DNSPattern,
  17. IPAddress_ID,
  18. IPAddressPattern,
  19. SRVPattern,
  20. URIPattern,
  21. verify_service_identity,
  22. )
  23. with contextlib.suppress(ImportError):
  24. # We only use it for docstrings -- `if TYPE_CHECKING`` does not work.
  25. from OpenSSL.crypto import X509
  26. from OpenSSL.SSL import Connection
  27. __all__ = ["verify_hostname"]
  28. def verify_hostname(connection: Connection, hostname: str) -> None:
  29. """
  30. Verify whether the certificate of *connection* is valid for *hostname*.
  31. :param connection: A pyOpenSSL connection object.
  32. :param hostname: The hostname that *connection* should be connected to.
  33. :raises service_identity.VerificationError: If *connection* does not
  34. provide a certificate that is valid for *hostname*.
  35. :raises service_identity.CertificateError: If the certificate chain of
  36. *connection* contains a certificate that contains invalid/unexpected
  37. data.
  38. :returns: ``None``
  39. """
  40. verify_service_identity(
  41. cert_patterns=extract_patterns(
  42. connection.get_peer_certificate() # type:ignore[arg-type]
  43. ),
  44. obligatory_ids=[DNS_ID(hostname)],
  45. optional_ids=[],
  46. )
  47. def verify_ip_address(connection: Connection, ip_address: str) -> None:
  48. """
  49. Verify whether the certificate of *connection* is valid for *ip_address*.
  50. :param connection: A pyOpenSSL connection object.
  51. :param ip_address: The IP address that *connection* should be connected to.
  52. Can be an IPv4 or IPv6 address.
  53. :raises service_identity.VerificationError: If *connection* does not
  54. provide a certificate that is valid for *ip_address*.
  55. :raises service_identity.CertificateError: If the certificate chain of
  56. *connection* contains a certificate that contains invalid/unexpected
  57. data.
  58. :returns: ``None``
  59. .. versionadded:: 18.1.0
  60. """
  61. verify_service_identity(
  62. cert_patterns=extract_patterns(
  63. connection.get_peer_certificate() # type:ignore[arg-type]
  64. ),
  65. obligatory_ids=[IPAddress_ID(ip_address)],
  66. optional_ids=[],
  67. )
  68. ID_ON_DNS_SRV = ObjectIdentifier("1.3.6.1.5.5.7.8.7") # id_on_dnsSRV
  69. def extract_patterns(cert: X509) -> Sequence[CertificatePattern]:
  70. """
  71. Extract all valid ID patterns from a certificate for service verification.
  72. :param cert: The certificate to be dissected.
  73. :return: List of IDs.
  74. .. versionchanged:: 23.1.0
  75. ``commonName`` is not used as a fallback anymore.
  76. """
  77. ids: list[CertificatePattern] = []
  78. for i in range(cert.get_extension_count()):
  79. ext = cert.get_extension(i)
  80. if ext.get_short_name() == b"subjectAltName":
  81. names, _ = decode(ext.get_data(), asn1Spec=GeneralNames())
  82. for n in names:
  83. name_string = n.getName()
  84. if name_string == "dNSName":
  85. ids.append(
  86. DNSPattern.from_bytes(n.getComponent().asOctets())
  87. )
  88. elif name_string == "iPAddress":
  89. ids.append(
  90. IPAddressPattern.from_bytes(
  91. n.getComponent().asOctets()
  92. )
  93. )
  94. elif name_string == "uniformResourceIdentifier":
  95. ids.append(
  96. URIPattern.from_bytes(n.getComponent().asOctets())
  97. )
  98. elif name_string == "otherName":
  99. comp = n.getComponent()
  100. oid = comp.getComponentByPosition(0)
  101. if oid == ID_ON_DNS_SRV:
  102. srv, _ = decode(comp.getComponentByPosition(1))
  103. if isinstance(srv, IA5String):
  104. ids.append(SRVPattern.from_bytes(srv.asOctets()))
  105. else: # pragma: no cover
  106. raise CertificateError(
  107. "Unexpected certificate content."
  108. )
  109. else: # pragma: no cover
  110. pass
  111. else: # pragma: no cover
  112. pass
  113. return ids
  114. def extract_ids(cert: X509) -> Sequence[CertificatePattern]:
  115. """
  116. Deprecated and never public API. Use :func:`extract_patterns` instead.
  117. .. deprecated:: 23.1.0
  118. """
  119. warnings.warn(
  120. category=DeprecationWarning,
  121. message="`extract_ids()` is deprecated, please use `extract_patterns()`.",
  122. stacklevel=2,
  123. )
  124. return extract_patterns(cert)