サーバー側からの SMTP メール送信には、その昔から
Wankuma.Net.Mail を活用させて頂いている。
さて最近、これも時代の流れで、SMTP Auth に対応した SMTP メール送信を実装する必要が発生。
ところが、Wankuma.Net.Mail は SMTP Auth に対応していない。
ざっと検索してみた限りでは、SMTP Auth 対応版の Wankuma.Net.Mail が流通していることは見つけられなかった。
そこで自前で Wankuma.Net.Mail を SMTP Auth 対応に変更してみた。
SmtpTransporter クラスに、SMTP サーバーとの対話コマンド毎のメソッドが用意されているので、ここに SMTPAuth(string userName, string pasword) メソッドを付け加え、このメソッド内で実装することにした。
詳細は割愛するが骨子は次のとおり。
まずは SMTP サーバーに AUTH コマンドを送信して、チャレンジのキーをもらう。
以下ではコードは省略しているが、SMTP サーバーからはステータス 334 が返されることを確認しておく。
this.ライタ.WriteLine("AUTH CRAM-MD5");
var 応答情報リスト = this.リーダ.Read();
ステータス334とともに、チャレンジのキーが返信されている。
もらったチャレンジのキーはBase64でエンコードされているのでデコードしておく。
var encodedKey = 応答情報リスト[0].Message;
var key = Convert.FromBase64String(key);
次に、SMTP Auth 用のパスワードで、このチャレンジのキーを HMAC MD5 でハッシュする。
.NET では HMACMD5 クラスが利用できる。HMACMD5 のコンストラクタにパスワードを渡してインスタンス生成し、そのインスタンスの ComputeHash メソッドに、ハッシュしたいバイト列(ここでは、Base64 デコード済みのチャレンジのキー)を渡せば、ハッシュ値のバイト列が返ってくる。
var md5 = new HMACMD5(Encoding.ASCII.GetBytes(password));
var hash = TextConvert.BytesToHexString(md5.ComputeHash(key));
ハッシュ値のバイト列は16進文字列に書式化してやる必要がある。
TextConvert クラスに
BytesToHexString 静的メソッドを追加実装しておいた。
最後に、こうして求めた HMAC MD5 16進文字列と、AMTP Auth 用のユーザー名とを、半角空白を区切りに挟んで連結し、Base64 でエンコードしてから、SMTP サーバーにチャレンジに対するレスポンスとして送信すればよい。
var res = Convert.ToBase64String(Encoding.ASCII.GetBytes(userName + " " + hash));
this.ライタ.WriteLine(res);
SMTP サーバーからはステータス 235 が返されていれば、認証完了である。
以上で SMTPAuth() メソッドの実装が完了したので、あとは、EHLO() メソッド呼び出し後に SMTPAuth() メソッド呼び出しを追加して完成だ。
少しばかりはまったのは、HMAC MD5 でハッシュした結果のバイト列を 16 進文字列に書式化する際に、英字は小文字にしなければならないところ。
当初誤って英字を大文字にしていたため、うまく認証が通らず、しばらく悩んでしまった。