Sending email without keyfile (only certfile) using Python smtplib
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.

