65.9K
CodeProject 正在变化。 阅读更多。
Home

支持隐式 SSL 安全套接层以及 OAuth2 开放授权或密码授权 (2.0) 的 SMTP 客户端

starIconstarIconstarIconstarIconstarIcon

5.00/5 (21投票s)

2017年3月1日

CPOL

7分钟阅读

viewsIcon

60611

downloadIcon

2918

附带的开源 C# .NET 库是一个实现了隐式 SSL 和 OAuth2 协议的 SMTP 客户端。该库回答了“如何使用 Gmail 作为服务器发送电子邮件?”或“如何发送使用实现了隐式 SSL 和端口 465 的电子邮件服务器的电子邮件?”的问题。

Image of TestUziSecureSmtpClient

引言

附带的 .NET 库 UziSecureSmtpClient 允许您从 .NET 应用程序发送安全电子邮件。此类库是用 C# 为 .NET Framework 编写的。该库是为 MM Interplast 开发的。MM Interplast 是一家总部位于缅甸的制造商,为食品、饮料和消费品行业提供定制的 PET 瓶胚、HDPE 瓶盖和容器,使用最先进的设备生产。开发该库是因为当前的 Microsoft Visual Studio SmtpClient 类不支持隐式 SSL 连接安全性。值得注意的是,使用隐式 SSL 与邮件服务器通信意味着所有内容(包括纯文本密码)都会在您的计算机和邮件服务器提供商之间进行加密。该库已通过使用以下电子邮件服务器提供商发送电子邮件进行了测试:Gmail、Yahoo、ATT、Rogers、HostGator 和 Network Solutions。

2.0 版本支持包含非 ASCII 字符的主题行。

该类库支持三种类型的出站电子邮件服务器。

  • Gmail:连接安全为 SSL/TLS。身份验证方法为 OAuth2。对于 GMAIL,出站主机服务器为 smtp.gmail.com,出站端口为 465。
  • Yahoo、ATT 和 Rogers 等服务器:连接安全为 SSL/TLS。身份验证方法为纯文本密码。但是,密码会像电子邮件的所有其他部分一样被 SSL 加密。出站端口号通常为 465。
  • 无连接安全和纯文本密码的服务器。出站端口号为 587。

OAuth2 身份验证方法仅通过 Gmail 进行了测试。要使用 OAuth2,需要创建一个刷新令牌文件。下面的步骤 2 描述了 Gmail 的创建过程。我不知道其他 OAuth2 身份验证出站电子邮件提供商是否使用相同的流程。我怀疑 GmailRefreshToken 程序需要修改或重写。

电子邮件消息格式在 Wikipedia MIME 条目中进行了描述。MIME 在六个链接的 RFC 备忘录中进行了规定: RFC 2045 RFC 2046 RFC 2047 RFC 4288 RFC 4289 RFC 2049;与 SMTP 电子邮件的集成在 RFC 1521 RFC 1522 中详细说明。

步骤 1 - 电子邮件帐户

为您的出站邮件创建一个电子邮件帐户。将其添加到您的电子邮件程序中,并确保您可以发送和接收电子邮件。记下出站服务器设置

  • 主机或服务器名称
  • 端口
  • 用户名
  • 用户密码
  • 身份验证方法
  • 连接安全

步骤 2 - 演示文件夹

创建一个新文件夹来测试软件。将演示 zip 文件中的五个文件解压到此文件夹中。

  • GMailRefreshToken.exe
  • UziSecureSmtpClient.dll
  • TestSecureSmtpClient.exe
  • EmailImage.png
  • Rfc2045.pdf

步骤 3 - 创建身份验证刷新令牌文件(仅限 Gmail)

如果您的电子邮件提供商不是 Gmail,请跳过此步骤。

