Microsoft MVP성태의 닷넷 이야기
.NET Framework: 761. C# - SmtpClient로 SMTP + SSL/TLS 서버를 이용하는 방법 [링크 복사], [링크+제목 복사],
조회: 15074
글쓴 사람
정성태 (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
정성태

... 46  47  48  49  50  51  52  53  54  55  [56]  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12233정성태6/19/20209460오류 유형: 619. SQL 서버 - The transaction log for database '...' is full due to 'LOG_BACKUP'. - 두 번째 이야기
12232정성태6/19/20208405오류 유형: 618. SharePoint - StoreBusyRetryLater 오류
12231정성태6/15/202010885.NET Framework: 911. Console/Service Application을 위한 SynchronizationContext - AsyncContext
12230정성태6/15/202010242오류 유형: 617. IMetaDataImport::GetMethodProps가 반환하는 IL 코드 주소(RVA) 문제
12229정성태6/13/202012086.NET Framework: 910. USB/IP PROJECT를 이용해 C#으로 USB Keyboard + Mouse 가상 장치 만들기 [1]
12228정성태6/12/202012191.NET Framework: 909. C# - Source Generator를 적용한 XmlCodeGenerator파일 다운로드1
12227정성태6/12/202016170오류 유형: 616. Visual Studio의 느린 업데이트 속도에 대한 원인 분석 [5]
12226정성태6/11/202013445개발 환경 구성: 493. OpenVPN의 네트워크 구성 [4]파일 다운로드1
12225정성태6/11/202012443개발 환경 구성: 492. 윈도우에 OpenVPN 설치 - 클라이언트 측 구성
12224정성태6/11/202020372개발 환경 구성: 491. 윈도우에 OpenVPN 설치 - 서버 측 구성 [1]
12223정성태6/9/202014320.NET Framework: 908. C# - Source Generator 소개 [10]파일 다운로드2
12222정성태6/3/202010185VS.NET IDE: 146. error information: "CryptQueryObject" (-2147024893/0x80070003)
12221정성태6/3/20209922Windows: 170. 비어 있지 않은 디렉터리로 symbolic link(junction) 연결하는 방법
12220정성태6/3/202012377.NET Framework: 907. C# DLL로부터 TLB 및 C/C++ 헤더 파일(TLH)을 생성하는 방법
12219정성태6/1/202011483.NET Framework: 906. C# - lock (this), lock (typeof(...))를 사용하면 안 되는 이유파일 다운로드1
12218정성태5/27/202011400.NET Framework: 905. C# - DirectX 게임 클라이언트 실행 중 키보드 입력을 감지하는 방법 [3]
12217정성태5/24/20209870오류 유형: 615. Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
12216정성태5/15/202013034.NET Framework: 904. USB/IP PROJECT를 이용해 C#으로 USB Keyboard 가상 장치 만들기 [14]파일 다운로드1
12215정성태5/12/202018053개발 환경 구성: 490. C# - (Wireshark의) USBPcap을 이용한 USB 패킷 모니터링 [10]파일 다운로드1
12214정성태5/5/202010376개발 환경 구성: 489. 정식 인증서가 있는 경우 Device Driver 서명하는 방법 (2) - UEFI/SecureBoot [1]
12213정성태5/3/202012009개발 환경 구성: 488. (User-mode 코드로 가상 USB 장치를 만들 수 있는) USB/IP PROJECT 소개
12212정성태5/1/20209670개발 환경 구성: 487. UEFI / Secure Boot 상태인지 확인하는 방법
12211정성태4/27/202011983개발 환경 구성: 486. WSL에서 Makefile로 공개된 리눅스 환경의 C/C++ 소스 코드 빌드
12210정성태4/20/202012444.NET Framework: 903. .NET Framework의 Strong-named 어셈블리 바인딩 (1) - app.config을 이용한 바인딩 리디렉션 [1]파일 다운로드1
12209정성태4/13/202010502오류 유형: 614. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우 (2)
12208정성태4/12/20209970Linux: 29. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우
... 46  47  48  49  50  51  52  53  54  55  [56]  57  58  59  60  ...