Testing a WCF Service with certificates locally - wcf

I have a WCF Service which exposes a method to receive content. This service is going to be consumed by a client over the internet. Client provided the following certificates and installed them as follows on my local machine:
Comodo Intermediate .cert
1) Intermediate Certification Authorities > Comodo Intermediate
Comodo Root .cert
2) Trusted Root Certification Authorities > Commodo Root
X509 Client Certificate .pem
3) Trusted People Store > Client certificate
I want to test/emulate a client call to test my webservice which is running locally. I installed the certificates and added the following binding to my WCF Service config
<protocolMapping>
<add scheme="https" binding="wsHttpBinding"/>
</protocolMapping>
<bindings>
<wsHttpBinding>
<binding>
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</wsHttpBinding>
</bindings>
I created a test client console application and added the following config
<behaviors>
<endpointBehaviors>
<behavior name="endpointCredentialBehavior">
<clientCredentials>
<clientCertificate findValue="ClientCertificate"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="Binding1">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
I know that on the testing and production environment, I have a server certificate but to test this all locally and successfully, would I need to create a server certificate and how so. Could this be done on the same box or would I have to use SOAP UI or something?

If you have a service certificate (issued by some formal institution), you can test it locally, please refer to the below link.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/transport-security-with-certificate-authentication
When we use the transport security with certificate, we should establish trust relationship between the client and the server first, and then if we want to use the self-signed certificate, we could PowerShell to create the certificate. Please refer to the below Powershell command to create self-signed certificate.
New-SelfSignedCertificate -DnsName "vabqia864VM" -CertStoreLocation "cert:\LocalMachine\My"
For details.
https://learn.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps
For server side, we are supposed to configure a port with the SSL certificate since we use https protocol(if we use IIS to host this, the web site binding module will do this).
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
For client side, we should provide a client certificate for authentication (also could use endpoint behavior to complete this).
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "9ee8be61d875bd6e1108c98b590386d0a489a9ca");
Feel free to let me know if there is anything I can help with.

Related

Why is my certificate not being send in the request?

I am calling a web service and need to provide a certificate for client authentication. But for some reason I cannot figure out why the certificate is not send when the server requests for it.
I tried different suggestions from several forums around all possible settings for the bindings and behaviors. But whatever I tried when inspecting the "Certificate" packet in Wireshark the certificate part stays empty.
I created a certificate for the server (.cer) and a certificate for the client (.pfx) See zip file.
On the server the .cer file is installed and marked as being a valid certificate for authentication.
I have installed the .pfx certificate in my certificate store (LocalMachine\My) the password is 'pvp'.
My client config is (and I already tried several different settings all with the same result):
<system.serviceModel>
<client>
<endpoint name="IgjEndpoint"
address="https://tekortkomingen-wvggz.webservices-dbb-acc.igj.nl/"
binding="customBinding"
bindingConfiguration="test"
behaviorConfiguration="IgjEndpointBehaviorConfig"
contract="IGJReference.WebserviceTekortkoming">
</endpoint>
</client>
<bindings>
<customBinding>
<binding name="test">
<textMessageEncoding messageVersion="Soap11WSAddressing10" />
<security authenticationMode="CertificateOverTransport" />
<httpsTransport requireClientCertificate="true" realm="" useDefaultWebProxy="false" />
</binding>
</customBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="IgjEndpointBehaviorConfig">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine" findValue="CN=PVP-SelfSigned-ClientCert-Acc" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
When I make the call to the web service I get a securitynegotiationexception "Could not establish secure channel for SSL/TLS with authority" which for as far as I know is the result of the certificate not being sent. When I make the call with postman it succeeds.
I have the wireshark output for postman and my application here.
Looking at the postman output you will see that the server's certificate request on line 1877 contains the "PVP-SelfSigned-ClientCert-Acc" as an acceptable CA. And at line 2189 my client certificate is send to the server.
But in the wireshark output when calling it from my application you still see that the server's certificate request on line 1745 contains the "PVP-SelfSigned-ClientCert-Acc" as an acceptable CA.
But on line 1959 there is no certificate being sent, however when I look at the request object when debugging I see the certicate is there in my request object under "client.ClientCredentials.ClientCertificate"
The last 5 days I have been searching the internet for possible causes and solutions to my problem but I had no luck so far with all the suggestions I have tried.
I hope someone with more expertise can make something out of the wireshark output and help me out?
In addition in my .net code I just do the following
var client = new WebserviceTekortkomingClient("IgjEndpoint");
var response = client.RequestTekortkomingen(new RequestTekortkomingen());
On the second line it fails on the connection.
Finally figured this one out. The other party needed to trust the root CA from our client certificate

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.

MessageSecurityException: The HTTP request was forbidden with client authentication scheme 'Anonymous'" when accessing credential secured WCF service