步骤 3A - 创建客户端 ID 令牌文件

  • 访问链接:https://console.developers.google.com/apis/
  • 如果您未登录 Google,系统会要求您登录。使用您的 Gmail 帐户登录。
  • 在左侧选择 API 管理器选项卡。
  • 在 Google Apps API 下选择 Gmail API。
  • 在左侧 API 管理器下选择凭据。
  • 点击创建凭据组合框。
  • 选择 OAuth 客户端 ID。
  • 在应用程序类型下,选择其他
  • 将名称更改为您的电子邮件应用程序。
  • 点击创建。
  • 您将看到一个 OAuth 客户端框,其中包含客户端 ID 和客户端密钥的随机文本数据。
  • 点击**确定**。
  • 您将看到一个单行表格,其中包含您的 OAuth 2.0 客户端 ID。
  • 在该行的末尾,有一个向下箭头图标。如果您将光标放在该图标上,它会显示下载 JSON。
  • 点击此图标。
  • 浏览器将允许您保存 JSON 文件。将文件保存到第 2 步中的演示文件夹。默认名称非常长。您可以将其更改为较短的名称(例如,SecureSmtpClientIDToken.json)。

步骤 3B - 创建刷新令牌文件

  • 运行 GMailRefreshToken.exe 程序。
  • 点击浏览按钮。
  • 找到您在演示文件夹中保存的 JSON 文件(如上面第 3A 步所示)。点击打开。名称将显示在 Gmail API 客户端 ID 文本框中。
  • 在电子邮件地址框中键入用户电子邮件地址。
  • 按下创建刷新令牌按钮。
  • 屏幕上将显示一个消息框“电子邮件授权”。点击确定
  • 您的默认浏览器将打开,或者会在您打开的浏览器中添加一个新选项卡。
  • 您需要登录您的电子邮件帐户。
  • 登录后,您将看到一条消息,请求允许查看和管理您的电子邮件。
  • 点击允许
  • 浏览器屏幕将变为蓝色,并会要求您关闭选项卡。同时,您将收到一个“保存刷新令牌文件”对话框。
  • SecureSmtpRefreshToken.xml 文件保存到您的演示文件夹。

步骤 4 - 运行演示程序

在开始编程之前,我强烈建议您运行演示程序 TestSecureSmtpClient.exe。您需要确保类库能够与您的电子邮件服务正常工作。演示程序将发送一封包含两种替代视图(纯文本和 HTML)以及两个附件的电子邮件。一个附件是给收件人的文件,另一个是内联图像,用于 HTML 视图。这两个附件都位于您的演示文件夹中。电子邮件内容可以在 TestSecureSmtpClient.cs 源代码中查看。文本摘自与 MIME 标准相关的 RFC 文档之一。

要运行演示程序,请启动 TestSecureSmtpClient.exe。选择电子邮件服务器的类型。填写相关信息。对于 Gmail,浏览到演示文件夹并选择 SecureSmtpRefreshToken.xml 文件。然后按发送电子邮件。如果邮件已成功发送,您将看到“电子邮件已成功发送”。否则,您将看到错误消息。

如果出现问题,请将源代码加载到 Visual Studio 中并在调试模式下运行。进程正在输出视图窗格中进行跟踪。

如果成功,您就可以将类库集成到您的代码中了。

步骤 5 - 从您的 .NET 应用程序发送电子邮件

要使用该库,您需要添加对附带的 UziSecureSmtpClient.dll 类库文件的引用。在每个引用该库的源文件中,都需要添加一个 using UziSecureSmtpClient 语句。UziSecureSmtpClient.dll 库必须随您的分发版一起提供。或者,您可以将库的源代码包含在您的应用程序中,从而避免分发单独的动态链接库(dll)文件的需要。该软件是用 Microsoft Visual Studio Community 2013 开发的。目标 .NET Framework 设置为 4.5。

典型的电子邮件消息包含主题行、发件人地址以及收件人、抄送或密送地址列表和邮件正文。邮件正文由多个部分组成。这些部分组织成一棵树。纯文本内容、HTML 内容和附件是叶节点。如果有多个部分,则部分之间用边界分隔。该库支持三种类型的边界:混合、替代和相关。 Wikipedia MIME 条目描述了这些边界。边界是部分。边界可以有子节点。下面的示例三展示了如何创建所有这些部分并将它们链接在一起以创建消息树。

