I have a WCF service that uses net.TCP binding, and this service can be consumed inside the LAN or through internet.
I have read that net.TCP by default use transport level security, but this security is point to point, I think that if I use my client out my LAN, through internet and the communication use many points, perhaps some of this points does not transmit the message without security. Is this correct?
So if I need message security too? I can use a ssl certificate x509 to encrypt the each message and that only can be decrypt by my service that has the private key?
Is there some document that explain how to use certificates with net.TCP binding? Can I use open ssl to create my certificate and use it with WCF?
Thanks.
First of all, both approaches are secure and will suffice for 90% of cases. Transport security secures your channel of communication, but doesn't encrypt your actual message. Message security encrypts your actual message, so servers that the message is passed through can not see the message contents and will need a private key to decrypt your messages. So one could argue message security is safer, at least its more suitable for internet communication. Some good links on WCF security:
Message Security in WCF and patterns & practices Improving Web Services Security Guide
netTcpBinding uses Transport security by default, but that doesn't mean you can't use Message security with it. Transport security has less computation overhead than Message security (where each message is encrypted) thus it has better performance. One caveat of using netTcpBinding over the internet is that it may not be guaranteed to work at all times (in the past I have successfully set up netTcpBinding over the internet though) since it uses some ports for message transmission that are not always guaranteed to be left open by network routers and firewalls (over the internet, your messages will be going through many routers and firewalls.) For internet communication, consider one of the HTTP bindings such as basicHttpBinding or wsHttpBinding which also supports message security.
You can use Message security like in other bindings:
<netTcpBinding>
<binding name="securedBinding">
<security mode="Message">
</security>
</binding>
</netTcpBinding>
and then set the bindingConfiguration on your endpoints to securedBinding.
And on the machine hosting your service (the server):
<behavoirs>
<serviceBehavior>
<behavior name="securityBehaviour">
<serviceCredentials>
<serviceCertificate
findValue="serviceCert"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavoir>
</serviceBehavior>
</behavoirs>
<services>
<service name="Service1" behaviorConfiguration="securityBehaviour">
<endpoint address="" binding="netTcpBinding" contract="IService1" bindingConfiguration="securedBinding">
</endpoint>
</service>
</services>
If you have limited clients and you know who they are, you can use self signed certificates. However if you want optimal security with many unknown clients consuming your service you're best off buying one from a known CA. You then need to install the server certificates on the server machine. Here is an article on how to secure your services with certificates, the blog also has some other useful WCF security articles that you may want to read.
Related
I'm working on a WCF service that is to be consumed by a client that is not developed by me and also it's not .NET (possibly Java).
In any case, the service should support mutual SSL authentication, where both the service and the client authenticate with certificates X.509 certs at the transport layer. The certificates have been exchanged between parties at a prior moment.
My problem is that I cannot seem to get the right WCF configuration such that client certificate authentication works correctly. What I expect is that, as part of the TLS handshake, the server also includes a Certificate Request, as seen below:
Following this, the client should answer with a `Certificate Verify' among other things:
The (latest) service configuration is this one. I'm using a custom binding, with authentication mode set to MutualSslNegotiated.
<bindings>
<customBinding>
<binding name="CarShareSecureHttpBindingCustom">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="MutualSslNegotiated"/>
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
...
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" httpHelpPageEnabled="false" />
<serviceCredentials>
<serviceCertificate findValue="..." storeLocation="LocalMachine" x509FindType="FindByIssuerName" storeName="My" />
<clientCertificate>
<certificate findValue="..." storeName="My" storeLocation="LocalMachine" x509FindType="FindByIssuerName"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
The Server Hello part of the handshake looks like this for all service configurations I have tried, with no CertificateRequest.
Other things I should mention:
The service is self hosted and listening on a non-default port (not 443). The server SSL certificate has been bound to this port.
I have also tried a basicHttpBinding and a wsHttpBidning with security mode set to Transport and client authentication set to Certificate, with no results (same results actually).
Any ideas would be appreciated.
OK, after a few more tries I figured it out. Posting this in case others run into the same issue.
I should continue by mentioning that this behavior really needs to be mentioned somewhere on MSDN, in a location that is really visible for anyone looking for WCF security information and not buried deep in some tool's documentation.
The platforms where I've been able to reproduce and fix this: Windows 8.1 x64 and Windows Server 2008 R2 Standard.
As I mentioned, my issue was that I could not configure WCF security such that the service would require client certificates. A common confusion that I noticed while looking for a solution is that many people believe that the client can send the certificate if it has it, unchallenged. This is, of course, not the case - the server needs to ask for it first and, moreover, specify which CAs are allowed through a CertificateRequest reply.
To summarize, my situation was:
Service is self-hosted.
Service runs on HTTPS, on a non standard port (not 443 but 9000).
This meant that I had to create an SSL certificate binding for port 9000 by using netsh.exe http add sslcert. Well, the binding had been created but there was a catch. I only found the issue after running netsh http show sslcert just to check on my binding:
IP:port : 0.0.0.0:9000
Certificate Hash : ...
Application ID : ...
Certificate Store Name : MY
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
-->Negotiate Client Certificate : Disabled
The culprit was the last property of the binding, "Negotiate Client Certificate", documented here. Apparently, by default, this property is disabled. You need to enable it explicitly while creating the binding.
Recreating binding with the statement below solved the issue:
netsh.exe http add sslcert ipport=0.0.0.0:9000 certhash=... appid=... certstorename=MY verifyclientcertrevocation=Enable VerifyRevocationWithCachedClientCertOnly=Disable UsageCheck=Enable clientcertnegotiation=Enable
Prior to checking the bindings I tried hosting a simple WCF service in IIS and enable client certificate authentication from there. It was very curious to see that although there was no CertificateRequest issued by IIS, it still failed with a 403.7. Even IIS didn't create the binding with the appropriate parameters.
Anyway, now it works and this is how you can fix it.
Not to forget, the service configuration changed as well (the binding security) in order to allow certificate negotiation:
<customBinding>
<binding name="CustomHttpBindingCustom" receiveTimeout="01:00:00">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="SecureConversation" requireSecurityContextCancellation="true">
<secureConversationBootstrap allowInsecureTransport="false" authenticationMode="MutualSslNegotiated" requireSecurityContextCancellation="true"></secureConversationBootstrap>
</security>
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
I had the same issue when my bosses were questioning why was our IIS hosted WCF service which implemented "2 way SSL" (mutual certificate authentication) not observed to be sending "Certificate Request" in the TLS handshake. After some investigation, we finally found that the certificate port binding configuration for Negotiate Client Certificate is disabled.
Get the current binding information by running the below.
netsh http show sslcert
Get the certificate hash and the application GUID from the first record (or the relevant SSL port), then run the following netsh command using an administrator console on the IIS server.
netsh http add sslcert ipport=0.0.0.0:443 certhash=xxxx appid={xxxxx} clientcertnegotiation=enable
Note that if an existing binding already exists for the IIS address and port, the following error will be reported.
SSL Certificate add failed, Error: 183 Cannot create a file when that file already exists.
Run the delete command to remove the existing binding before retrying to add it back again.
netsh http delete sslcert ipport=0.0.0.0:443
After the reconfiguration, the observed Wireshark TLS handshake became as expected. However, in my opinion, this setting doesn't matter in the end as the client certification is used for authentication whether during the initial handshake or afterwards within the encrypted exchange and 2 way SSL is achieved.
WCF is extremely extensible and has a lot of ready-to-use features, however I continue struggling with some topics and the more documentation I read, the more I get confused.
I hope to get some answers from the community. Feedback on any assumption or question is extremely welcome.
For the record: to really accept a single answer I should divide this post in multiple questions but it would lead to even more confusion.
I am pretty sure there are some real WCF experts online who can answer the few questions in this document all at once so I can accept a single answer as the real deal to setup clientcertificate authentication using IIS the right way.
Let me sketch the situation and partner request:
1: The partner requirement and the question to use a client certificate.
Partner X needs to call an API on my backend and they have the clear requirement to use Clientcertificate authentication.
They created the clientcertificate and provided us the certificate with only the public key since it seems only logic they keep the private key actually private and in their own system(s).
The certificate was imported on the local computer account and looking at the certification path this is valid. All intermediate certification authorities and in the end the root certification authority are trusted.
2: Our WCF serverside configuration
I have a serviceBehavior configured as such:
<behavior name="ClientCertificateBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceCredentials>
<serviceCertificate findValue="<serialnumber here>" x509FindType="FindBySerialNumber" />
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" />
</clientCertificate>
</serviceCredentials>
</behavior>
I guess I made a first mistake here and should use ChainTrust to actually validate the certificate using its certification path. What do you think?
The service is configured as such:
<service behaviorConfiguration="ClientCertificateBehavior" name="<Full service namespace and servicename>">
<endpoint binding="basicHttpBinding" bindingConfiguration="Soap11CertificateBasicHttpBinding"
contract="<The interface>"></endpoint>
</service>
The binding looks like this:
It is a basicHttpBinding to force SOAP1.1 (according to the partner's specifications).
<binding name="Soap11CertificateBasicHttpBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
3: Hosting the WCF service in IIS and the IIS configuration
We host our WCF services in IIS7.
We configured the folder in which the services reside to require SSL and to accept Client certificates.
Authentication-wise anonymous authentication is enabled.
The thing is that communication from the partner works and we were confident that everything was OK, however toggling the IIS-setting to 'require' client certificate shows us that all of a sudden it is no longer possible to successfully call our service.
Am I correct to assume that following things are not done correctly:
The serviceCerticate in the serviceBehavior is not really necessary. This is a setting used by the client. Or is it necessary to provide this certificate information for the service endpoint to match the certificate that's being send by the client?
For clientcertificate authentication to really work in IIS the certificate needs to be mapped to a user. This user should be granted permissions on the folder containing the services and all authentication mechanisms (anonymous, windows,...) should be disabled.
This way IIS will handle the actual handshake and validate the servicecommunication.
Or is it more a matter of extra security mapping the certificate to a user?
By setting 'Accept' on IIS we bypass the actual certificate validation between client and server.
All authentication mechanisms like 'anonymous' and 'windows' have to be disabled on IIS for the folder which holds the services.
In your scenario, you don't need to configure certificates in WCF, IIS handles those for you. You can clear the entire <serviceCredentials> block, because:
The <serviceCertificate> of <serviceCredentials> specifies an X.509 certificate that will be used to authenticate the service to clients using Message security mode, which you do not use, and the <clientCertificate> of <serviceCredentials> defines an X.509 certificate used to sign and encrypt messages to a client form a service in a duplex communication pattern.
See here how to map client certificates to user accounts.
The scenario is this: there are 2 WCF Web Services, one a client (WCFClient), one a server (WCFServer), deployed on different machines. I needed certificate communication between the two of them.
On the server WCF I have set the binding to use certificates as client credential type.
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
Also, in the behaviour section, among other settings, I have
<serviceBehaviors>
<behavior name="Server.ServiceBehavior">
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</clientCertificate>
<serviceCertificate findValue="Server"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
On the client WCF Service I added this endpoint behaviour
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="Client"
x509FindType="FindBySubjectName"
storeLocation="LocalMachine"
storeName="TrustedPeople" />
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
When I wanted to test my services, I had an error message:
The service certificate is not provided for target 'http://blablabla...'. Specify a service certificate in ClientCredentials.
So I started checking things out on the Internet. After trying many things, the only thing that actually worked is adding this on my client:
<serviceCertificate>
<defaultCertificate findValue="Server"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
As you might think, yes, this means I need the Server certificate on my client machine. Which is clearly a very bad thing.
It works for my testing purposes, but it is an unacceptable for deployment.
I would want to understand what really could cause that error message and what the solution may be.
Later edit: In this project the client must not have the server certificate (even without having the private key). This is the specification of the system and it's quite difficult (in bureaucracy terms) to go beyond this.
There will be multiple clients, each with the client WCF service running, and each should know nothing more that their own certificate. The server will know the server certificate and all the clients certificate.
Looking here it reads,
When considering authentication, you may be used to thinking primarily
of the client identity. However, in the context of WCF, authentication
typically refers to mutual authentication. Mutual authentication not
only allows positive identification of the clients, but also allows
clients to positively identify the WCF services to which they are
connected. Mutual authentication is especially important for
Internet-facing WCF services, because an attacker may be able to spoof
the WCF service and hijack the client’s calls in order to reveal
sensitive data.
The service credentials to be used depend largely on the client
authentication scheme you choose. Typically, if you are using
non-Windows client authentication such as username or certificate
authentication, a service certificate is used for both service
authentication and message protection. If you are using Windows client
authentication, the Windows credentials of the process identity can be
used for both service authentication and message protection.
It looks to me that you do need the server certificate on the client machine, and that this is a good thing, not a bad thing. Note that you do not need (and should not put) the server's private key on the client machine. The private key is not contained in a certificate -- only the public key is.
Having the server certificate on the client machine means only having the server's public key on the client machine. The benefit is that the client now knows that it is talking to the real server.
I'm not familiar with WCF services, but this seems fine as far as the use of certificates.
why is it bad to have the service certificate on the client machine? it is only the public portion of it, not the private key.
if you use wshttpbinding you can set negotiateServiceCredential=true in which case the client will get the server cert dynamically. The price is a little bit of performance hit, and this endpoint will not be interoperable to non .net clients.
I actually forgot about this question, but at that time I have found the solution.
My actual problem was that I was using a basicHttpBinding for the communication I wanted to secure. basicHttpBinding implies ussing that serviceCredential part.
http://msdn.microsoft.com/en-us/library/ms731338(v=vs.85).aspx
Because of the system requirements I had, I changed the binding to wsHttpBinding. Now I don't need to put the server certificate on the client machine.
TL;DR version is at the bottom.
I have constructed three WCF web services -- one that's using .NET 4 and two that's using .NET 3.5 -- that is consumed by an Android client. The Android client performs the calls using ksoap2-android. When the services were complete and the client could make all the calls and get all the data from the services, we decided to activate HTTPS communication for the web services. They are hosted on a server running IIS.
I'm not alone on this task. I work full-time with it, mainly the Android client. I have two coworkers, both of which have a lot of other responsibilities. The first is mostly involved in the services and the second is mostly involved in the server.
I've read a lot of guides, blogs and articles on the Internet on how to enable HTTPS for a WCF web service, but still I haven't been able to completely resolve this. For the Android client to be able to consume the client, we are limited to using the basicHttpBinding, since the wsHttpBinding contains some security details that are not supported by Android, or something. I'm not sure, but I read it some forum somewhere. If I'm wrong, I'd happily be corrected!
Okay, so I'll give a short account of what I've done so far:
I've enabled transport security, this is how the binding(s) looks:
<bindings>
<basicHttpBinding>
<binding name="basicHttp" closeTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
</bindings>
I've enabled meta data publishing over HTTPS using this line of code:
<serviceMetadata httpsGetEnabled="true" />
and this:
<endpoint address="" binding="mexHttpsBinding" contract="IMetadataExchange" />
Also, my second coworker has installed a trusted certificate (a real certificate from a CA) on the IIS server and added a https binding on the server.
Okay, so far, so good. Now we could connect to the services in the browser.
When we had done this it was possible to visit the service in the browser in a secure, encrypted, manner.
The problem is that it isn't possible to make a call to the service, neither in Android nor WCF Test Client. If I call the service on the https address, I get a 404 in response. WCF Test Client returns this:
There was no endpoint listening at https://[my service address] that could accept the message. This is often caused by an incorrect address or SOAP action.
To be able to debug this, I'm using Wireshark to intercept the messages and see what really happens. I've found out that after the set up procedures (handshake, establishing trust etc.) the client sends the header and get a 101 Continue in response, after which it POSTs the body. This should be normal behavior. But then the service returns 404 Not Found. Wireshark says that the full request URI is the http address. I've configured the server to use the https address, so why does it make a call to the http address?
I've tried setting the address and listenUri attribute of the endpoint to https and http respectively and the other way around. If I do that, the server answers the request with 405 Method Not Allowed.
Is there any way to solve this? What am I missing?
What am I missing?
TL;DR version below
I'm hosting three WCF SOAP web services with IIS, one using .NET 4 and the others using .NET 3.5. I'm trying to make a call from the WCF Test Client over HTTPS. I've enabled transport security, meta data publishing over HTTPS, installed a trusted certificate and added a https binding on the server.
When I try to make a call from WCF Test Client it says that there was no endpoint listening at the address. Wireshark tells me it makes the call to the http version of the service (i.e. "http://[my address]" instead of "https://[my address]"), although it is configured to call the https address. The service returns 404 Not Found. If I set the address of the service to https and listenUri to http, I get 405 Method Not Allowed. If I do it the other way around it says that it cannot be activated. What am I missing?
I solved this by removing the endpoint and using the <protocolMapping> tag, like this:
<protocolMapping>
<add scheme="https" binding="basicHttpBinding" bindingConfiguration="basicHttps" />
</protocolMapping>
I keep getting the following error "Could not find a base address that matches scheme https for the endpoint with binding WebHttpBinding. Registered base address schemes are [http]." This started because I went to Basic Transport Authentication by adding:
<webHttpBinding>
<binding name="secureBasic">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</webHttpBinding>
After googling the common fix seemed to be the following code, but I had no success with it:
<baseAddressPrefixFilters>
<add prefix="http://mywebsiteurl"/>
</baseAddressPrefixFilters>
Still, nothing works. All I want is to use basic http authentication on a non-https connection. I have configured absolutely nothing and it appears by default WCF wants to force a HTTPS connetion. Anyone run into this?
If you tell WCF to use transport security mode, you must use a transport protocol that supports secure communication. HTTP doesn't support secure communication because it's a plaintext protocol (anyone that intercepts your communication can simply read what is being transmitted). HTTPS does support secure communication so your only option for a webHttpBinding with transport security is to use HTTPS (which you can configure in IIS).
Here is a blog post describing your error and how to solve it using TransportCredentialOnly. It's about basicHttpBinding but the same holds for your webHttpBinding.
If you want only transport level authentication (= Basic authentication) but you don't want transport level secure communication (= HTTPS) you have to set security mode to TransportCredentialOnly.