Question: Trying to send email with a certificate file using the following script:

```
import smtplib

client = smtplib.SMTP(myhost, myport)
client.ehlo()
client.starttls(certfile=mycertfile)
client.ehlo()

client.login(myusername, mypassword)
client.sendmail(sender, receiver, Message)
client.quit()

```

I get the following error:

```
SSLError: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib

```

I think documentations ([smtplib.html](https://docs.python.org/2/library/smtplib.html#smtplib.SMTP.starttls) and [ssl.html](https://docs.python.org/2/library/ssl.html)) say I need to provide a private key. I only have the certificate file (base64 PEM format). My devops says that a private key is not required in this case because I do not need to identify the local side of the connection.

Question
--------

Is there a way to send the email without providing the private key? If a private key is required, why?

Answer: 
There are two ways to use SSL/TLS: client authenticated and "basic" where the client is unauthenticated. In client authenticated connections, the both the server and the client send a certificate to the other. In "basic" only the server does.

If you pass neither a certificate nor a keyfile, `smtplib` will use a basic connection, where the client is authenticated.

If you use a certificate, it will be used for a client authenticated connection. In that case, the server will demand that the client shows it owns the certificate by signing a handshake message. For the client to be able to do that it also needs the private key, which can be either in the certificate file or as a separate keyfile.

Long story short, if you want to use a client certificate, you must also use a key. If not, you can just leave both empty.

---

OTOH, maybe you have a server certificate file or CA list you want to use with the connection?

In that case you need to pass it to `ssl.wrap_socket` in the `ca_certs` parameter. Since you use Python 2.6 there's no easy way to do that with `smtplib` (Python 3.3+ has a `context` argument to [starttls](https://docs.python.org/3.4/library/smtplib.html#smtplib.SMTP.starttls)).

How to solve this depends on your application. For example, if you do not need `ssl` for anything else, a hackish solution would be to monkey-patch `ssl.wrap_socket` with one that provides your `ca_cert` (as well as `cert_reqs=CERT_REQUIRED`, likely).

A more full blown solution would be to extend `smtplib.SMTP` with your own variant that does allow passing in those parameters.
Here's a monkey-patch taken from this [page](https://github.com/cybercase/django-smtp-starttls/blob/master/django_smtp_starttls.py):

```
class SMTPExt(smtplib.SMTP):
    """
    This class extends smtplib.SMTP and overrides the starttls method
    allowing extra parameters and forwarding them to ssl.wrap_socket.
    """

    def starttls(self, keyfile=None, certfile=None, **kwargs):
        self.ehlo_or_helo_if_needed()
        if not self.has_extn("starttls"):
            raise SMTPException("STARTTLS extension not supported by server.")
        (resp, reply) = self.docmd("STARTTLS")
        if resp == 220:
            self.sock = ssl.wrap_socket(self.sock, keyfile, certfile, **kwargs)
            self.file = SSLFakeFile(self.sock)
            # 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)

```

Using the root cert from [requests](https://github.com/kennethreitz/requests/blob/master/requests/cacert.pem)