SecureSmtpClient 类对象是可重用的。这对于发送多个 Gmail 电子邮件的应用程序来说是一个优势。Gmail 刷新令牌文件永远不会过期。当创建 SecureSmtpClient 类对象时,软件会内部创建一个有效期为一小时的授权令牌。如果 SecureSmtpClient 对象长时间重用,软件将每小时请求一个新的授权令牌。所有这些对您的应用程序都是透明的。对您来说的好处是性能略有提升。如果您为每封邮件创建一个新的 SecureSmtpClient,软件将不得不为每封邮件请求一个新的授权令牌。如果您重用 SecureSmtpClient 来发送多封邮件,每小时只有一个授权请求。

下面将提供三个发送电子邮件的示例。

示例一:不安全网络连接和纯文本密码

/////////////////////////////////////////////////////////////////////
// Example One: Unsecure network connection and plain text password.
// Message content is plain text.
// The message is a single part message with no boundaries.
/////////////////////////////////////////////////////////////////////
private void ExampleUnsecurePlainText
        (
        string Host,
        string UserName,
        string UserPassword,
        string FromName,
        string FromAddress,
        string ToName,
        string ToAddress,
        string MessageSubject,
        string MessageText
        )
    {
    try
        {
        // Create SMTP client.
        if(ConnectionUnsecurePlain == null) ConnectionUnsecurePlain = 
           new SecureSmtpClient(ConnectMethod.Secure, Host, UserName, UserPassword);

        // Create mail message object.
        SecureSmtpMessage Message = new SecureSmtpMessage();

        // Set subject.
        Message.Subject = MessageSubject;

        // Set mail from address and display name.
        Message.From = new MailAddress(FromAddress, FromName);

        // Add minimum one or more recipients.
        // in addition you can add CC or BCC recipients.
        Message.To.Add(new MailAddress(ToAddress, ToName));

        // Add mail body contents.
        // The plain text is the only content part.
        // Load it to the root part of the message.
        SecureSmtpContent PlainTextContent = 
                   new SecureSmtpContent(ContentType.Plain, MessageText);
        Message.RootPart = PlainTextContent;

        // Send mail.
        ConnectionUnsecurePlain.SendMail(Message);
        MessageBox.Show("Email was successfully sent");
        }

    // Catch exceptions.
    catch (Exception Exp)
        {
        MessageBox.Show(Exp.Message);
        }
    return;
    }

示例二:安全网络连接和无身份验证

/////////////////////////////////////////////////////////////////////
// Example Two: Secure network connection and no authentication.
// Message content is HTML.
// The HTML has an embedded image. The image is sent as an inline
// attachment. This message is a two parts message. It has two related 
// parts, the HTML part and the inline image attachment part.
// The two parts are separated by SecureSmtpMultipart boundary.
/////////////////////////////////////////////////////////////////////
private void ExampleSecureHtml
        (
        string Host,
        string UserName,
        string UserPassword,
        string FromName,
        string FromAddress,
        string ToName,
        string ToAddress,
        string MessageSubject,
        string MessageHtml
        )
    {
    try
        {
        // Create smtp client.
        if(ConnectionSecureHtml == null) ConnectionSecureHtml = 
         new SecureSmtpClient(ConnectMethod.Secure, Host, UserName, UserPassword);

        // Create mail message object.
        SecureSmtpMessage Message = new SecureSmtpMessage();

        // Set subject.
        Message.Subject = MessageSubject;

        // Set mail from address and display name.
        Message.From = new MailAddress(FromAddress, FromName);

        // Add minimum one or more recipients.
        // in addition you can add CC or BCC recipients.
        Message.To.Add(new MailAddress(ToAddress, ToName));

        // This message has two related parts, the html part and the inline image 
        // attachment part.
        // The two parts are separated by SecureSmtpMultipart boundary.
        // the multipart boundary is the root part.
        SecureSmtpMultipart Related = new SecureSmtpMultipart(MultipartType.Related);
        Message.RootPart = Related;

        // The first part is the HTML content.
        // It is added to array of part content children of the multipart parent.
        SecureSmtpContent HtmlContent = new SecureSmtpContent(ContentType.Html, MessageHtml);
        Related.AddPart(HtmlContent);

        // The second part is the inline image attachment.
        SecureSmtpAttachment ImageAttachment = 
          new SecureSmtpAttachment(AttachmentType.Inline, "EmailImage.png", "IMAGE001");
        ImageAttachment.MediaType = "image/png";
        Related.AddPart(ImageAttachment);

        // Send mail.
        ConnectionSecureHtml.SendMail(Message);
        MessageBox.Show("Email was successfully sent");
        }

    // Catch exceptions.
    catch (Exception Exp)
        {
        MessageBox.Show(Exp.Message);
        }
    return;
    }

