성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <div style='font-family: 맑은 고딕, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>WCF 인증서 설정 관련 오류 정리</div><br /> <br /> WCF 사용자 정의 인증 구현 예제는, 사실 구현보다는 문제가 발생했을 때 오류에 대한 대응이 더 어렵습니다. 설정이 조금이라도 틀리면 오류가 발생하게 되고, 추상화가 워낙 잘 되어 있다보니 도대체 어느 설정에서 잘못되었는지 알 수 없을 때가 많기 때문입니다.<br /> <br /> 이번 회에서는 대표적인 오류 유형에 따른 원인을 살펴보겠습니다. 즉, 여기 나오는 예외들은 아래의 글을 실습하면서 만날 수 있는 유형들입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > WCF 사용자 정의 인증 구현 예제 ; <a target='_tab' href='/2/0/864'>http://www.sysnet.pe.kr/2/0/864</a> </pre> <hr style='width: 50%' /><br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.InvalidOperationException:<br /> The ChannelDispatcher at 'net.tcp://localhost:9001/myservice' with contract(s) '"IHelloWorld"' is unable to open its IChannelListener. ---> System.InvalidOperationException: The ChannelDispatcher at 'net.tcp://localhost:9001/myservice' with contract(s) '"IssueAndRenewSession"' is unable to open its IChannelListener. ---> System.InvalidOperationException: The service certificate is not provided. Specify a service certificate in ServiceCredentials.</span><br /> <br /><br /> <br /> 위와 같은 오류는 그래도 양반입니다. ^^ "The service certificate is not provided. Specify a service certificate in ServiceCredentials."과 같이 메시지에 해답이 있기 때문입니다. 해당 서비스가 clientCredentialType="UserName" 설정을 포함하기 때문에 보안을 위해 인증서를 필요로 하는데 <serviceCertificate /> 구성이 없기 때문에 발생한다는 것을 알 수 있습니다. 해결하기 위해서는 다음과 같은 식의 구성을 포함해야 겠지요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > <serviceCertificate findValue="myserver" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="Root" /> </pre> <br /> <hr style='width: 50%' /><br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.InvalidOperationException:<br /> Cannot find the X.509 certificate using the following search criteria: StoreName 'Root', StoreLocation 'LocalMachine', FindType 'FindBySubjectName', FindValue 'myserver'.</span><br /><br /> <br /> 역시 메시지에서 보는 것처럼 <serviceCertificate />에 지정된 인증서를 찾을 수 없기 때문에 발생한다는 것을 알 수 있습니다. 위의 예에서는 StoreLocation == "LocalMachine"인데 이 위치는 "인증서 관리자"에서 보면 "Local Computer" 영역이고 StoreName == "Root"는 "Trusted Root Certification Authorities"이므로 "SubjectName"이 "myserver"이라면 다음과 같은 영역에 인증서가 위치해 있어야 합니다.<br /> <br /> <img alt='wcf_username_auth_config_error_1.png' src='/SysWebRes/bbs/wcf_username_auth_config_error_1.png' /><br /> <br /> <hr style='width: 50%' /><br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.ArgumentException:<br /> The certificate 'CN=myserver' must have a private key that is capable of key exchange. The process must have access rights for the private key.<br /> </span><br /><br /> <br /> 이렇게 오류나는 경우는 해당 인증서가 "key exchange" 용으로 생성되지 않아서입니다. makecert를 이용해서 생성한 경우라면 다음과 같이 반드시 exchange 옵션을 주어야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > makecert -n "CN=myserver" -r <b style='COLOR: blue'>-sky exchange</b> -sv mycert.pvk mycert.cer </pre> <br /> <hr style='width: 50%' /><br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.ServiceModel.AddressAccessDeniedException: HTTP could not register URL http://+:9000/. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details). ---> System.Net.HttpListenerException: Access is denied</span><br /><br /> <br /> 이 오류는 뭐... 워낙 유명해서. ^^<br /> 대부분 Vista/7 이상의 운영체제에서 "관리자 권한"으로 실행되지 않은 응용 프로그램 내부에 WCF 서비스를 호스팅 할 때 발생합니다. (XP인 경우에도 비관리자 계정으로 로그한 경우 발생합니다.) 사용하려는 포트(위의 예에서는 9000번)를 미리 등록해 주어야 하는데 일반 사용자 권한으로는 등록할 수 없기 때문에 위와 같은 오류가 발생하는 것입니다.<br /> <br /> 물론, 방법은 간단합니다. "관리자 권한"으로 실행한 명령행에서 다음과 같이 포트를 등록해 주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > netsh http add urlacl url=http://+:9000/ user="[계정]" </pre> <br /> (엄밀히는 포트가 아닌 URL 단위인데, 자세한 설명은 생략합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.ServiceModel.AddressAlreadyInUseException: <br /> HTTP could not register URL http://+:9000/. Another application has already registered this URL with HTTP.SYS. ---> System.Net.HttpListenerException: Failed to listen on prefix 'http://+:9000/' because it conflicts with an existing registration on the machine.</span><br /><br /> <br /> "netsh http add urlacl"을 많이 사용하다 보면 이런 오류를 만날 가능성이 쪼끔 있습니다. ^^ 같은 포트로 이미 등록되어 있기 때문에 사용할 수 없다는 것입니다. 그런데... 가만 생각해 보면 이미 등록되어 있다면 그걸 사용하면 될 텐데 왜... 이런 오류가 발생했느냐는 것입니다.<br /> <br /> 이유는 "netsh http show urlacl"을 이용해서 현재 등록된 URL을 확인해 봐야 합니다. 위와 같은 오류가 발생했다면 아마도 같은 포트에 다음과 같은 식으로 스키마가 다른 체로 이미 등록되어 있는 경우입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > Reserved URL : <b style='COLOR: blue'>https</b>://+:9000/ User: TEST\mytestAccount Listen: Yes Delegate: No SDDL: D:(A;;GX;;;S-...[생략]...-1000) </pre> <br /> 보시는 것처럼, 현재 사용하려는 URL은 "<b style='COLOR: blue'>http</b>://+:9000/"인데, 이미 그 포트로 등록된 스키마는 "<b style='COLOR: blue'>https</b>://+:9000/"으로 충돌이 발생한 것입니다. 관리자 계정으로 실행한 "명령행"에서 "netsh http delete urlacl url=https://+:9000/"와 같은 식으로 기존 등록된 URL을 삭제해 주고 다시 http 스키마로 등록해 주면 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 잠시 오류 정리를 벗어나서. 사용자 정의 인증 모듈에서 Validate 메서드 안에서 인증을 허용하지 않는다면 예외를 발생하면 된다고 했는데요. 이에 대해서 다음의 블로그에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > Silverlight 3: Securing your WCF service with a custom username / password authentication mechanism ; <a target='_tab' href='http://blogs.infosupport.com/blogs/alexb/archive/2009/10/02/silverlight-3-securing-your-wcf-service-with-a-custom-username-and-password-authentication-mechanism.aspx'>http://blogs.infosupport.com/blogs/alexb/archive/2009/10/02/silverlight-3-securing-your-wcf-service-with-a-custom-username-and-password-authentication-mechanism.aspx</a> </pre> <br /> 아래와 같은 언급이 있습니다.<br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> If validation fails, throw a <b style='COLOR: blue'>SecurityTokenException</b> if you want a non informative message or a <b style='COLOR: blue'>FaultException</b> if you want a informative message. If validation succeeds, just do nothing. <br /> </span><br /><br /> <br /> 실제로 각각 아래와 같은 코드로 테스트를 해보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > throw new FaultException("Test account can be authenticated ONLY."); throw new SecurityTokenException("Test account can be authenticated ONLY."); </pre> <br /> SecurityTokenException으로 한 경우, 인자에 지정된 문자열이 다음과 같이 예외 메시지에서 나타나지 않습니다.<br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.ServiceModel.Security.MessageSecurityException:<br /> An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. ---> System.ServiceModel.FaultException: An error occurred when processing the security tokens in the message.<br /> </span><br /><br /> <br /> 대신 FaultException으로 한 경우에는 정상적으로 문자열 정보가 넘어오게 됩니다.<br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.ServiceModel.Security.MessageSecurityException:<br /> An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. ---> System.ServiceModel.FaultException: <b style='COLOR: blue'>Test account can be authenticated ONLY.</b></span><br /><br /> <br /> 어느 쪽을 선택하실 건가요? ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 인증서까지 정상적으로 설치했는데 다음과 같은 오류가 발생하는 경우가 있습니다.<br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.ArgumentException:<br /> The certificate 'CN=myserver' must have a private key that is capable of key exchange. The process must have access rights for the private key. ---> System.Security.Cryptography.CryptographicException: <b style='COLOR: blue'>Keyset does not exist</b></span><br /><br /> <br /> 얼핏 보면, 이전에 "key exchange" 유형으로 인증서를 만들지 않았을 때와 오류 메시지가 비슷한데요. 한가지 차이점은 "Keyset does not exist" 메시지가 있다는 정도입니다. 이런 경우에는 해당 인증서의 개인키에 대한 "파일 접근 권한"이 없어서입니다.<br /> <br /> FindPrivateKey 유틸리티를 이용해서 해당 인증서의 개인키 물리 파일 위치를 알아내고 cacls.exe를 이용해서 권한을 적절하게 부여해 주면 됩니다. 자세한 내용은 다음에서 참조하세요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > 인증서의 개인키를 담은 물리 파일 위치 알아내는 방법 ; <a target='_tab' href='/2/0/865'>http://www.sysnet.pe.kr/2/0/865</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> 이번 오류는 클라이언트를 실행할 때 발생할 수 있습니다.<br /> <br /> <span style='BACKGROUND-COLOR: #ccffcc; FONT-STYLE: italic; MARGIN: 10px 0px 10px 10px; WIDTH: 800px; FONT-FAMILY: 맑은 고딕, Consolas, Verdana; COLOR: #005555'> System.ServiceModel.Security.MessageSecurityException:<br /> Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was '192.168.1.2' but the remote endpoint provided DNS claim 'myserver'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity 'myserver' as the Identity property of EndpointAddress when creating channel proxy.</span><br /><br /> <br /> 다행히 위의 경우에도 오류 메시지에 답이 있습니다. 이런 상황은 인증서를 생성할 때 지정한 "CN=" 값이 WCF 클라이언트에서 endpoint에 지정한 주소와 다른 경우에 발생합니다.<br /> <br /> 예를 들어, 서버 측에서 사용한 인증서는 "CN=myserver"로 생성했는데, WCF 클라이언트 측에서는 endpoint 설정을 다음과 같이 한 경우입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > <endpoint name="TcpNetConf" address="net.tcp://<b style='COLOR: blue'>192.168.1.2</b>:9001/myservice" binding="netTcpBinding" bindingConfiguration="netTcpBindingConf" behaviorConfiguration="netTcpBehavior" contract="WcfLibrary.IHelloWorld"> </endpoint> </pre> <br /> 이런 경우에는 해결 방법이 다양합니다.<br /> <br /> <ol> <li>인증서를 CN=192.168.1.2로 해서 다시 받는다.</li> <li>또는, 접근 주소를 CN=myserver에 지정된 것처럼 "net.tcp://myserver:9001/myservice"로 변경한다.</li> <li>또는, identity 설정을 이용한다.</li> </ol> <br /> 2번의 경우, myserver 이름에 대한 IP가 정상적으로 풀이되지 않는다면 임시적으로 HOSTS 파일을 이용해서 지정해 주는 것으로 우회할 수 있습니다.<br /> <br /> 3번의 경우를 자세히 살펴봐야 할텐데요. 위와 같이 CN=myserver로 지정된 인증서를 유효하게 받아들이고 싶다면 다음과 같이 endpoint에 identity 설정을 해주는 것으로 해결할 수 있습니다<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; width: 800px; background-color: #fbedbb; overflow-x: scroll; font-family: Consolas, Verdana;' > <endpoint name="TcpNetConf" address="net.tcp://192.168.1.2:9001/myservice" ...[생략]... contract="WcfLibrary.IHelloWorld"> <b style='COLOR: blue'><identity> <dns value="myserver"/> </identity></b> </endpoint> </pre> <br /> <br /><br /><hr /><span style='color: Maroon'>[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1362
(왼쪽의 숫자를 입력해야 합니다.)