I'm trying to understand the process of transport security authentication, based on certificates. Suppose I'm making a service with the following config with https opened on 8732 port:
<wsHttpBinding>
<binding name="SecurityTest">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
<service name="MyNamespace.MyService">
<host>
<baseAddresses>
<add baseAddress="https://localhost:8732/MyService/" />
</baseAddresses>
</host>
<endpoint
address=""
binding="wsHttpBinding" bindingConfiguration="SecurityTest"
contract="MyNamespace.IContract" >
</endpoint>
</service>
Then I create a self-signed certificate for Root Authority so that I could create new certificates:
makecert -n "CN=MyAuthority" -r -sv MyAuthority.pvk MyAuthority.cer -sky exchange
Then I add my MyAuthority.cer to the local machine "Root" cataloge. After this I create another certificate using my MyAuthority certificate and place it in local machine's "My" catalog:
makecert -sky exchange -sk local -iv MyAuthority.pvk -n "CN=local" -ic MyAuthority.cer local.cer -sr Localmachine -ss My
Then I use netsh to bind my local.cer certificate to 8732 port:
netsh http add sslcert ipport=0.0.0.0:8732 certhash=02b751d7f71423c27141c9c385fc3d3976 d7 aa b5 appid={C4BFC5DC-2636-495B-9803-8DD8257C92C3}
The server service side is done, and it starts and works. Now I create a client:
<bindings>
<wsHttpBinding>
<binding name="SecurityTest" >
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint name="testPoint"
address="https://localhost:8732/MyService/"
binding="wsHttpBinding" bindingConfiguration="SecurityTest"
behaviorConfiguration="ep"
contract="MyNamespace.IContract">
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="ep" >
<clientCredentials>
<clientCertificate findValue="local"
storeLocation="CurrentUser" storeName="My"
x509FindType="FindBySubjectName" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
When I start it and consume the service method, I get an error:
MessageSecurityException: The HTTP request was forbidden with client authentication scheme 'Anonymous'" when accessing credential secured WCF service from remote computer
I what to ask if I understand everything well in this scheme, and maybe to get advice, how to solve this error.
Does my service uses local.cer to encrypt messages on transport level?
Do I have to add MyAuthority.cer to Trusted published catalog on each client machine in order my clients could decrypt the messages without creating personal validation handlers?
Does my client in current example uses the local.cer as his credentials, and this certificate would be send to the service side?
How does server side handles the client certificate? Does it check if it was signed by MyAuthority.cer or it checks it with the ssl certificate? How I can see what the certificate is checked with?
Why do I get the error?
Thanks in advance
1). Does my service uses local.cer to encrypt messages on transport level?
Yes, it does.
2). Do I have to add MyAuthority.cer to Trusted published catalog on each client machine in order my clients could decrypt the messages without creating personal Validation handlers?
Yes, since you are using a self-signed certificate (signed by an authority/CA you created) -- the clients would need to either trust the authority/CA or you would need to write code/configuration on the client side for an "exception".
3). Does my client in current example uses the local.cer as his credentials, and this certificate would be send to the service side?
It may be OK but you shouldn't use the same certificate for both client and server -- you should use a different certificate for the client. Currently, you are instructing it to use the following certificate, per your configuration:
<clientCertificate findValue="localhost" storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName" />
So if you have a certificate with subject name = "localhost" in your CurrentUser/My store, and the identity running the client program can access it (and its private key), it will be presented to the server as a client certificate.
4). How does server side handles the client certificate? Does it check if it was signed by MyAuthority.cer or it checks it with the ssl certificate? How I can see what the certificate is checked with?
The Framework on the server side checks that the client certificate presented is valid and trusted, that is all. If a client presents a certificate signed by e.g. VeriSign and you have the VeriSign CAs in your Machine/Trusted CAs store, that would be considered valid client certificate. If you want to limit the accepted certificates to only those signed by a specific CA, you would need to add additional code for that (or remove all the other trusted CAs from the store).
5). Why do I get the error?
There are a few reasons you could see that (rather cryptic) error message. First off, do you have a certificate in your store matching what is specified in item 3?

WCF with SSL hosted by iis 7.5 or consolehost

this is a very straightforward and easy question.
So please give me an easy answer(tutorial would be good)..
And not a very difficult expert anwswer(what I all the time find on google..)
So the question is:
1. How do you create a simple webservice that will be hosted by iis 7.5 and has SSL
How do you create a simple webservice that will be hosted by console and has SSL
If you create a client that use a SSL wcf host then you first need to select Yes or No, how can the computer automatically select YES?
To configure IIS hosted WCF service work with SSL you need:
1. Install Certificate
2. Set SSL is required in your application settings in inetmgr.
3. Configure security in WCF service settings. Here is an example of security settings of WCF service
...
<service behaviorConfiguration="YourServiceBehavior" name="...">
...
<behaviors>
<serviceBehaviors>
<behavior name="YourServiceBehavior">
...
<serviceCredentials>
<serviceCertificate findValue="CerttificateName" storeLocation="CertificateStoreLocation"
storeName="CertificateStoreName" x509FindType="FindBySubjectName" />
</serviceCredentials>
...
</behavior>
</serviceBehaviors>
</behaviors>
...
<bindings>
<basicHttpBinding>
<binding name="YourBindingName" ...>
...
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Basic" proxyCredentialType="None"/>
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
...
I don't have an answer for you about console hosted WCF service and SSL.
Also I'm not sure what do you mean when asking how can the computer automatically select yes. It seems you are talking about accept using of certificate considered as not valid. Line of code below should help to do that.
ServicePointManager.ServerCertificateValidationCallback = delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; };