connecting to wcf service hosted on domain from a client that is not on the domain - wcf

I would like an example or explanation of how to connect a client to a wcf service when the client is not on the domain.
I imagine there is a way to specify domain credentials with the client and the wcf service could talk to the authority (dc) to see if the client is secure.
I followed the examples on the msdn and can connect to see the metadata (methods available) but when using wshttpbinding I get "An unsecured or incorrectly secured fault was received from the other party".
Thanks in advance!

By default, wsHttpBinding will use Windows credentials - this only works if both your service and your calling client are member of the same domain (or member of domains with a mutual trust relationship).
If you want to authenticate using username/password, there's a number of things you need to do:
the service needs a certificate to authenticate itself to the caller, and to provide an encryption mechanism for the exchange of username/passwords and messages. So you will need to create a security certificate and install it on the server machine, and configure it:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="Internet">
<serviceCredentials
findValue="MyServiceCertificate"
storeLocation="LocalMachine"
storeName="My"
X509FindType="FindBySubjectName" />
</behavior>
<serviceBehaviors>
<behaviors>
<services>
<service name="MyService" behaviorConfiguration="Internet">
......
</service>
</services>
</system.serviceModel>
the client needs to set up a config that defines wsHttpBinding with message security, and username/password client credentials
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="UserNameWS">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
<wsHttpBinding>
<bindings>
<client>
<endpoint name="Default"
address="........."
binding="wsHttpBinding" bindingConfiguration="UserNameWS"
contract="........." />
</client>
</system.serviceModel>
on the server side, you need to set up a mechanism to authenticate those username/passwords - typically, the easiest way is to use the ASP.NET membership system
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="Internet">
<userNameAuthentication
userNamePasswordValidationMode="MembershipProvider" />
<serviceCredentials
.....
</system.serviceModel>
before each call from the client, you need to set the username/password on your client-side proxy (this is one of the few things you cannot do in config - works only in code).
proxy.ClientCredentials.UserName.UserName = "YourUserName";
proxy.ClientCredentials.UserName.Password = "Top$Secret";
Read all about WCF security at the WCF Security Guidance site on Codeplex.

The error message "An unsecured or incorrectly secured fault was received from the other party" is a rather misleading one. A common cause is a difference in the bindings configuration between the client and the server. Check the system.serviceModel section of the web.config at the service side, and modify your client settings to match.

The reason why you can access metadata and cannot call service is that you are using WsHttpBinding probably with default configuration. It uses message security wich is involved only for service usage - not service metadata. It uses Windows credentials and Windows security to encrypt and sign message. Because of Windows security it works only when both client and server are on the same domain.
Your client is not part of domain - you can send windows credentials either with message security or transport security. In case of message security you will have to use clientCredentialType="UserName", default password validator and you will have to configure X509 certificate in service behavior to support encryption and signing. In case of transport security will either use HTTPS (X509 certificate configured in http.sys/IIS) or TransportCredentialOnly mode which will send windows user name and password as a plain text over HTTP (this is bad solution). In case of transport security set clientCredentialType="Basic".

Related

intermittent error could not establish secure channel for ssl/tls

