使用Python smtplib发送没有密钥文件(仅限certfile)的电子邮件

时间:2014-06-16 18:36:00

标签: python email smtp python-2.6

尝试使用以下脚本发送带有证书文件的电子邮件:

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

我收到以下错误:

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

我认为文档(smtplib.htmlssl.html)说我需要提供一个私钥。我只有证书文件(base64 PEM格式)。我的开发人员说在这种情况下不需要私钥,因为我不需要识别连接的本地端。

问题

有没有办法在不提供私钥的情况下发送电子邮件?如果需要私钥,为什么?

2 个答案:

答案 0 :(得分:2)

使用SSL / TLS有两种方法:客户端验证和"基本"客户端未经身份验证的位置。在客户端身份验证的连接中,服务器和客户端都将证书发送给另一个。在"基本"只有服务器才这样做。

如果既没有传递证书也没有传递密钥文件,smtplib将使用基本连接,客户端通过身份验证。

如果您使用证书,它将用于客户端验证的连接。在这种情况下,服务器将通过签署握手消息要求客户端显示它拥有证书。为了使客户端能够这样做,它还需要私钥,私钥可以在证书文件中,也可以作为单独的密钥文件。

简而言之,如果您想使用客户端证书,您还必须使用密钥。如果没有,你可以把它们都留空。


OTOH,您是否有要与连接一起使用的服务器证书文件或CA列表?

在这种情况下,您需要将其传递给ssl.wrap_socket参数中的ca_certs。由于使用Python 2.6,smtplib没有简单的方法(Python 3.3+与starttls有一个context参数)。

如何解决这个问题取决于您的应用程序。例如,如果您不需要ssl其他任何内容,那么一个hackish解决方案就是使用ssl.wrap_socket(以及ca_cert提供cert_reqs=CERT_REQUIRED。 ,很可能)。

更全面的解决方案是使用您自己的变体扩展smtplib.SMTP,允许传递这些参数。

答案 1 :(得分:1)

这是从page获取的猴子补丁:

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)

使用requests

中的根证书