Microsoft MVP성태의 닷넷 이야기
.NET Framework: 761. C# - SmtpClient로 SMTP + SSL/TLS 서버를 이용하는 방법 [링크 복사], [링크+제목 복사]
조회: 14966
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)

C# - SmtpClient로 SMTP + SSL/TLS 서버를 이용하는 방법

SMTP 서버에 아래와 같이 TLS를 설정한 경우,

cert_smtp_1.png
(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




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/8/2018]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2018-07-13 01시39분
[황윤하] 안녕하세요 궁금한점이 있어 댓글 남깁니다.
위와 같이 인증 콜백 부분에서 콜백값이 RemoteCertificateNameMismatch , RemoteCertificateChainErrors 두개가 나타납니다.
특정 회사에서 테스트 시
[guest]
2018-07-13 01시42분
[황윤하] 아.. 댓글이 이상하게 남겨졌네요.. 이어서
특정 회사에서 테스트 시에만 저렇게 두개가 남겨지는데 이유를 잘 모르겠습니다.
인증 프로세스 콜백값을 임의로 true로 할수는 없습니다. (인증프로세스가 꼭 포함되어야 한다고 했습니다.)
저런 에러가 나는 이유가 무엇일까요?
그 특정회사는 Exchange를 사용하고 있으며 회사내에서 사용하는 메일 프로그램이 따로 있습니다.
피씨내에 아웃룩을 깔고 회사내 메일 프로그램에 전송 시 메일은 전송이 되는데 제가 개발한 프로그램에서는 전송이 되지 않습니다.
제가 개발한 프로그램에서 인증하는 과정에서 문제가 생긴 듯 한데.. 잘 모르겠습니다....ㅜㅜ
[guest]
2018-07-13 02시04분
저렇게 한다고 해서 "인증 프로세스"가 없어지는 것은 아닙니다. 인증은 그 나름대로 진행되지만, 단지 저 콜백은 "인증서의 유효성"만 체크하는 것을 생략할 수 있는 것입니다.

두 개의 메시지에 대한 오류 원인은 본문에 설명해 놓았습니다. 자세히 읽어보세요.
정성태
2018-07-13 02시09분
[황윤하] 아!! 네!! 인증 프로세스가 없어지는게 아닌거군요.. return을 true로 해서 다시 테스트 해보겠습니다! 좋은정보 정말 감사합니다!!!!!
[guest]
2021-03-26 09시22분
jstedfast/MailKit - cross-platform mail client library built on top of MimeKit.
; https://github.com/jstedfast/MailKit
정성태

... 31  32  33  34  35  36  [37]  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12704정성태7/12/20217977개발 환경 구성: 576. Azure VM의 서비스를 Azure Web App Service에서만 접근하도록 NSG 설정을 제한하는 방법
12703정성태7/11/202113624개발 환경 구성: 575. Azure VM에 (ICMP) ping을 허용하는 방법
12702정성태7/11/20218754오류 유형: 733. TaskScheduler에 등록된 wacs.exe의 Let's Encrypt 인증서 업데이트 문제
12701정성태7/9/20218396.NET Framework: 1075. C# - ThreadPool의 스레드는 반환 시 ThreadStatic과 AsyncLocal 값이 초기화 될까요?파일 다운로드1
12700정성태7/8/20218819.NET Framework: 1074. RuntimeType의 메모리 누수? [1]
12699정성태7/8/20217604VS.NET IDE: 167. Visual Studio 디버깅 중 GC Heap 상태를 보여주는 "Show Diagnostic Tools" 메뉴 사용법
12698정성태7/7/202111577오류 유형: 732. Windows 11 업데이트 시 3% 또는 0%에서 다운로드가 멈춘 경우
12697정성태7/7/20217462개발 환경 구성: 574. Windows 11 (Insider Preview) 설치하는 방법
12696정성태7/6/20218029VC++: 146. 운영체제의 스레드 문맥 교환(Context Switch)을 유사하게 구현하는 방법파일 다운로드2
12695정성태7/3/20218072VC++: 145. C 언어의 setjmp/longjmp 기능을 Thread Context를 이용해 유사하게 구현하는 방법파일 다운로드1
12694정성태7/2/20219994Java: 24. Azure - Spring Boot 앱을 Java SE(Embedded Web Server)로 호스팅 시 로그 파일 남기는 방법 [1]
12693정성태6/30/20217767오류 유형: 731. Azure Web App Site Extension - Failed to install web app extension [...]. {1}
12692정성태6/30/20217650디버깅 기술: 180. Azure - Web App의 비정상 종료 시 남겨지는 로그 확인
12691정성태6/30/20218500개발 환경 구성: 573. 테스트 용도이지만 테스트에 적합하지 않은 Azure D1 공유(shared) 요금제
12690정성태6/28/20219309Java: 23. Azure - 자바(Java)로 만드는 Web App Service - Tomcat 호스팅
12689정성태6/25/20219835오류 유형: 730. Windows Forms 디자이너 - The class Form1 can be designed, but is not the first class in the file. [1]
12688정성태6/24/20219549.NET Framework: 1073. C# - JSON 역/직렬화 시 리플렉션 손실을 없애는 JsonSrcGen [2]파일 다운로드1
12687정성태6/22/20217546오류 유형: 729. Invalid data: Invalid artifact, java se app service only supports .jar artifact
12686정성태6/21/20219983Java: 22. Azure - 자바(Java)로 만드는 Web App Service - Java SE (Embedded Web Server) 호스팅
12685정성태6/21/202110201Java: 21. Azure Web App Service에 배포된 Java 프로세스의 메모리 및 힙(Heap) 덤프 뜨는 방법
12684정성태6/19/20218655오류 유형: 728. Visual Studio 2022부터 DTE.get_Properties 속성 접근 시 System.MissingMethodException 예외 발생
12683정성태6/18/202110169VS.NET IDE: 166. Visual Studio 2022 - Windows Forms 프로젝트의 x86 DLL 컨트롤이 Designer에서 오류가 발생하는 문제 [1]파일 다운로드1
12682정성태6/18/20217838VS.NET IDE: 165. Visual Studio 2022를 위한 Extension 마이그레이션
12681정성태6/18/20217177오류 유형: 727. .NET 2.0 ~ 3.5 + x64 환경에서 System.EnterpriseServices 참조 시 CS8012 경고
12680정성태6/18/20218269오류 유형: 726. python2.7.exe 실행 시 0xc000007b 오류
12679정성태6/18/20218876COM 개체 관련: 23. CoInitializeSecurity의 전역 설정을 재정의하는 CoSetProxyBlanket 함수 사용법파일 다운로드1
... 31  32  33  34  35  36  [37]  38  39  40  41  42  43  44  45  ...