【问题标题】:MailKit gets an SslHandshakeException with LetsEncrypt SSL certificatesMailKit 使用 LetsEncrypt SSL 证书获得 SslHandshakeException
【发布时间】:2020-09-05 16:39:57
【问题描述】:

我有一个服务器(Centos 7)设置用作邮件服务器。使用 postfix/dovecot/opendkim/opendmarc.. 它可以正常工作,例如,用户可以使用 gmail 连接他们的电子邮件。能够收发邮件。

此外,当我使用 MailKit 并从我的家用电脑测试我的 .NET Core 应用程序时,MailKit 连接正常并且电子邮件已发送。

但是,当我将应用程序部署到我的服务器时,MailKit 无法连接。

如果我查看日志,我会看到以下内容

postfix/submission/smtpd[4486]: match_hostname: unknown ~? 127.0.0.1/32
postfix/submission/smtpd[4486]: match_hostaddr: MY_SERVER_IP ~? 127.0.0.1/32
postfix/submission/smtpd[4486]: match_hostname: unknown ~? MY_SERVER_IP/32
postfix/submission/smtpd[4486]: match_hostaddr: MY_SERVER_IP  ~? MY_SERVER_IP/32
postfix/submission/smtpd[4486]: lost connection after STARTTLS from unknown[MY_SERVER_IP]

但如果我在日志中看起来更高一点,我会看到

Anonymous TLS connection established from unknown[MY_SERVER_IP]: TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)

我的 MailKit(可以在服务器外部正常工作):

using (SmtpClient emailClient = new SmtpClient())
{
    await emailClient.ConnectAsync(emailConfiguration.SmtpServer, emailConfiguration.SmtpPort, SecureSocketOptions.StartTls);
    emailClient.AuthenticationMechanisms.Remove("XOAUTH2");  

    await emailClient.AuthenticateAsync(emailConfiguration.SmtpUsername, emailConfiguration.SmtpPassword);
    await emailClient.SendAsync(message);

    await emailClient.DisconnectAsync(true);
}

编辑: MailKit 的例外(证书是正确的,不是自签名的):

MailKit.Security.SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.
May 19 16:07:37 domain.com NETCoreApp[4452]: The server's SSL certificate could not be validated for the following reasons:
May 19 16:07:37 domain.com NETCoreApp[4452]: • The server certificate has the following errors:
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get certificate CRL
May 19 16:07:37 domain.com NETCoreApp[4452]: • The root certificate has the following errors:
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get certificate CRL
May 19 16:07:37 domain.com NETCoreApp[4452]: • unable to get local issuer certificate
May 19 16:07:37 domain.com NETCoreApp[4452]: ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

【问题讨论】:

  • FWIW, emailClient.AuthenticationMechanisms.Remove("XOAUTH2"); 自 MailKit 2.0 以来不再需要
  • 谢谢,我不知道!

标签: centos postfix-mta mailkit dovecot


【解决方案1】:

unable to get certificate CRL 错误听起来像是 SslStream 无法获取 CRL,可能是因为 CRL 服务器由于某种原因无法访问。

您可以尝试在 ConnectAsync 之前添加 emailClient.CheckCertificateRevocation = false; 以检查是否是问题所在。

另一个错误unable to get local issuer certificate 可能是因为运行 MailKit 的服务器在其 X509Store 中没有根 CA 证书,但您的家用 PC 有。

更新:

问题在于 LetsEncrypt SSL 证书不包含 CRL 位置,这意味着证书吊销检查将失败。

要绕过这个,你需要在连接之前设置client.CheckCertificateRevocation = false;

【讨论】:

  • 感谢您的回复!我服务器上的证书是用 Letsencrypt 的 certbot 安装的,它应该很好,因为我没有浏览器(和 gmail)抱怨。可能值得尝试将其关闭。但是,我认为,因为 MailKit 试图从内部连接到同一台服务器..防火墙/权限出了点问题。我还没有尝试过你的答案,但找到了解决方案。看我的回答。
  • 好的,所以问题是 LetsEncrypt SSL 证书不包含导致 SslStream 无法验证它们的 CRL 服务器位置。
  • 您需要指定client.CheckCertificateRevocation = false;
  • 嗯,我明白了。不知道这个。关闭该选项是有道理的。谢谢!
【解决方案2】:

我找到了一个可行的答案,但不是我的首选方法,因为我希望能够将 MailKit 用于我自己的服务器之外的更多功能(使其可从应用程序本身进行配置)

我之所以找到解决方案,是因为我认为这与一些内部流量出错有关..

通过使用来自 System.Net.Mail 的旧 SmtpClient,我能够使用 DefaultCredentials

using (SmtpClient client = new SmtpClient("127.0.0.1"))
{
    client.UseDefaultCredentials = true;

    MailAddress from = new MailAddress(emailMessage.FromAddress.Address, emailMessage.FromAddress.Name);

    foreach (IEmailAddress emailAddress in emailMessage.ToAddresses)
    {
        MailAddress to = new MailAddress(emailAddress.Address, emailAddress.Name);

        MailMessage email = new MailMessage(from, to)
        {
            Subject = emailMessage.Subject,
            Body = emailMessage.Content
        };

        await client.SendMailAsync(email);
    }
}

【讨论】:

    【解决方案3】:

    我在带有 .NET core 3.1 的 ubuntu 20.04 上遇到了同样的问题

    经过 3 小时的反复试验,我终于找到了解决方案
    我刚刚忽略了Certificate Validation CallBack

    using var client = new SmtpClient(new ProtocolLogger("smtp.log"));
    client.CheckCertificateRevocation = false;
    client.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
    client.Connect("your.smtp.host", 587, SecureSocketOptions.StartTls);
    

    我希望这会有所帮助:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多