WCF Service Not Encrypting Response - wcf

We have a WCF service hosted in IIS with the following customBinding. The service receives request from IBM Datapower that is encrypted and signed. The service can verify the signature and decrypt the request fine. But the response sent out is not encrypted or signed. (I can verify this using WCF logging)
<customBinding>
<binding name="myCustomBinding">
<textMessageEncoding messageVersion="Soap11" />
<httpsTransport requireClientCertificate="false" realm="" />
</binding>
</customBinding>
<behaviors>
<serviceBehaviors>
<behavior name="myServiceBehavior">
<serviceCredentials>
<serviceCertificate findValue="{serverCertificateName}" x509FindType="FindBySubjectName"
storeLocation ="LocalMachine" storeName ="My"/>
<clientCertificate>
<certificate findValue="{clientCertificateName}" x509FindType="FindBySubjectName"
storeLocation ="LocalMachine" storeName ="My" />
<authentication certificateValidationMode="None" includeWindowsGroups="false"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors></behaviors>
The MessageContracts have ProtectionLevel set to ProtectionLevel.EncryptAndSign
Am I missing something here?

I am not a WCF expert so forgive me if I am asking below question.
To encrypt and sign a content [in PKI domain] you need to have a private key [and everybody knows that is not included in certificate]. So I am just curious, as to where that private key is specified in above configuration. Is it the case that if you specify the certificate in above configuration, the corresponding private key is automatically picked up [I am just curious because I am not sure what is going under the hood]?
On the contrary when you receive an encrypted content, the only thing you need to verify and decrypt is the certificate [practically in most cases, keeping aside key negotiations that may happen].
Just let me know if this helps? May be this comment is too trivial [kindly excuse me if this is the case].

Related

How to perform ssl user authentication

A self-signed certificate is used for authentication in the WCF application. The server specified:
<security mode="Message">
<message clientCredentialType="Certificate"/>
</security>
...
<clientCertificate>
<authentication certificateValidationMode="PeerOrChainTrust" revocationMode="NoCheck"/>
</clientCertificate>
The certificate is enabled correctly on the client:
<endpointBehaviors>
<behavior name="wsHttpCertificateBehavior">
<clientCredentials>
<clientCertificate findValue="<Thumbprint>" storeName="My" storeLocation="LocalMachine" x509FindType="FindByThumbprint"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
On the client, the certificate is added to the trusted root certificates. When calling service methods, an error occurs: the calling user's identity was not verified by the service. I don't understand what else you need to specify for verification. If you remove the certificate and specify
<security mode= "None"/>
the client hangs when calling the service method. I don't understand why. I've been fighting this for a week. Please help me!
This is a demo using X.509 self-signed certificate verification:
<system.serviceModel>
<services>
<service name="Microsoft.Samples.X509CertificateValidator.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior">
<!-- use host/baseAddresses to configure base address provided by host -->
<host>
<baseAddresses>
<add baseAddress="http://localhost:8001/servicemodelsamples/service"/>
</baseAddresses>
</host>
<!-- use base address specified above, provide one endpoint -->
<endpoint address="certificate" binding="wsHttpBinding" bindingConfiguration="Binding" contract="Microsoft.Samples.X509CertificateValidator.ICalculator"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<!-- X509 certificate binding -->
<binding name="Binding">
<security mode="Message">
<message clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CalculatorServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<!--
The serviceCredentials behavior allows one to specify authentication constraints on client certificates.
-->
<clientCertificate>
<!--
Setting the certificateValidationMode to Custom means that if the custom X509CertificateValidator
does NOT throw an exception, then the provided certificate will be trusted without performing any
validation beyond that performed by the custom validator. The security implications of this
setting should be carefully considered before using Custom in production code.
-->
<authentication certificateValidationMode="Custom" customCertificateValidatorType="Microsoft.Samples.X509CertificateValidator.CustomX509CertificateValidator, service"/>
</clientCertificate>
<!--
The serviceCredentials behavior allows one to define a service certificate.
A service certificate is used by a client to authenticate the service and provide message protection.
This configuration references the "localhost" certificate installed during the setup instructions.
-->
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
This is the configuration file of the service, we need to specify the location of the certificate.
serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.Custom;
serviceHost.Credentials.ClientCertificate.Authentication.CustomCertificateValidator = new CustomX509CertificateValidator();
We custom verify the self-signed certificate.
public class CustomX509CertificateValidator : System.IdentityModel.Selectors.X509CertificateValidator
{
// This Validation function accepts any X.509 Certificate that is self-issued. As anyone can construct such
// a certificate this custom validator is less secure than the default behavior provided by the
// ChainTrust X509CertificateValidationMode. The security implications of this should be carefully
// considered before using this validation logic in production code.
public override void Validate(X509Certificate2 certificate)
{
// Check that we have been passed a certificate
if (certificate == null)
throw new ArgumentNullException("certificate");
// Only accept self-issued certificates
if (certificate.Subject != certificate.Issuer)
throw new SecurityTokenException("Certificate is not self-issued");
}
}
If you need a complete example of this demo you can download it in this link:
https://www.microsoft.com/en-us/download/details.aspx?id=21459