My WPF(desktop) application is calling two type of services - one WCF service which is hosted in our internal environment and other one is a 3rd party web service.
My application is using .Net framework 4.0. Due to some recent windows update applied on organization, my 3rd party web service stop working and I start getting this error
"System.ServiceModel.Security.SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'abc.net'. ---> System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel. "
In order to resolve that I had added this line before calling the 3rd party web service.
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
And, it start working perfectly. However, it impacted my internally hosted WCF service. I start getting the same error for my WCF service. Strange part is that, i am not getting is error every time, it occurs all of a sudden and when we restart the application, WCF service call again start working fine.
Error:
"System.ServiceModel.Security.SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'abc.net'. ---> System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel."
Some of WCF service config details :
<bindings>
<wsHttpBinding>
<binding maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
----
<serviceBehaviors>
<behavior name="WcfServerSyncService.LocalDataCacheSyncServiceBehavior">
<serviceMetadata httpsGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="True" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfServerSyncService.CustomValidator,WcfServerSyncService" />
</serviceCredentials>
</behavior>
<serviceBehaviors>
And, one more thing only specific set of users are getting this error and not all. But, for these users this work sometime and sometime not.
Any suggestion ?
TLS1.2 requires the support of the OS and the SDK, therefore, I suggest you install the DotNet framework and target high version of DotNet Runtime.
https://learn.microsoft.com/en-us/dotnet/framework/network-programming/tls
Based on the best practice of transport layer security, we had better not specify the TLSversion while calling the WCF service protected by an SSL certificate. OS will decide on the TLS version.
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls13 | System.Net.SecurityProtocolType.Tls;
In addition, WCF server-side hosted in the WPF shall not produce a communication error, which only occurs on the client-side. The only error might be the account running WPF doesn't have permission to access the certificate. The transport layer security requires binding a certificate to the service port that is used to publish service.
At last, since the WCF server authenticates the client with custom validation, this will inevitably lead to certain errors.
Feel free to let me know if there is anything I can help with.

Configure a WCF service to authenticate the client by using X509 certificates but without server credentials

I have developed a ASP.NET MVC website that also hosts a WCF 4 service; and I have created a .NET Windows application that complements the website and interacts with it by consuming that web service via the internet. Both programs were created using .NET 4 in Visual Studio 2010. The binding used with the WCF service is a WsHttpBinding.
My only security requirement is that the WCF service is not consumed by some unknown party, as that would contaminate the information in my website's database. I have no need for any privacy: I don't care if some third party gets to see the messages I am sending to the web service (i.e., no need for encryption)
Considering that single requirement, and as far as my knowledge about security in web services goes, the best way to implement that security scenario would be for the client to sign each SOAP message with a X509 Certificate, and have the web service trust any message signed with a certificate present in its "trusted people" store. Since certificates use Public/Private key pairs, even if the message is not encrypted it cannot be reproduced by an unauthorized third party without the private key used to sign the message. This would also assure me message integrity.
Considering that decision, I have created a test certificate using makecert.exe. I installed the version with both the private and public key in the client machine (the one with the windows application), in the "Current User" "My" certificate store. Then, I exported the certificate (without the private key), and installed it in "Trusted People" of "Local Machine" certificate store of the web server.
My (unsuccessful) attempt to configure that scenario so far goes like this:
Binding is configured to use Message security, where the Message expects client credentials of the certificate type. Also, the server is configured to use PeerTrust instead of ChainTrust, since the test certificate is not emitted by a trusted CA:
<bindings>
<wsHttpBinding>
<binding>
<security mode="Message">
<transport clientCredentialType="None" proxyCredentialType="None" />
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="Standard">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode = "PeerTrust" trustedStoreLocation="LocalMachine"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
The configuration on the client windows application sets the certificate as its credential. The binding configuration is identical to the one in the server.
<behaviors>
<endpointBehaviors>
<behavior name="CertificateCredential">
<clientCredentials>
<clientCertificate findValue="ErrEye"
storeLocation="CurrentUser"
storeName="My"
x509FindType="FindBySubjectName" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
The problem is that with this configuration, when I try to check if the Web Service host was started successfully, I get the following error:
The service certificate is not provided. Specify a service certificate in ServiceCredentials.
My problem is, I don't WANT to specify a "service certificate". As far as I understand, the service certificate would allow the client to authenticate the service, but I don't need that; I just want the service to authenticate the client and that's it. I do understand this means that if someone impersonates the web server (by tampering the DNS settings of my client computer, for example) it would receive the messages from my client and the client would not know it is not communicating with its intended recipient: I don't mind if that happens.
In summary, my question is: How do I configure in WCF the security scenario I've described?
Thanks in advance for your help.
As long as its .net to .net (and remains so) - add this
negotiateServiceCredential=true
to:
<message clientCredentialType="Certificate" />
But beware, you will lose interoperability with non .net clients.

