Error accessing web service through SSL from IIS-hosted application - c#

This has been bothering me for three days now and after tons of googling around I decided to post a question. I have a WCF service application ("local service") that connects to a "remote web service" (Java) securely (2-way certificate authentication).
My service-side config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="IhAdapterPortBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="Certificate" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://someserver.com:8085/IhAdapter" binding="basicHttpBinding"
bindingConfiguration="IhAdapterPortBinding" contract="IHAdapter.IhAdapter"
name="IhAdapterPort" behaviorConfiguration="IHAdapterEndpointBehavior" />
</client>
<services>
<service name="SomeCompany.SomeService">
<endpoint address="" binding="basicHttpBinding"
contract="SomeCompany.ISomeService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="IHAdapterEndpointBehavior">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine" storeName="My" findValue="123...abc" x509FindType="FindByThumbprint"/>
<serviceCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
Now to the problem. When hosting the service in Visual Studio Web Development Server or calling the remote service from local test client (.exe), the call succeeds. But when the local service is IIS-hosted (localhost or some other server IIS), I get exception:
Could not establish secure channel for SSL/TLS with authority 'https://someserver.com:8085'
with Inner Exception:
The request was aborted: Could not create SSL/TLS secure channel.
What I tried or checked so far:
correct cert store location (Local Computer, not User)
Private Key Permissions (went to MMC, found cert, right click, All Tasks, Manage Private Key, set to all permissions to Everyone)
set the IIS application user (Connect As) to the local user with administrative privileges
One more thing: the current remote server cert is issued for another hostname, so I have to override the validation programmatically. So to create a remote service object in local service, I have theese lines of code:
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = ((senderParam, certificate, chain, sslPolicyErrors) => true);
IHAdapter.IhAdapterClient ihAdapter = new IHAdapter.IhAdapterClient();
ihAdapter.SomeMethod(parameters); // the exception gets thrown here
What else could I be missing? Any ideas, pointers?

Ok, answering my own question.
Based on this link: How to give ASP.NET access to a private key in a certificate in the certificate store?
I solved my problem.
The key to my solution was this check list:
Create / Purchase certificate. Make sure it has a private key.
Import the certificate into the "Local Computer" account. Best to use Certificates MMC. Make sure to check "Allow private key to be
exported"
Based upon which, IIS 7.5 Application Pool's identity use one of the following:
IIS 7.5 Website is running under ApplicationPoolIdentity. Using Certificates MMC, added "IIS AppPool\AppPoolName" to Full Trust on certificate in "Local Computer\Personal". Replace "AppPoolName" with the name of your application pool.
IIS 7.5 Website is running under NETWORK SERVICE. Using Certificates MMC, added "NETWORK SERVICE" to Full Trust on certificate in "Local Computer\Personal".
IIS 7.5 Website is running under "MyIISUser" local computer user account. Using Certificates MMC, added "MyIISUser" (a new local computer user account) to Full Trust on certificate in "Local Computer\Personal".
I almost surely did all of those things but obviously never all together at once. Hopefully this helps someone else. Thanks anyway for all the help.

I think all such messages are due to some machine in the chain (client, proxy, server) not "liking" a certificate for some reason.
To elaborate on what twk said, if you're using self-signed certificates, or your own CA, you need to install the signing cert in the trusted authorities store on the server at least, and possibly on the proxy.
Common problems I've encountered:
The certificate on the server is not signed by an authority that the PROXY or the CLIENT trusts
The certificate on the CLIENT is not signed by an authority that the PROXY or the SERVER trusts
Oops, I forgot to export the private key when I created the cert to be installed on the client
My process does not have read permissions to the private key on the client
The client certificate is password protected and I didn't specify credentials when reading the certificate.
For more visit Could not create SSL/TLS secure channel - Could the problem be a proxy server?

Related

WCF net.tcp with signed certificate

