ということをやりました。STARTTLSに関してはAWS SESのサンプルにドキュメントがある等、よく記事を見かけるのですが、SMTPS over SSL(以下SMTPS)はあまりよい記事がなかった。
ので、後世のために残します。

目次

  1. 環境
  2. STARTTLSとSMTPS
  3. STARTTLS
  4. SMTPS
    1. 実装の前に…
    2. SMTPSでの実装
  5. 通信を確認する
  6. TLSとSSL
  7. 平文での送信
  8. その他

2016/10/31 追記

ちなみに、今回のコードは文字コードとか7bit/8bitとか一切考慮していないので、そのあたり注意してください。
また、STARTTLSはSystem.Net.Mail.SmtpClientを使用していますが、Xamarinの場合はそれでも「非推奨(旧型式)」の警告がでるようです。

XamarinでSmtpClientが旧形式の警告が表示されてしまう

上記の質問を見ると、この記事のSMTPS時に使用しているMailKitを使用するように促されるようです。
MS的にはSystem.Net.Mail.SmtpClientはもう使用せずに、OSSのMailKitを使ってくださいってことなのかな?今回の記事ではMailKitはSMTPSでしか使用してないですが、当然STARTTLSでも使用できます。

環境


今回は下記の環境で開発してます。

  • .NET Framework 4.5
  • MailKit 1.8.1 (SMTPS時に使用)

STARTTLSとSMTPS


下記のページが参考になります。

TLS/SSLで電子メールの安全性を高める

要するに…

  • STARTTLS

    • 受信側がTLS/SSLに対応しているか確認する。
    • されていれば暗号化して送る。されていないと平文で送る。
    • 一般的に587番ポート
  • SMTPS

    • 最初から最後まで暗号化して送る。
    • 一般的に465番ポート

では、どちらを使うかです。全部SMTPSでやればいいのではと思うのですが、サーバー側で対応していなかったりすることがあるので、全部SMTPSというわけにはいかないようです。使い分けは下記のteratailの質問が詳しいので、そちらを参考にするといいと思います。

SMTP over SSLとSMTP STARTTLSのどちらを使うべきか

では実際にどう書くか下記に記述していきます。

STARTTLS


System.Net.Mail.SmtpClientを使えば簡単です。こんな感じ。一瞬ですね。
他にもプロパティがいろいろありますが、省略。気が向いたら別途書くかもしれません。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace sample
{
class Program
{
static void Main(string[] args)
{
string host = "192.168.1.10";
int port = 587;

try
{
using (var smtp = new System.Net.Mail.SmtpClient(host, port))
{
smtp.Timeout = 10000;
smtp.Credentials = new System.Net.NetworkCredential("sender@example.com", "password");
smtp.EnableSsl = true;

using (var mail = new System.Net.Mail.MailMessage())
{
mail.From = new System.Net.Mail.MailAddress("sender@example.com");
mail.To.Add("receiver@example.com");
mail.Subject = "題名:テスト";
mail.SubjectEncoding = Encoding.UTF8;
mail.Body = "テストメールです";
mail.BodyEncoding = Encoding.UTF8;

smtp.Send(mail);
}
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}

SMTPS


実装の前に…

こいつはなんとSystem.Net.Mailでサポートされていません。(というかSmtpClient.EnableSsl プロパティがSMTPSで使えない。)

以上です。

だと悲しいので実装方法を調べた。
結果、以下のいずれかで実装できるってのがわかった。

  • System.Web.Mailを使う(ただし、非推奨)
  • オレオレで実装する
  • Nugetでパッケージ探す

まず、一つ目System.Web.Mailを使う方法。これが検索結果では最も引っかかる。

例えば、下記の有名な「DOBON.NET プログラミング道」さんや「Stack over flow」の最も今回の要件に近いと思われる質問でもこの方法がベストアンサーに挙げられていたりする。

SMTP認証でメールを送信する

How can I send emails through SSL SMTP with the .NET Framework?

が、こいつは非推奨で「非推奨だからSystem.Net.Mailを使いましょ~ね~」という警告が出る。上記2つとも記事が古いので仕方ないと思うのだけども、要するに非推奨のやつとか使いたくない。使いたくないよね?

そこで次がオレオレで実装する案がでてくるわけですね。いろいろ調べたところ、自前で実装されてる方の記事が2つみつかりました。

SMTP(SSL有り)でメール送信

SMTP Over SSL 接続で配送依頼を行う DLL

「あ~これでイケるぜ!」とか思いながらオレオレ実装をはじめたのはいいんですが…
これ、クライアントとサーバーとのハンドシェイクとかも自分で書かないといけない んですね…(前述の2記事もやってます)
う~ん、そこまで理解して書くのは正直時間がない…ということで、この案はあきらめました。(いやまあ、コピペしてもいいんですけど、解らずに実装してやらかすのが嫌なので…)

そこで、最後の案です。Nugetで探す。 むしろオレオレ実装の前にやれよ
で、見つけたのがMailKitというパッケージです。開発も活発だし、作ってる人がMonoプロジェクトに所属しているっぽいので、これで行きましょう。オレオレ実装で時間を無駄にしてはいけません。あるものを使いましょう。

SMTPSでの実装

MailKitを使って実装します。Nugetでパッケージをインストールして、後は下記の通りですね。メール本文の作成にはMimeKitってのを使用していますが、これはMailKitインストールと同時にインストールされます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using System;
using MailKit;
using MimeKit;

namespace sample
{
class Program
{
static void Main(string[] args)
{
string host = "192.168.1.10";
int port = 465;

try
{
using (var smtp = new MailKit.Net.Smtp.SmtpClient())
{
//第3引数のオプションでTLS(SSL)を使うかどうか指定する
smtp.Connect(host, port, SecureSocketOptions.SslOnConnect);
smtp.Authenticate("user", "password");

var mail = new MimeKit.MimeMessage();
var builder = new MimeKit.BodyBuilder();

mail.From.Add(new MimeKit.MailboxAddress ("","sender@example.com"));
mail.To.Add(new MimeKit.MailboxAddress ("", "receiver@example.com"));
mail.Subject = "題名:テスト";
builder.TextBody = "テストです。";
mail.Body = builder.ToMessageBody();

smtp.Send(mail);
smtp.Disconnect(true);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}

通信を確認する


別にやらなくてもいいんですけど、気になったのでSMTPSの方だけやってみました。
WireSharkとかで確認するといいと思います。結果のスクリーンショットとかは撮ってないのでないです。

TLSとSSL


WireSharkで確認すると、気付くんですね。「あれ、SMTPS(SMTP over SSL)なのにプロトコルがTLSやん…」って。
で、いろいろ調べると…

SSLとTLSの違いと脆弱性
通信用語の基礎知識
理解してるつもりの SSL/TLS でも、もっと理解したら面白かった話

要するに、SSLってのは脆弱性が発見されたので3.0が最後で廃止になってます。その後SSL3.0をもとにTLS1.0が策定されるのですが、SSLが普及してしまっていたので読み方が残ってしまっているということらしいです。
SMTP over SSLという呼び方もたぶんその時の名残なんでしょう。

いや、やめろよ紛らわしい。

平文での送信


まあ、使わないと思いますが、平文で送信したい方は前述のSTARTTLSのコードのポートを25に変更してsmtp.EnableSsl = true;を削除(あるいはfalse)にしたら平文で送信できます。

その他


前述のコードでAmazon SESを使用してメールを送信するとこまでやったので、次回はそれについて書こうと思います。