REST WCF Service hosting(https) in Windows Service on Win2003

I am self hosting a REST WCF service in Windows service. The service exposes a method which returns a boolean and I am able to get the REST webservice to work on WinXp. I also generated a development certificate using makecert and assigned it to the port(1443) that the service listens on. Https also works well on WinXp. But When I use the same in Win2003, and type the URL in IE, it says "The page cannot be displayed". There were no errors in hosting the service, the service listens on the port(the ServiceHost.Open didnt have any errors and its successful). Is there an settings that has to be done in Win2003 for this? Win2003 is the production environment and https has to work on that.
Here is the config file I used,
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="httpBinding">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="spectrumServiceBehavior" name="MyApp.TestService">
<host>
<baseAddresses>
<add baseAddress="https://localhost:1443/" />
</baseAddresses>
</host>
<endpoint address=""
binding="webHttpBinding"
bindingConfiguration ="httpBinding"
contract="MyApp.ITestService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="spectrumServiceBehavior">
<!-- 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="true" />
<serviceMetadata httpsGetEnabled ="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
Any ideas?
I've experienced the same problem. WinXP development environment working with HTTPS, certificate created locally and assigned to a custom port with httpcfg.
Production environment on Windows 2003 with the imported certificate and the same httpcfg configuration giving "interrupted connection" while trying to obtain the WSDL of the service.
Looking at the Windows log I finally found this error:
Event Source: Schannel
A fatal error occurred when attempting to access the SSL server credential private key. The error code returned from the cryptographic module is 0x80090016.
My problem was that I imported the certificate in the user-account store, and then copied in the local store. In this way the private-key is left behind... http://support.microsoft.com/kb/939616 (without even a warning!!!)
despite, opening the certificate in the new location, shows the presence of a private-key!
I expect that on Windows 2003 you must allow application to listen on port (unless it runs as admin) and you must assign SSL certificate to the port - both is performed by httpcfg.exe. Also check that there is no firewall blocking the communication on the port.

Securing WCF hosted inside public MVC2 App

