C# - SmtpClient로 SMTP + SSL/TLS 서버를 이용하는 방법
SMTP 서버에 아래와 같이 TLS를 설정한 경우,

(Require TLS encryption)
SmtpClient를 이용해 다음과 같이 코딩을 할 수 있습니다.
SmtpClient smtp = new SmtpClient();
smtp.Host = "...[hostname]...";
smtp.EnableSsl = true;
MailAddress from = new MailAddress("test1@test.com");
MailAddress to = new MailAddress("test2@test.com");
MailMessage msg = new MailMessage(from, to);
msg.Body = "Test is good";
msg.Subject = "Test mail";
smtp.Send(msg);
그런데, Send에서 인증서 관련한 여러 가지 예외가 발생할 수 있습니다. 이럴 때는 ServerCertificateValidationCallback을 등록해 두면,
static void Main(string[] args)
{
System.Net.ServicePointManager.ServerCertificateValidationCallback += callBackFunc;
SmtpClient smtp = new SmtpClient();
...[생략]...
smtp.Send(msg);
}
static bool callBackFunc(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return false;
}
상황에 따른 오류를 sslPolicyErrors를 통해 알아낼 수 있습니다.
가령, SMTP 서버의 인증서에 등록된 Subject가 "smtpsvc.test.com"인데 다음과 같이 Host 이름을 주면,
smtp.Host = "192.168.100.5"; // "smtpsvc.test.com"가 아닌 다름 이름을 준 경우.
sslPolicyErrors 매개 변수는 System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch 값으로 그 사실을 알려주면서 Send 메서드에서는 다음과 같은 예외가 발생합니다.
Unhandled Exception: System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
...[생략]...
at System.Net.Mail.ReadLinesCommand.Send(SmtpConnection conn)
at System.Net.Mail.EHelloCommand.Send(SmtpConnection conn, String domain)
at System.Net.Mail.SmtpConnection.GetConnection(ServicePoint servicePoint)
at System.Net.Mail.SmtpTransport.GetConnection(ServicePoint servicePoint)
at System.Net.Mail.SmtpClient.GetConnection()
at System.Net.Mail.SmtpClient.Send(MailMessage message)
at ConsoleApp1.Program.Main(String[] args) in E:\ConsoleApp1\ConsoleApp1\Program.cs:line 34
또는 정상적으로 Host 명을 주었는데, 여전히 System.Security.Authentication.AuthenticationException 예외가 발생하면서 sslPolicyErrors == System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors 오류 값이 나올 수 있습니다. 이럴 때는 해당 인증서의 chain에 등록된 상위 인증서들을 클라이언트에서 신뢰할 수 없기 때문입니다. 대개의 경우 자체 인증서 서비스를 설치해서 운영하는 경우 저런 상황이 발생합니다. 따라서 SMTP SSL/TLS에 사용 중인 인증서를 발급한 자체 인증서 서비스의 (공개키만 보유한) 루트 인증서를 C# 클라이언트 프로그램이 실행되는 컴퓨터에 등록해 줘야 합니다.
또는, 상위 기관 없이 자체 발급한 인증서를 사용한다면 해당 (공개키만 보유한) 인증서만 동일하게 클라이언트에 등록해 주면 됩니다.
물론, 테스트를 위한 경우라면 인증서의 유효성 검사를 ServerCertificateValidationCallback을 이용해 무시할 수 있습니다. 즉, 다음과 같이 무조건 true를 반환하도록 만들면 됩니다.
static bool callBackFunc(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
ServerCertificateValidationCallback은 과거에도 WCF SSL 통신 관련해서 사용한 적이 있었습니다. ^^
basicHttpBinding + 사용자 정의 인증 구현
; https://www.sysnet.pe.kr/2/0/1082
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]