If I have an issued SSL certificate from a trusted CA, do I still have to import the SSL certificate to the client machine when connecting to a WCF service over net.tcp?
When I was using wsdualhttpbinding I could simply connect via https. Now I switched to net.tcp and added
<bindings>
<netTcpBinding>
<binding name="InsecureTcp" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate"/>
</security>
</binding>
</netTcpBinding>
</bindings>
to the web.config file.
I can access the WSDL-file via https but when I try to connect from my client I get the following error:
Additional information: The client certificate is not provided. Specify a client certificate in ClientCredentials.
I have tried to add a custom behavior to the client config file:
<behaviors>
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="example.com" x509FindType="FindBySubjectName"
storeLocation="LocalMachine" storeName="My" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
But this only works in combination with importing the certificate to my local cert store...
I hope I get your point - you don't want to use client certificates for authentication? Then modify <security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate"/>
</security>
the clientCredentialType to one of the following:
http://msdn.microsoft.com/en-us/library/system.servicemodel.httpclientcredentialtype(v=vs.110).aspx
You can use multiple authentication mechanisms, if you want:
http://msdn.microsoft.com/de-de/library/ms731316(v=vs.110).aspx

wcf service # iis - svc reachable but wsdl link not working

i wrote a wcf service and deployed it on a local iis without any problems.
local system:
windows 7 x64
iis 7.5
now i try to deploy my service on a remote system with iis server.
i copyed the service.svc, web.config and the bin directory to a folder of the other system.
after that i added a new application to the default website and linked it to the folder containing my files. i opend the browser an tryed to reach the svc file.
file is displayed correct with both wsdl links, but whene i click the links the url changes but the site dont change. tryed this on the remote system and on local client. same beavior on both systems.
remote system:
windows server 2012 r2 x64
iis 8.5
what ive done:
added a service behavior which enables service metadata
set the httpGetEnabled=True on that service metadata behavior to allow http browsing to that metadata
added mex endpoint on my service
here my web.config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MetadateBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service
behaviorConfiguration="MetadateBehavior"
name="ServerLib.Handler.WcfReciveHandler">
<endpoint
address=""
binding="wsDualHttpBinding"
contract="Common.Contracts.IServerContract" />
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
i found a similar post here but with no answer to my problem. (WCF hosting: Can access svc file but cannot go to wsdl link).
i enabled tracing on my service but no logs are created so far. so i think the service is not running. on my local system togs are created.
i tryed to add a service reference to my client project with the url of the svc file, but this ends with an error:
There was an error downloading '\http://dev.develop.local/asd/Service.svc/_vti_bin/ListData.svc/$metadata' The request failed with HTTP status 404: Not Found. Metadata contains a reference that cannot be resolved:
followed by html document - think the file that is displayd whene i access the svc file.
ive checked my application eventlog on the remote system and noticed following error that seems to be written whene i tryed to add the service reference:
WebHost failed to process a request.
Sender Information: System.ServiceModel.Activation.HostedHttpRequestAsyncResult/24984138
Exception: System.Web.HttpException (0x80004005): There was no channel actively listening at 'https://dev.develop.local/asd/Service.svc/mex'. This is often caused by an incorrect address URI. Ensure that the address to which the message is sent matches an address on which a service is listening. ---> System.ServiceModel.EndpointNotFoundException: There was no channel actively listening at 'https://dev.develop.local/asd/Service.svc/mex'. This is often caused by an incorrect address URI. Ensure that the address to which the message is sent matches an address on which a service is listening.
at System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
i hope that someone can help.
You are missing the configuration section for the connection binding for "wsDualHttpBinding". You need to add a bindingConfiguration attribute to your endpoint element and point it to a binding configuration element.
It should look something like this (this is for wsHttpBinding not wsDualHttpBinding):
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:30:00" sendTimeout="00:30:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="524288"
messageEncoding="Text" textEncoding="utf-8"
useDefaultWebProxy="true">
<reliableSession enabled="true" ordered="true"/>
<readerQuotas maxDepth="32" maxStringContentLength="52428800" maxArrayLength="52428800"
maxBytesPerRead="52428800" maxNameTableCharCount="52428800" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="None" algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
thanks toadflakz. i finally got it.
there have been several problems. your answer helps with one (point 2) of them.
the server and website i published the service only accepted https and my service dont provide the metadata via https. solution was to add httpsGetEnabled to my serviceMetadata.
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
that was the main problem of the wsdl file that was not loaded correctly. after adding this all went fine but communication failed - recived exceptions that point to a authentication problem - > thats point 2
wsDualHttpBinding requires as toadflakz said a bindingConfiguration. at least the information that no security mode is required. my binding config looks like this.
<wsDualHttpBinding>
<binding name="wsHttpDual">
<security mode="None">
<message clientCredentialType="None" algorithmSuite="Default" />
</security>
</binding>
</wsDualHttpBinding>
after that i added a new website for my service listening to http requests on a very high port. client seems to do something but recived a timeout. i added the port to incoming connections on server firewall but it was not enough - brings me to problem 3.
my communication was blocked by the client firewall. i added the server ip clientside - working fine now.
Restarting the visual studio solved my problem, I know its weird, but for someone who has tried everything and still failed , just try restarting visual studio. Hope it helps some one.

