尝试使用以下脚本发送带有证书文件的电子邮件:
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.html和ssl.html)说我需要提供一个私钥。我只有证书文件(base64 PEM格式)。我的开发人员说在这种情况下不需要私钥,因为我不需要识别连接的本地端。
有没有办法在不提供私钥的情况下发送电子邮件?如果需要私钥,为什么?
答案 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
中的根证书