Issue with WCF Transport Security SSL

I have a set of WCF Services on one server that is being called from my website on another server. Without any security and just basicHttpBinding, this works just fine. Now I want to completely secure the same setup using SSL Transport with a self-signed certificate.
First I tried to use IIS on the Services Server to create a self-signed certificate, but the issue is that it is always creating the cert with the subject in mixed case but the url is lower case. Not sure if that will cause an issue, but I didn't succeed with it.
So I used makecert to (1) create a root cert that I then installed the *.cer file on the client server and service server into the Root CA stores and then (2) created another cert, using the root as the CA, that will be used for signing. I copied this one *.pfx and installed into the Personal store of the LocalComputer for both servers again.
So, now on my Services server, I went into IIS and setup the binding for 443 using the client cert. Then I selected my virtual directory and setup SSL requiring SSL and then selecting 'Required' for Client Certificates.
Now, if I just try to bring up the virtual directory in IE, using https and the full name as it shows in the cert, I get 403.7. I can't seem to get passed this error.
If I try to hit this virtual directory from the website server, I get a plain 403.
If I change my IIS setup to 'Accept' client certs instead of 'Require', the I can browse to my services on both boxes.
Somethings missing...but can't seem to find it.
Update:
Ok, so I created a one-page website and locked it down Requiring the same cert and was able to install that cert into IE on a client and hit the website. Finally, after installing the cert into IT, it allowed me to browse my Services virtual directory and even bring up the page on one of my services.
Question 1: When installing the server cert, I installed the private key version on both Server and Client machine into the LocalMachine -> Personal location. Is this correct or should I just install the public key into the client? It seems to be working.
Still problem - when making a WCF call now I'm getting the following:
SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'myserver.mydomain.com'.
Here's my server configuration:
<system.serviceModel>
<protocolMapping>
<add scheme="https" binding="basicHttpBinding"/>
</protocolMapping>
<bindings>
<basicHttpBinding>
<binding name="SecureCertBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="MyName.MyService" behaviorConfiguration="SecureBehavior">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="SecureCertBinding" name="SecureAlertService" contract="MyName.IMyService"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SecureBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceCredentials>
<serviceCertificate findValue="myserver.mydomain.com" storeName="My" storeLocation="LocalMachine" x509FindType="FindBySubjectName"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Here's my client configuration:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_SmallData" maxReceivedMessageSize="5000000">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="524288" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="SecureEndpoint">
<clientCredentials>
<clientCertificate findValue="myserver.mydomain.com" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="https://myserver.mydomain.com/Services/MyService.svc" binding="basicHttpBinding" behaviorConfiguration="SecureEndpoint"
bindingConfiguration="BasicHttpBinding_SmallData" contract="MyName.IMyService" name="BasicHttpBinding_IMyService"/>
</client>
</system.serviceModel>
How can I tell if the client is sending the certificate with the request?
Got it!! Finally.
I restarted the whole process, including creating new certs and all. Once I got passed the part where IE couldn't even navigate to the Services Virtual Directory, I knew from that point I was mainly up against WCF.
I use a service account on my Services Server for the application pool that has proper authority to the database. This way my connection strings are Windows Auth.
Make sure this service account has Full Permissions to the Cert (Snap-in...Manage Private Keys).
After doing this, I was still getting an error but I found an entry in my System Event Logs that stated an error occurred when trying to retrieve the private key from a cert. This finally helped.
My website (aka...Services client) is still running the application pool with ApplicationPoolIdentity. If this is the case:
Make sure to give the group 'IIS_Usrs' Full Permissions to the Cert.
Bingo...everything worked from this point forward...complete security.

WCF Web Service with mutual authentication certificates failing on client chain trust validation

