本製品は、SMTP over SSL(SMTPs)およびSTARTTLS(STLS)でのSSL通信に対応しています。
SSLを使用してSMTPサーバーとセキュアなデータのやり取りを行うには、サーバーとの接続時にMailSecurityクラスの各プロパティを設定し、さらに同クラスのValidationCallbackフィールドにサーバーのSSL証明書を検証するためのプロシージャを指定します。その他の設定や処理は、通常の接続方法と同じです。
SMTP over SSLでSSL通信を行う場合は、EncryptプロパティをImplicit(暗黙的)モードに設定します。この場合、クライアントとサーバーの両方がセッションの間中SSLを使用するという前提で、専用のポート(一般的には465番の通信ポート)を使用して最初からSSL通信が行われます。
STARTTLSでSSL通信を行う場合は、EncryptプロパティをExplicit(明示的)モードに設定します。この場合、クライアントは通常のポート(一般的には587番または25番の通信ポート)を使用してサーバーに接続し、STARTTLSコマンドを実行します。サーバーがこのコマンドに対応しており、肯定的な応答を返してきた場合に、その後のセッションでSSL通信が行われます。
 |
- SSL通信を行うには、サーバー側でSSLの設定が必要です。
- サンプルコードの先頭にある名前空間の参照文(Visual BasicではImports、C#ではusing)は、ソースファイルの先頭に記述してください。
- 本トピック以降のサンプルコードでは、サーバー接続設定に関するコードの記載を省略している場合があります。
- 実際の動作では、本トピックで説明している処理の後、Sendメソッドなどの処理を実行したタイミングで、SMTPサーバーとの通信が開始されます。
|
SSL証明書を検証せずに接続する
SSLでサーバーに接続する、もっとも単純な方法のサンプルコードを示します。
サーバーからSSL証明書を受信したときに実行するプロシージャを、ValidationCallbackフィールドに指定します。本来このプロシージャには、証明書の妥当性をチェックし、問題がない場合はTrue、問題がある場合はFalseを戻り値として返す処理を実装します。
ただし、以下のサンプルコードではプロシージャ内で証明書の妥当性を判断していません。この場合、サーバーから受信した証明書が信頼されないものであっても、その証明書を承認してSSL通信が確立されます。
この方法は、接続先が信頼できるサーバーであることがあらかじめ分かっている場合や、テスト用サーバーに証明書作成ツール(Makecert.exe)で作成した自己証明書を置いた場合など、SSL証明書の検証を省略しても問題のない条件下のみで利用できます。
Imports Dart.Mail
Imports Dart.Mail.Smtp
Imports System.Net.Security
Imports System.Security.Authentication
Imports System.Security.Cryptography.X509Certificates
Private Sub SecureSmtpLogin()
' SSL接続の種類を指定します。
Smtp1.Session.Security.Encrypt = Encrypt.Explicit
' プロトコルを指定します。
Smtp1.Session.Security.Protocols = System.Security.Authentication.SslProtocols.Tls
' サーバーのSSL証明書を検証する際に呼び出されるコールバック関数を指定します。
Smtp1.Session.Security.ValidationCallback = AddressOf remoteCertificateValidation
' SMTPサーバーのサーバー名、ポート番号、ユーザー名、パスワードを指定します。
Smtp1.Session.RemoteEndPoint = New Dart.Mail.IPEndPoint("smtp.myMailServer.com", Smtp.GetDefaultPort(Smtp1.Session))
Smtp1.Session.Username = "myUsername"
Smtp1.Session.Password = "myPassword"
' 認証方式を設定します。
Smtp1.Session.Authentication = Authentication.Login
End Sub
Private Function remoteCertificateValidation(ByVal sender As Object, ByVal remoteCertificate As X509Certificate, _
ByVal chain As X509Chain, ByVal sslPolicyErrors As SslPolicyErrors)
' SSL証明書の検証を行わず、すべての証明書に対してTrue(問題なし)を返します。
Return True
End Function
using Dart.Mail;
using Dart.Mail.Smtp;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
private void SecureSmtpLogin()
{
// SSL接続の種類を指定します。
smtp1.Session.Security.Encrypt = Encrypt.Explicit;
// プロトコルを指定します。
smtp1.Session.Security.Protocols = System.Security.Authentication.SslProtocols.Tls
// サーバーのSSL証明書を検証する際に呼び出されるコールバック関数を指定します。
smtp1.Session.Security.ValidationCallback = remoteCertificateValidation;
// SMTPサーバーのサーバー名、ポート番号、ユーザー名、パスワードを指定します。
smtp1.Session.RemoteEndPoint = new Dart.Mail.IPEndPoint("smtp.myMailServer.com", Smtp.GetDefaultPort(smtp1.Session));
smtp1.Session.Username = "myUsername";
smtp1.Session.Password = "myPassword";
// 認証方式を設定します。
smtp1.Session.Authentication = Authentication.Login;
}
private bool remoteCertificateValidation(Object sender, X509Certificate remoteCertificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// SSL証明書の検証を行わず、すべての証明書に対してTrue(問題なし)を返します。
return true;}
SSL証明書を検証した上で接続する
前述の「SSL証明書を検証せずに接続する」方法では、サーバーのSSL証明書をすべて「問題なし」と見なしてSSL接続を行っていました。次はSSL証明書を検証した上で、SSLでサーバーに接続するサンプルコードを示します。
サーバーのSSL証明書を検証して、問題がない場合はTrue、問題がある場合はFalseを戻り値として返すプロシージャを作成し、そのプロシージャをValidationCallbackフィールドに指定します。この検証処理はSendメソッドなどを実行した際に実行されますが、Falseが返された場合はメソッドの処理で例外が発生します。
ValidationCallbackフィールドに指定する、サーバーのSSL証明書を検証するプロシージャは、RemoteCertificateValidationCallbackデリゲート型(System.Net.Security名前空間のクラス)のプロシージャです。以下のサンプルコードでは、一例として、.NET Framework標準の機能のみを使用したremoteCertificateValidationプロシージャで、SSL証明書の検証を行っています。各処理の詳細については、MSDNライブラリなどを参照してください。
Imports Dart.Mail
Imports Dart.Mail.Smtp
Imports System.Net.Security
Imports System.Security.Authentication
Imports System.Security.Cryptography.X509Certificates
Private Sub SecureSmtpLogin()
' SSL接続の種類を指定します。
Smtp1.Session.Security.Encrypt = Encrypt.Explicit
' プロトコルを指定します。
Smtp1.Session.Security.Protocols = System.Security.Authentication.SslProtocols.Tls
' サーバーのSSL証明書を検証する際に呼び出されるコールバック関数を指定します。
Smtp1.Session.Security.ValidationCallback = AddressOf remoteCertificateValidation
' SMTPサーバーのサーバー名、ポート番号、ユーザー名、パスワードを指定します。
Smtp1.Session.RemoteEndPoint = New Dart.Mail.IPEndPoint("smtp.myMailServer.com", Smtp.GetDefaultPort(Smtp1.Session))
Smtp1.Session.Username = "myUsername"
Smtp1.Session.Password = "myPassword"
' 認証方式を設定します。
Smtp1.Session.Authentication = Authentication.Login
End Sub
' サーバーのSSL証明書を検証するプロシージャの一例です。
Private Function remoteCertificateValidation(ByVal sender As Object, ByVal remoteCertificate As X509Certificate, _
ByVal chain As X509Chain, ByVal sslPolicyErrors As SslPolicyErrors) As Boolean
' サーバー証明書に問題がない場合、trueを返します。
If sslPolicyErrors = SslPolicyErrors.None Then
Return True
End If
Dim acceptCertificate As Boolean = True
Dim msg As String = "このサーバーの証明書には、以下の問題があります。" & Constants.vbCrLf
' サーバーが証明書を提示しなかった場合
If (sslPolicyErrors And _
SslPolicyErrors.RemoteCertificateNotAvailable) = SslPolicyErrors.RemoteCertificateNotAvailable Then
msg = msg & Constants.vbCrLf & " - サーバーから証明書が提示されませんでした。" & Constants.vbCrLf
acceptCertificate = False
Else
' 証明書の名前が一致しない場合
If ((sslPolicyErrors And _
SslPolicyErrors.RemoteCertificateNameMismatch) = SslPolicyErrors.RemoteCertificateNameMismatch) Then
msg = msg & Constants.vbCrLf & " - 証明書の名前が一致しませんでした。" & Constants.vbCrLf
acceptCertificate = False
End If
' 証明書に関するその他の問題がある場合
If (sslPolicyErrors And _
SslPolicyErrors.RemoteCertificateChainErrors) = SslPolicyErrors.RemoteCertificateChainErrors Then
For Each item As X509ChainStatus In chain.ChainStatus
If item.Status <> X509ChainStatusFlags.RevocationStatusUnknown AndAlso _
item.Status <> X509ChainStatusFlags.OfflineRevocation Then
Exit For
End If
If (item.Status <> X509ChainStatusFlags.NoError) Then
msg = msg & Constants.vbCrLf & " -" & item.StatusInformation
acceptCertificate = False
End If
Next item
End If
End If
' 検証結果が不正だった場合、メッセージボックスを表示します。
If acceptCertificate = False Then
msg = msg & Constants.vbCrLf & "接続を続行しますか?"
If MessageBox.Show(msg, "セキュリティの警告: サーバーのSSL証明書エラー", _
MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) = System.Windows.Forms.DialogResult.Yes Then
acceptCertificate = True
End If
End If
Return acceptCertificate
End Function
using Dart.Mail;
using Dart.Mail.Smtp;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
private void SecureSmtpLogin()
{
// SSL接続の種類を指定します。
smtp1.Session.Security.Encrypt = Encrypt.Explicit;
// プロトコルを指定します。
smtp1.Session.Security.Protocols = System.Security.Authentication.SslProtocols.Tls
// サーバーのSSL証明書を検証する際に呼び出されるコールバック関数を指定します。
smtp1.Session.Security.ValidationCallback = remoteCertificateValidation;
// SMTPサーバーのサーバー名、ポート番号、ユーザー名、パスワードを指定します。
smtp1.Session.RemoteEndPoint = new Dart.Mail.IPEndPoint("smtp.myMailServer.com", Smtp.GetDefaultPort(smtp1.Session));
smtp1.Session.Username = "myUsername";
smtp1.Session.Password = "myPassword";
// 認証方式を設定します。
smtp1.Session.Authentication = Authentication.Login;
}
private static bool remoteCertificateValidation(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// サーバー証明書に問題がない場合、trueを返します。
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
bool acceptCertificate = true;
string msg = "このサーバーの証明書には、以下の問題があります。\r\n";
// サーバーが証明書を提示しなかった場合
if ((sslPolicyErrors &
SslPolicyErrors.RemoteCertificateNotAvailable) ==
SslPolicyErrors.RemoteCertificateNotAvailable)
{
msg = msg + "\r\n - サーバーから証明書が提示されませんでした。\r\n";
acceptCertificate = false;
}
else
{
// 証明書がサーバー名と一致しない場合
if ((sslPolicyErrors &
SslPolicyErrors.RemoteCertificateNameMismatch) ==
SslPolicyErrors.RemoteCertificateNameMismatch)
{
msg = msg + "\r\n - 証明書の名前が一致しませんでした。\r\n";
acceptCertificate = false;
}
// 証明書に関するその他の問題がある場合
if ((sslPolicyErrors &
SslPolicyErrors.RemoteCertificateChainErrors) ==
SslPolicyErrors.RemoteCertificateChainErrors)
{
foreach (X509ChainStatus item in chain.ChainStatus)
{
if (item.Status !=
X509ChainStatusFlags.RevocationStatusUnknown &&
item.Status !=
X509ChainStatusFlags.OfflineRevocation)
break;
if (item.Status !=
X509ChainStatusFlags.NoError)
{
msg = msg + "\r\n -" + item.StatusInformation;
acceptCertificate = false;
}
}
}
}
// 検証が失敗した場合、メッセージボックスを表示します。
if (acceptCertificate == false)
{
msg = msg + "\r\n接続を続行しますか?";
if (MessageBox.Show(msg, "セキュリティの警告: サーバーのSSL証明書エラー",
MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) ==
DialogResult.Yes)
acceptCertificate = true;
}
return acceptCertificate;
}
クライアント認証
セキュアなSMTPサーバーは、接続時にクライアントへ証明書を要求し、身元の証明を求める場合があります。このプロセスをクライアント認証といいます。
ユーザーが証明書を選択する方法
サーバーからクライアントへ証明書の要求があった場合に、ユーザーがローカルシステムにある任意の証明書を選択し、クライアント認証に使用する方法です。
ユーザーが選択した証明書を戻り値として返すメソッドを作成し、そのメソッドをSelectionCallbackフィールドに指定します。サーバーから証明書の要求があったときには、このメソッドが実行され、戻り値として返された証明書がサーバーへ提示されます。
付属サンプルのフォーム「CertificateListForm」では、クライアント認証に使用する証明書をユーザーが選択するUI処理を実現しています。具体的な実装例についてはサンプルをご覧ください。
SelectionCallbackフィールドに指定する、クライアント認証に使用する証明書を返すメソッドは、LocalCertificateSelectionCallbackデリゲート型(System.Net.Security名前空間のクラス)のメソッドです。付属サンプルのLocalCertificateSelectionメソッドでは.NET Framework標準の機能のみを使用して、クライアントの証明書を選択しています。各処理の詳細については、MSDNライブラリなどを参照してください。
あらかじめ証明書を指定しておく方法
サーバーからの要求に対して提示する証明書を、あらかじめ指定しておく方法です。
ローカルシステムの証明書ストアにある任意の証明書を、サーバーとの接続前にCertificatesプロパティ(クライアント証明書のコレクション)にセットしておきます。SelectionCallbackフィールドにメソッドを指定しない場合、サーバーから証明書の要求があったときには、Certificatesプロパティにセットした証明書がクライアント認証に使用されます。
 |
Certificatesプロパティに複数の証明書が存在する場合は、コレクション内の最初の証明書(Certificates(0)またはCertificates[0])がクライアント認証に使用されます。 |
Imports Dart.Mail
Imports Dart.Mail.Smtp
Imports System.Net.Security
Imports System.Security.Authentication
Imports System.Security.Cryptography.X509Certificates
Private Sub SecureSmtpLogin()
' SSL接続の種類を指定します。
Smtp1.Session.Security.Encrypt = Encrypt.Explicit
' プロトコルを指定します。
Smtp1.Session.Security.Protocols = System.Security.Authentication.SslProtocols.Tls
' サーバーのSSL証明書を検証する際に呼び出されるコールバック関数を指定します。
Smtp1.Session.Security.ValidationCallback = AddressOf remoteCertificateValidation
' SMTPサーバーのサーバー名、ポート番号、ユーザー名、パスワードを指定します。
Smtp1.Session.RemoteEndPoint = New Dart.Mail.IPEndPoint("smtp.myMailServer.com", Smtp.GetDefaultPort(Smtp1.Session))
Smtp1.Session.Username = "myUsername"
Smtp1.Session.Password = "myPassword"
' 現在のユーザーの"My"証明書ストアを参照します。
Dim store As New X509Store(StoreName.My, StoreLocation.CurrentUser)
' 証明書ストアを読み取り専用で開きます。
store.Open(OpenFlags.ReadOnly)
' ストア内の証明書をCertificatesプロパティにセットします。
Smtp1.Session.Security.Certificates = store.Certificates
' 認証方式を設定します。
Smtp1.Session.Authentication = Authentication.Login
End Sub
Private Function remoteCertificateValidation(ByVal sender As Object, ByVal remoteCertificate As X509Certificate, _
ByVal chain As X509Chain, ByVal sslPolicyErrors As SslPolicyErrors) As Boolean
' コードの内容は前述の「SSL証明書を検証せずに接続する」
' または「SSL証明書を検証した上で接続する」を参照してください。
End Function
using Dart.Mail;
using Dart.Mail.Smtp;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
private void SecureSmtpLogin()
{
// SSL接続の種類を指定します。
smtp1.Session.Security.Encrypt = Encrypt.Explicit;
// プロトコルを指定します。
smtp1.Session.Security.Protocols = System.Security.Authentication.SslProtocols.Tls
// サーバーのSSL証明書を検証する際に呼び出されるコールバック関数を指定します。
smtp1.Session.Security.ValidationCallback = remoteCertificateValidation;
// SMTPサーバーのサーバー名、ポート番号、ユーザー名、パスワードを指定します。 smtp1.Session.RemoteEndPoint = new Dart.Mail.IPEndPoint("smtp.myMailServer.com", Smtp.GetDefaultPort(smtp1.Session));
smtp1.Session.Username = "myUsername";
smtp1.Session.Password = "myPassword";
// 現在のユーザーの"My"証明書ストアを参照します。
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
// 証明書ストアを読み取り専用で開きます。
store.Open(OpenFlags.ReadOnly);
// ストア内の証明書をCertificatesプロパティにセットします
smtp1.Session.Security.Certificates = store.Certificates;
// 認証方式を設定します。
smtp1.Session.Authentication = Authentication.Login;
}
private bool remoteCertificateValidation(Object sender, X509Certificate remoteCertificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// コードの内容は前述の「SSL証明書を検証せずに接続する」
// または「SSL証明書を検証した上で接続する」を参照してください。
}