I've got a WCF service that is to be called by an application hosted on the web server (for the short-medium term, we'll only need a single web server so disregard scalability issues)
The web server serves a public website. at example.com
The WCF service exposes calls which amongst other things run jobs and provide certain admin functionality not supported by the web model eg long running database operations.
The WCF service has to be hosted inside the web site as it uses compatibility mode to take advantage of the Asp.Net http(s) pipeline - specifically, the service can generate emails and the emails are templated using MVC. One side-effect of this is that the call has to use the publicly visible hostname eg https://example.com/JobService.svc so that links in emails point to example.com as opposed to localhost or similar.
Obviously, I don't want the general public to be able to kick off jobs/admin tasks so I want to secure the WCF service.
I can only use https as opposed to net.tcp or similar for the binding thanks to relying on the Asp.net http pipeline.
I have to bind to the publicly accessible IP address to be able to use the proper hostname (unless someone knows a way around this?)
I can't use kerberos/NTLM as the server isn't on a domain (and NTLM is weak anyway)
I can't use certificates as it complains:
The SSL settings for the service 'SslRequireCert' does not match those of the IIS 'None'.
NB: I don't quite understand this as the website itself is only served via https. http simply returns a redirect to the same page via https.
(An interesting issue I'm having is that although the mex is served via https, the URLs inside the WSDL use http. I'm assuming this is a side-effect of not being able to set up TLS properly on my service so it thinks it's http even though it also responds on https)
So, I'm running out of ideas for how to secure my service. I could, of course, from within the service itself examine the request and determine if it comes from an IP used by the current server - but this feels very nasty and I'm effectively ignoring the work of experts and trying to put something in its place - Not a very good place to start.
Can anyone suggest a way to limit access to this service to processes on the local machine?
I've attached my current config below. (This is currently giving me the certificate error mentioned above)
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="WebJobServiceHTTPBinding" openTimeout="00:10:00"
sendTimeout="00:10:00">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"
aspNetCompatibilityEnabled="true">
<serviceActivations>
<add relativeAddress="WebJob.svc"
service="MyApp.WebJobService"
factory="MyApp.WCFDIServiceHostFactory" />
</serviceActivations>
</serviceHostingEnvironment>
<services>
<service behaviorConfiguration="WebJobServiceBehavior" name="MyApp.WebJobService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="WebJobServiceHTTPBinding"
name="HTTPEndpoint" contract="MyApp.JobService.Common.IWebJobService" />
</service>
</services>
<standardEndpoints>
<mexEndpoint>
<standardEndpoint name="WebJobServiceMex" />
</mexEndpoint>
</standardEndpoints>
<behaviors>
<serviceBehaviors>
<behavior name="WebJobServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<serviceCertificate findValue="[Thumbprint of x509 cert used by website for SSL]"
storeName="Root" x509FindType="FindByThumbprint" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
"Can anyone suggest a way to limit access to this service to processes on the local machine?"
Run your service in a different web site in IIS, if you're not already.
You could bind your service in IIS to the internal network IP address which would allow internal LAN clients to access the service but not external clients.
Another binding option is to bind to a port that is not open on your firewall in order to allow access from internal clients only. Even better, bind to a port that is not open on your firewall, and bind to the internal LAN IP.
You could also try binding to IP address 127.0.0.1.
In the end, I was forced to implement my own Authentication system. This was relatively simple as authenticatio implied authorization - ie no permission levels. That said, I'm still unhappy at the solution and will change it if another option presents itself.

How to configure a WCF service to only accept a single client identified by a x509 certificate

I have a WCF client/service app that relies on secure communication between two machines and I want to use use x509 certificates installed in the certificate store to identify the server and client to each other. I do this by configuring the binding as <security authenticationMode="MutualCertificate"/>. There is only client machine.
The server has a certificate issued to server.mydomain.com installed in the Local Computer/Personal store and the client has a certificate issued to client.mydomain.com installed in the same place. In addition to this the server has the client's public certificate in Local Computer/Trusted People and the client has the server's public certificate in Local Computer/Trusted People.
Finally the client has been configured to check the server's certificate. I did this using the system.servicemodel/behaviors/endpointBehaviors/clientCredentials/serviceCertificate/defaultCertificate element in the config file.
So far so good, this all works. My problem is that I want to specify in the server's config file that only clients that identify themselves with the client.mydomain.com certificate from the Trusted People certificate store are allowed to connect.
The correct information is available on the server using the ServiceSecurityContext, but I am looking for a way to specify in app.config that WCF should do this check instead of my having to check the security context from code.
Is that possible? Any hints would be appreciated.
By the way, my server's config file looks like this so far:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="MyServer.Server" behaviorConfiguration="CertificateBehavior">
<endpoint contract="Contracts.IMyService" binding="customBinding" bindingConfiguration="SecureConfig">
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/SecureWcf"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="CertificateBehavior">
<serviceCredentials>
<serviceCertificate storeLocation="LocalMachine" x509FindType="FindBySubjectName" findValue="server.mydomain.com"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="SecureConfig">
<security authenticationMode="MutualCertificate"/>
<httpTransport/>
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>
There doesn't appear to be a way to do what I want using web.config.
I ended up adding a behavior with this tag:
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="CurrentUser" revocationMode="NoCheck"/>
</clientCertificate>
And then add the client's certificate to the "trusted people" certificate store of the user that the server runs as.
Check out the WCF Security Guidance page on Codeplex - excellent and very useful stuff!
In particular, check out the How-To's and even more specifically the
How To – Use Certificate Authentication and Message Security in WCF calling from Windows Forms
It explains in great detail how to set up a WCF service which requires its clients to present a valid certificate, and how to check that. If you want to allow only a single client, deploy that certificate only specifically to that one single client.
Hope this helps!