Intranet - web to remote wcf CredentialCache.DefaultNetworkCredentials not working

I have a ASP.NET MVC intranet application hosted in IIS that added WCF service reference the WCF resides in another computer and also expect windows authentication.
In my web this code is working great:
proxy = new MyProxyClient("configurationName", "remoteAddress");
proxy.ClientCredentials.Windows.ClientCredential.UserName = "myUserName";
proxy.ClientCredentials.Windows.ClientCredential.Password = "MyPassword";
proxy.SomeMethod(); //work great
but if I want the credential not to be hardcoded like this I am using: CredentialCache.DefaultNetworkCredentials like this:
proxy = new MyProxyClient("configurationName", "remoteAddress");
proxy.ClientCredentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
proxy.SomeMethod(); //not working throw exception
the above code throw SecurityNegotiationException with message: The caller was not authenticated by the service.
and the inner exception is: The request for security token could not be satisfied because authentication failed.
How can I pass the credential of the current user to the WCF service without hardcoded user name and password?
If your organization uses regular Windows authentication (NTLM) you can't do what you want due to "one-hop" restriction: credentials passed from user's computer to your server use "one-hop" (from direct login to one external computer) and such credentials can't be used to authenticate other servers from the first one.
More information can be found using following search term:ntlm one hop,i.e. Why NTLM fails and Kerberos works.
Standard solution:
Kerberos (often requires significant effort to get approval to enable/configure)
Use some other form of authentication than Windows. Consider if OAuth is possible. Don't go basic auth.
Switch WCF service to claims based authentication.
If WCF service can it can trust caller to verify incoming credentials more approaches are possible:
Run code under particular account that signs in locally on server and have permissions to call the service. The easiest approach is what shown in your post, but storing domain passwords (or any passwords) in plain text is not secure. One can also run process account under special credentials that have access to the remote service and temporary revert impersonation after verifying user credentials.
You can also configure WCF service to require client certificate and use such certificate when calling the WCF service. This way WCF service can verify if caller is known.
In the web.config (client and server), in the <system.serviceModel> section add/modify a binding to look something like this:
<basicHttpBinding>
<binding name="MyBasicBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
And, add this to client side web.config <system.web> section:
<identity impersonate="true" />
<authentication mode="Windows" />
The two changes will make the end-user the current user of the web request which will then be sent in the WCF message.
The user can then be retrieved on the server side like this:
ServiceSecurityContext.Current.WindowsIdentity
Please make sure the following configuration is there in service web.config.
<system.serviceModel>
<services>
<service name="MyWcf.Service1" behaviorConfiguration="MySvcBehavior">
<endpoint address="" binding="wsHttpBinding" contract="MyWcf.IService1" bindingConfiguration="MyWsHttpBinding"></endpoint>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="MyWsHttpBinding">
<security mode="Message">
<transport clientCredentialType="Windows"/>
</security>
</binding>
</wsHttpBinding>
<basicHttpBinding>
<binding name="MyBasicBinding">
<security mode="Message">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="MySvcBehavior">
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
And in your client configuration file following should be there.
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1">
<security>
<transport clientCredentialType="Windows" />
<message clientCredentialType="Windows" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost/MyWcf/Service1.svc" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IService1" contract="MyWCFService.IService1"
name="WSHttpBinding_IService1">
</endpoint>
</client>
</system.serviceModel>
I tried by passing System.Net.CredentialCache.DefaultNetworkCredentials using above configuration and working fine for me. If it's not working for you then put debug point on line which passing credential and watch that System.Net.CredentialCache.DefaultNetworkCredentials Domain, UserName & Password values are blank or not. If Blank then it should work.
I assume that if you're looking within your CredentialCache.DefaultNetworkCredentials attribut you won't see the credential information.
From Microsoft website : The authentication informations return by the DefaultNetworkCredentials property will be only available for NTLM, Negotiate or Kerberos authentication.
To get your credential you need to implement this authentication.
You can use it by using impersonation wihtin your intranet application.
Impersonation allow your intranet application to be executed by the user of this one.
More information here :http://technet.microsoft.com/en-us/library/cc961980.aspx

