Как принять самозаверяющий сертификат с почтового сервера через smtplib (TSL)? - PullRequest
0 голосов
/ 04 марта 2019

Мой скрипт

from stmplib import SMTP
con = SMTP(server, port)
con.starttls()
con.login(user, pass)
con.quit()

падает с ошибкой: python2.7/ssl.py", line 847, in do_handshake self._sslobj.do_handshake()

Когда я запускаю команду openssl на этом сервере, он падает с ошибкой 21: Verify return code: 21 (unable to verify the first certificate).

Я хотел бы знать, как указать в smtplib опции python «всегда принимать самозаверяющий сертификат, когда установлено соединение через tls с почтовым сервером»? Как я это делаю в requests.get ключ установки verify=False.

Обновление Этот вариант с пользовательским классом smtp и context = ssl._create_unverified_context() возвращает ту же ошибку, что и выше:

import smtplib
import ssl

class MySMTP(smtplib.SMTP):
    def __init__(self, host='', port=0, timeout=5):
        smtplib.SMTP.__init__(self, host, port, timeout=timeout)
        self._host = host

    def starttls(self, keyfile=None, certfile=None, context=None):
        from urllib import _have_ssl

        self.ehlo_or_helo_if_needed()
        if not self.has_extn("starttls"):
            raise SMTPNotSupportedError("STARTTLS extension not supported by server.")
        (resp, reply) = self.docmd("STARTTLS")
        if resp == 220:
            if not _have_ssl:
                raise RuntimeError("No SSL support included in this Python")
            if context is not None and keyfile is not None:
                raise ValueError("context and keyfile arguments are mutually "
                             "exclusive")
            if context is not None and certfile is not None:
                raise ValueError("context and certfile arguments are mutually "
                             "exclusive")
            if context is None:
                context = ssl._create_stdlib_context(certfile=certfile,
                                                 keyfile=keyfile)
            self.sock = context.wrap_socket(self.sock,
                                        server_hostname=self._host)
            self.file = None
            # RFC 3207:
            # The client MUST discard any knowledge obtained from
            # the server, such as the list of SMTP service extensions,
            # which was not obtained from the TLS negotiation itself.
            self.helo_resp = None
            self.ehlo_resp = None
            self.esmtp_features = {}
            self.does_esmtp = 0
        return (resp, reply)

con= MySMTP(server, port)
context  = ssl._create_unverified_context()
con.starttls(context = context)
con.login(user, pass)
con.quit()

Ответы [ 2 ]

0 голосов
/ 03 августа 2019

Поздний ответ, но я исправил ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:852) на python 3.7 с помощью ssl._create_unverified_context(), то есть:

import smtplib, ssl
context = ssl._create_unverified_context()
with smtplib.SMTP_SSL("domain.tld", 465, context=context) as server:
    server.login(user, password)
    server.sendmail(sender_email, receiver_email, message.as_string())
0 голосов
/ 12 марта 2019

Действительно кажется, что ваш подход с использованием подкласса SMTP class и переопределением starttls() метода для принятия context аргумента - это путь, который фактически даже представлен в Python 3.x.

Однако:

con = MySMTP(server, port)
context  = ssl.create_default_context(cafile=PATH_TO_CERTIFICATE_AUTHORITY_ROOT_CRT_FILE)
con.starttls(context=context)
con.login(user, pass)
con.quit()

должен работать вместо непроверенного контекста.

Когда вы самостоятельно подписывали свой сертификат, вы использовали свой собственный центр сертификации, поэтому, вероятно, вы создали сертификат корневого ЦС раньше.

Этот корневой сертификат должен быть известен вашему SMTP-клиенту для проверки сертификата вашего SMTP-сервера.
Найдите этот файл, поместите его в местоположение, доступное для вашего SMTP-клиента, и задайте для этого пути значение PATH_TO_CERTIFICATE_AUTHORITY_ROOT_CRT_FILE.

Вместо этого вы сделали следующее:

ssl._create_stdlib_context(certfile=certfile, keyfile=keyfile)

, но certfile и keyfile являются сертификатом и ключом клиента - некоторые серверы не принимают соединения от непроверенного клиента.

Полезные ссылки:
ssl.create_default_context () документация
Если вы не можете найти свой корневой сертификат, вы можете начать с нуля (просто игнорируйте все окна MacOS на изображениях - ваш SMTP-клиент хочет получить доступ к файлу корневого сертификата - который они добавляют в браузер в этом руководстве)

...