I'm developping a proof of concept for a WCF web service using SSL and certificates for mutual authentication.
So, I have 2 certificates both provided by a valid certification authority (these are production certificates, not development). Here are the chains and the store locations for the certificates :
Server Certificate Chain
Issuer Root CA
Intermediate 1 CA
Server Authentication certificate
I don't know if this detail is important or not : server certificate is a wildcard certificate for the domain (*.mydomain.com)
Client Certificate Chain
Issuer Root CA
Intermediate 2 CA
Client Authentication certificate
Issuer Root CA is common root CA for both certificates.
Intermediates certificates are differents.
Store Location
Issuer Root CA have been imported into Trusted Root CA on both server and client machines
Intermediate CA 1 & 2 have been imported into Intermediate CA on both server and client
Issuer and intermediates certificates have both public keys only.
Server certificate have been imported into Personal on server machine. This certificate have a private key.
Server certificate have been imported into Personal on client machine. This certificate have a public key only.
Client authentication certificate have been imported into Personal on both server and clients machines. These certificates have both private keys.
I created a simple WCF application project hosted in IIS 8.5 with framework C# 4.0.
I use the example classes provided by default at the project creation, I have just renamed it into DemoService.svc.
Then, I created the client (I use a winform application for target users to have a graphical interface to view results) and add the web service reference.
Then, I modified the service configuration to set up mutual authentication. All is done via web.config :
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="demoServiceBehavior">
<serviceAuthorization principalPermissionMode="UseWindowsGroups"></serviceAuthorization>
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<serviceCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" findValue="*.mydomain.com"/>
<clientCertificate>
<authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck" mapClientCertificateToWindowsAccount="true"
trustedStoreLocation="LocalMachine"/>
<certificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"
findValue="myservice.mydomain.com"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="demoServiceBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
<serviceActivations>
<add service="WcfMutualAuthenticationServiceDemo.DemoService" relativeAddress="DemoService.svc" />
</serviceActivations>
</serviceHostingEnvironment>
<services>
<service name="WcfMutualAuthenticationServiceDemo.DemoService" behaviorConfiguration="demoServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="https://myservice.mydomain.com/"/>
</baseAddresses>
</host>
<endpoint name="demoServiceEndpoint"
address=""
binding="basicHttpBinding"
bindingConfiguration="demoServiceBinding"
contract="WcfMutualAuthenticationServiceDemo.IDemoService"></endpoint>
</service>
</services>
</system.serviceModel>
I modified the client configuration to set up mutual authentication too :
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="demoClientBehavior">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"
findValue="myservice.dekra-automotivesolutions.com"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="demoClientBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://myservice.mydomain.com/DemoService.svc"
behaviorConfiguration="demoClientBehavior"
binding="basicHttpBinding"
bindingConfiguration="demoClientBinding"
contract="DemoServiceValidReference.IDemoService"
name="demoServiceEndpoint" />
</client>
</system.serviceModel>
When I call the web service via the client, it return an exception :
System.ServiceModel.Security.SecurityNegotiationException: Could not
establish secure channel for SSL/TLS with authority
'myservice.mydomain.com'. --->
System.Net.WebException: The request was aborted: Could not create
SSL/TLS secure channel. at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan
timeout) --- End of inner exception stack trace ---
After investigation, I found an entry in the server's event viewer :
Handling an exception. Exception details:
System.IdentityModel.Tokens.SecurityTokenValidationException: The
X.509 certificate CN=myservice.mydomain chain building failed. The
certificate that was used has a trust chain that cannot be verified.
Replace the certificate or change the certificateValidationMode. A
certification chain processed correctly, but one of the CA
certificates is not trusted by the policy provider.
at
System.IdentityModel.Selectors.X509CertificateChain.Build(X509Certificate2
certificate) at
System.IdentityModel.Selectors.X509CertificateValidator.ChainTrustValidator.Validate(X509Certificate2
certificate) at
System.IdentityModel.Selectors.X509SecurityTokenAuthenticator.ValidateTokenCore(SecurityToken
token) at
System.IdentityModel.Selectors.SecurityTokenAuthenticator.ValidateToken(SecurityToken
token) at
System.ServiceModel.Channels.HttpsChannelListener1.CreateSecurityProperty(X509Certificate2
certificate, WindowsIdentity identity, String authType) at
System.ServiceModel.Channels.HttpsChannelListener1.ProcessAuthentication(IHttpAuthenticationContext
authenticationContext) at
System.ServiceModel.Activation.HostedHttpContext.OnProcessAuthentication()
at
System.ServiceModel.Channels.HttpRequestContext.ProcessAuthentication()
at
System.ServiceModel.Channels.HttpChannelListener1.HttpContextReceivedAsyncResult1.Authenticate()
at
System.ServiceModel.Channels.HttpChannelListener1.HttpContextReceivedAsyncResult1.ProcessHttpContextAsync()
Regarding this exception, I found that the issue is on the chain validation of the client authentication certificate, but I don't know why.
At this point, I'm stuck !
I don't know what's wrong with my certificates, and I don't know how to find a solution.
I truly hope that someone could help me fixing this issue.
Edit :
We have tested with another client certificate for the certification chain to be the same on server and client certificates, it don't change anything.
Solution was found today : configuration is OK on client and server.
It was a misconfiguration on the IIS server certificate mapping, the functionnality was not configured but not enabled.

Configuring WCF Transport security for IIS6 over SSL

