WCF with transport security via certificates - c#

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

Related

httpBasicBinding with authentication and no message or transport security

I've got a small WCF service in intranet and I need to implement authentication in it. This service communicates with different Java clients over http (uses basicHttpBinding). I've tried to configure binding like so
<basicHttpBinding>
<binding>
<security mode="None">
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
And added service behaviour
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="LocalTasks.Services.Validators.IntegrationUserNameValidator,LocalTasks.Services"/>
</serviceCredentials>
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
But calls to this service do not hit my custom UserNamePassword validator.
I've also tried the same variant but with security mode set to Message and added a certificate. In this case validator worked fine.
How to configure service to authenticate users via basicHttpBinding without any message or transport security?
You cannot do that. You need either message or transport security, in order to have the credentials encrypted when they travel from the client to the server.
You really should use Message or Transport security.
You could also use TransportCredentialOnly, which will not require an SSL certificate, but that is recommended for testing only.
<basicHttpBinding>
<binding name="Authentication" >
<security mode="TransportCredentialOnly" >
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>

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

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"

Error accessing web service through SSL from IIS-hosted application

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?

Categories

Resources