示例三 Gmail:安全网络连接和 OAuth2 身份验证

/////////////////////////////////////////////////////////////////////
// Example Three: Secure network connection and OAuth2 authentication.
// This example is specific to GMail.
// The message is a multi-part message. At the root level it is a
// mixed part message with two mixed parts: content and file 
// attachment.
// The content part is divided into two alternate parts: HTML content 
// and plain text content.
// The HTML part is divided into two related parts: the HTML content
// and an embedded image. The image is sent as an inline attachment.
/////////////////////////////////////////////////////////////////////
private void ExampleGMail
        (
        string Host,
        string UserName,
        string RefreshToken,
        string FromName,
        string FromAddress,
        string ToName,
        string ToAddress,
        string MessageSubject,
        string MessageHtml
        )
    {
    try
        {
        // Create smtp client.
        if(ConnectionGMail == null) ConnectionGMail = 
          new SecureSmtpClient(Host, UserName, new SecureSmtpOAuth2(RefreshToken));

        // Create mail message object.
        SecureSmtpMessage Message = new SecureSmtpMessage();

        // Set subject.
        Message.Subject = MessageSubject;

        // Set mail from address and display name.
        Message.From = new MailAddress(FromAddress, FromName);

        // Add minimum one or more recipients.
        // in addition you can add CC or BCC recipients.
        Message.To.Add(new MailAddress(ToAddress, ToName));

        // This message is made of two alternative methods for content plus 
        // an attachment file.
        // Create mixed multipart boundary.
        SecureSmtpMultipart Mixed = new SecureSmtpMultipart(MultipartType.Mixed);
        Message.RootPart = Mixed;

        // Create alternative boundary.
        // There are two alternatives to the content: plain text and HTML.
        // NOTE: The recipient of the message will display the last part it knows
        // how to display. For email program that can handle both HTML and text 
        // make sure the HTML part is last part.
        SecureSmtpMultipart Alternative = new SecureSmtpMultipart(MultipartType.Alternative);
        Mixed.AddPart(Alternative);

        // Add plain text mail body contents.
        SecureSmtpContent PlainTextContent = 
             new SecureSmtpContent(ContentType.Plain, PlainTextView);
        Alternative.AddPart(PlainTextContent);

        // The HTML alternative has two related parts. 
       // The HTML content and an inline image attachment
        // create related boundary
        SecureSmtpMultipart Related = new SecureSmtpMultipart(MultipartType.Related);
        Alternative.AddPart(Related);

        // Add html mail body content.
        SecureSmtpContent HtmlContent = new SecureSmtpContent(ContentType.Html, HtmlView);
        Related.AddPart(HtmlContent);

        // Add inline image attachment.
        // NOTE image id is set to IMAGE001 
        // this id must match the html image id in HtmlView text.
        SecureSmtpAttachment ImageAttachment = 
          new SecureSmtpAttachment(AttachmentType.Inline, "EmailImage.png", "IMAGE001");
        ImageAttachment.MediaType = "image/png";
        Related.AddPart(ImageAttachment);
        
        // Add file attachment to the email.
        // The recipient of the email will be able to save it as a file.
        SecureSmtpAttachment PdfAttachment = 
          new SecureSmtpAttachment(AttachmentType.Attachment, "rfc2045.pdf");
        Mixed.AddPart(PdfAttachment);

        // Send mail.
        ConnectionGMail.SendMail(Message);
        MessageBox.Show("Email was successfully sent");
        }

    // Catch exceptions.
    catch (Exception Exp)
        {
        MessageBox.Show(Exp.Message);
        }
    return;
    }

历史

  • 2017/03/01:版本 1.0 原始版本
  • 2019/06/20:版本 2.0 支持主题行包含非 ASCII 字符
© . All rights reserved.