I've been attempting to set up WCF transport security using SSL on IIS6.
The client is on a seperate machine on the same domain.
I understand the premise of certificates, root CA etc and have a working set of certs for message security and can use these no probs in the same enviroment set up. (i've learn't a lot over the last week :)
I'm having an nightmare trying to get my client to authenticate against the IIS 6 service when i switch it to SSL. Always recieving 'annonymous authetication not allowed' when calling.
IN IIS i have
a root signed CA cert set on the site for SSL port 443
(if i browse the https:// svc page i can see the IE padlock and the page says you need a cert to communicate)
under secure communications i have
require SSL channel
require 128 bit encryption
require client certificates
enable client certificate mapping (set up with a many to 1 mapping to a admin account on the IIS box for now matched on the cert subject O field )
under web site security (authentication and access control)
Anonymous access = ON
Intergrated Windows Authentication = OFF
basic Authentication = ON
For the client wsHttpBinding i have a certificate ready to authenticate and a custom endpoint behaviour to supply this info but i don't think its getting this far!
UPDATED SERVER CONFIG
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="CertificateWithTransport">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WCFServiceCertificate.Service1" behaviorConfiguration="credentialConfig">
<endpoint address="https://svnvmig02/Service1.svc"
binding="wsHttpBinding"
bindingConfiguration="CertificateWithTransport"
contract="WCFServiceCertificate.IService1">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="credentialConfig">
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
UPDATED CLIENT CONFIG
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://svnvmig02/Service1.svc" binding="wsHttpBinding" behaviorConfiguration="CustomBehavior"
bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference1.IService1"
name="WSHttpBinding_IService1">
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="svnvmig02" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
EDIT:
Probably worth mentioning that my VS projects are 3.5 but IIS6 is running .net4
With the amended config (thanks Fabio ;) i can now IE browse the address https://svnvmig01/Service1.svc from the client machine and see the generated svc page which allows me to click on the wsdl URl which is also available.
The majority of the pages i have found on the net refer to selfhosting or IIS7....I'm hoping IIS7 support is better ;)
Any help would be greatly appreciated :)
Your config includes:
https://svnvmig02:8091/Service1.svc
The normal port for ssl is 443.
It may be that the request is not going to the site that you expect it to go to. Therefore, you are getting and unexpected error message.
Check the IIS logs to make sure which site is receiving the request.
I think your issue here may be that you have IIS set to:
Anonymous access = OFF
I use transport security on several of my servers, and all the IIS6 ones have that setting ON, not OFF. This also corresponds to the error message you provided:
'annonymous authetication not allowed'
Without anon access off, IIS will either want the user to enter a username/password, or pass along a windows / active directory / kerberos credentials.

netTcpBinding without Windows credentials?

I've got a machine-control application where I have a single client computer and five server boxes communicating on the machine subnet. There is no domain controller. I would like to use netTcpBinding to allow for reliability and transaction support.
Is is possible to use username / password authentication with this binding, when a domain controller is not present? I would prefer not to use a certificate as I don't want to manage certificates across 900 computers (150 machines) that will not be connected to the office LAN.
Yes, of course - but only if you use Message security (rather than transport security). Define your binding configuration like so:
<netTcpBinding>
<binding name="UserNameSecurity">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</netTcpBinding>
and then reference that binding configuration in your endpoints (on server and client):
<endpoint address="....."
binding="netTcpBinding"
bindingConfiguration="UserNameSecurity"
contract="IMyService" />
Marc
UPDATE:
Ah, yes, on the server-side, you'll need a certificate to authenicate the service to the client calling it, and it's also used to encrypt+sign the messages. That's on the server only - clients need not install anything.
Configuration:
<behaviors>
<serviceBehavior>
<behavior name="ServerInternet">
<serviceCredentials>
<serviceCertificate
findValue="MyServiceCertificate"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehavior>
</behaviors>
<services>
<service name="MyServiceInternet"
behaviorConfiguration="ServerInternet">
....
</service>
</services>
Make sure to install your server's certificate into the "Local Machine" folder on your server, under the "subject name" that you specify in your config.
There is something you can try first. Set serviceNegotiationCredentials to true:
<message negotiateServiceCredential="true"/>
This will create a secure conversation between your client and your service without a domain controller.
BUT, if there isn't any domain controller, the client doesn't trust your service, so it will fail.
So you should set the expected identity of the service. You can find that in the WSDL of your service. By default, if you are hosted on IIS, it seems to be:
<client>
<endpoint>
<identity>
<servicePrincipalName value="host/NETWORKSERVICE"></servicePrincipalName>
</identity>
</endpoint>
</client>
I don't think you'll need it, but maybe you'll have to allow anonymous logon on the service side:
<serviceBehaviors>
<behavior>
<serviceCredentials>
<windowsAuthentication allowAnonymousLogons="true"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>