Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

When I run command openssl to this server it falls with error 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.

Update This variant with custom smtp class and context = ssl._create_unverified_context() return the same error as above:

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()
like image 405
Serenity Avatar asked Mar 04 '19 03:03

Serenity


People also ask

Can you use a self-signed certificate for TLS?

If you want to secure your website with an SSL/TLS certificate, you can use a free self-signed SSL/TLS certificate.

How do I set up SSL TLS email?

So, how to configure an SMTP with SSL/TLS? It's very simple: you just need to open your mail client's configuration panel and flag “Use SSL/TLS” in the “Connection security” field. Remember also to set the right SMTP SSL port for the email transmission – normally, port 465.


2 Answers

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(), i.e.:

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())
like image 184
Pedro Lobito Avatar answered Sep 28 '22 12:09

Pedro Lobito


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.

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.

This root certificate must be known to your SMTP client in order to verify your SMTP server certificate.
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.

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.

Useful links:
ssl.create_default_context() documentation
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)

like image 31
ElmoVanKielmo Avatar answered Sep 28 '22 12:09

ElmoVanKielmo