Securing WCF using Certificates for Transport Mode

I am using WCF service with Transport mode. I need to make this service secure using Certificates.
I followed the steps given in the below link and got it working in my local
http://www.codeproject.com/Articles/36683/simple-steps-to-enable-X-certificates-on-WCF
The issues i am facing now are:-
In the above mentioned steps, i had the service running locally as well as my client application that calls the service, running locally. Now for my QA deployment i have the service hosted on one server and my Client application on another server.
I dont have visual studio commant prompt in both these servers to create the certificates using makecert.
My service will be in https.
I will be using some third party to get the certificates for my production, but now i am not able to figure out how to get this working for my QA environment.
I have tried creating a Certificate to Act as Root Certificate Authority and installed it in both the servers. In this approach also i am stuck as to how will i create my client and server certificates under this root authority.
Kindly let me know how can i get this working.
Thanks in advance.
Finally got it working. I had to create certificates using makecert in my local and then had to import them in my QA environment (will be using certificates from a trusted third party for my production environment). My requirement was to make my service secure and not allow unauthorised clients to consume it.
My service web-config.
Changes in the binding
<bindings>
<basicHttpBinding>
<binding name="basicHttpEndpointBinding">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" />
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings
Changes in behavior.
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck" />
</clientCertificate>
</serviceCredentials>
My Client web-config.
Changes in binding
<binding name="">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" />
<message clientCredentialType="Certificate" />
</security>
</binding>
Add behavior
<behaviors>
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="RootCATest"
x509FindType="FindByIssuerName"
storeLocation="LocalMachine"
storeName="My"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
Attach this behavior to the endpoint
behaviorConfiguration="CustomBehavior"

WCF with transport security via certificates

I want to secure my WCF service using client certificates, i.e. only client certificates from a specific root CA should be allowed to call my service.
For testing purposes I've created a single client certificate without a CA first. I registered the client certificate at the server's certificate store (under current user -> trusted people).
Within VS2013 I've enabled SSL on the WCF service project in order to have an HTTPS endpoint. I've adapted the following Web.config file of the service as follows:
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</clientCertificate>
...
</serviceCredentials>
<wsHttpBinding>
<binding name="wsHttpEndpointBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
Furthermore I've adapted the App.config file of my client application as follows:
<clientCredentials>
<clientCertificate findValue="Client" x509FindType="FindBySubjectName" storeLocation="CurrentUser" storeName="TrustedPeople" />
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
However, this does not work, I get the following exception message:
An error occurred while making the HTTP request to https://localhost:44300/Service1.svc. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.
If I switch to message security (instead of transport security) and switch to the HTTP protocol everything seems to work just fine. So I guess I've missed some HTTPS-enabling step?! How to make transport security work?
Your certificate is not configured properly.Follow below steps
Copy the thumbprint of the certificate and run below command from command prompt in elevated mode
netsh http add sslcert [Ipaddress:port] certhash=[thumbprint of certifcate]
appid={unique id for application.you can use GUID for this]
[Ipaddress:port] Ipaddress and port
[thumbprint of certifcate]: thumbprint of certificate without spaces
appid :unique id you can use guid
How to generate GUID from command prompt
Open visual studio command prompt and run below command
c:>uuidgen

Categories

Resources