如何通过smtplib(TLS)接受来自电子邮件服务器的自签名证书?

问题描述 投票:5回答:1

我的剧本

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)

我想知道如何在python选项的smtplib中指定“当通过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()
python python-2.7 ssl smtplib starttls
1个回答
1
投票

看来你的方法是继承SMTP类并重写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()

应该工作而不是未经验证的上下文。

当您自行签署证书时,您使用了自己的证书颁发机构,因此可能之前已创建了根CA证书。

必须为SMTP客户端知道此根证书才能验证SMTP服务器证书。 因此,找到该文件,将其放在SMTP客户端可访问的位置,并将此路径设置为PATH_TO_CERTIFICATE_AUTHORITY_ROOT_CRT_FILE的值。

你做的是:

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

certfilekeyfile是客户端证书和密钥 - 某些服务器不接受未经验证的客户端的连接。

有用的链接: ssl.create_default_context() documentation In case you can't find your root certificate you can start from scratch(只是忽略图片上的所有MacOS窗口 - 您的SMTP客户端想要访问根证书文件 - 他们在本指南中添加到浏览器中的文件)


0
投票

迟到的答案,但我通过使用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())
© www.soinside.com 2019 - 2024. All rights reserved.