简体   繁体   English

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

[英]How to accept self-signed certificate from e-mail server via smtplib (TSL)?

My script我的剧本

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

falls with error: python2.7/ssl.py", line 847, in do_handshake self._sslobj.do_handshake()出现错误: python2.7/ssl.py", line 847, in do_handshake self._sslobj.do_handshake()

When I run command openssl to this server it falls with error 21: Verify return code: 21 (unable to verify the first certificate) .当我向该服务器运行命令openssl时,它会出现错误 21: Verify return code: 21 (unable to verify the first certificate)

I would like to know how to specify in smtplib of python option “always accept self-signed certificate when connect is established via tls to e-mail server"? Like I do in requests.get setting key verify=False .我想知道如何在 python 选项的 smtplib 中指定“通过 tls 建立到电子邮件服务器的连接时始终接受自签名证书”?就像我在requests.get中设置密钥verify=False一样。

Update This variant with custom smtp class and context = ssl._create_unverified_context() return the same error as above:使用自定义 smtp class 和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()

Late answer, but I fixed the ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:852) on python 3.7 using ssl._create_unverified_context() , ie:迟到的答案,但我修复了ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:852) on 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())

It really seems that your approach with subclassing SMTP class and overriding starttls() method to accept context argument is the way to go - it's actually even introduced in Python 3.x.看起来你的子类化SMTP类和覆盖starttls()方法来接受context参数的方法是要走的路 - 它实际上甚至在 Python 3.x 中引入。

However:然而:

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()

should work instead of unverified context.应该工作而不是未经验证的上下文。

When you self signed your certificate you used your own certificate authority, so probably you created root CA certificate before.当您自签名证书时,您使用了自己的证书颁发机构,因此您之前可能创建了根 CA 证书。

This root certificate must be known to your SMTP client in order to verify your SMTP server certificate.您的 SMTP 客户端必须知道此根证书才能验证您的 SMTP 服务器证书。
So find that file, put it in location accessible to your SMTP client and set this path as a value of PATH_TO_CERTIFICATE_AUTHORITY_ROOT_CRT_FILE .因此,找到该文件,将其放在您的 SMTP 客户端可以访问的位置,并将此路径设置为PATH_TO_CERTIFICATE_AUTHORITY_ROOT_CRT_FILE的值。

What you did instead was:你做的是:

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

but certfile and keyfile are client certificate and key - some servers won't accept connections from unverified client.但是certfilekeyfile是客户端证书和密钥 - 有些服务器不会接受来自未经验证的客户端的连接。

Useful links:有用的链接:
ssl.create_default_context() documentation ssl.create_default_context() 文档
In case you can't find your root certificate you can start from scratch (just ignore all MacOS windows on pictures - your SMTP client wants access to root certificate file - the one they add to the browser in this guide) 如果您找不到根证书,您可以从头开始(只需忽略图片上的所有 MacOS 窗口 - 您的 SMTP 客户端想要访问根证书文件 - 他们在本指南中添加到浏览器的文件)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM