Why is my certificate not being send in the request? - ssl

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

Related

Testing a WCF Service with certificates locally

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.

Secure WCF service with basicHttpBinding

PC: Windows 8.1 + all updates, VS 2012 + all updates.
I've created a WCF service which I have tested and it works. I want to use this service against a Windows Phone 8 application therefore need to create a basicHttpBinding service which accepts a username and password in order to access the data. Here are the steps I've taken after completing the service which successfully runs on IIS.
Created an SSL (followed steps here http://msdn.microsoft.com/en-us/library/hh556232.aspx)
Changed my web.config file so it contains:
<system.serviceModel>
<services>
<service behaviorConfiguration="NewBehavior1" name="Service">
<endpoint address="" binding="basicHttpBinding" contract="IService" bindingConfiguration="NewBinding1" />
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="NewBinding1">
<security mode="TransportWithMessageCredential" />
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="MEX">
<serviceMetadata/>
</behavior>
<behavior name="NewBehavior1">
<serviceMetadata httpGetEnabled="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="Service, Services" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
Navigated to http://localhost/Service/Service.svc and got a 403.4 message (can't access without SSL) which is correct so I replaced the http with https and I could view the service page.
Knowing that it works using https I open up the WCF Test Client tool and navigate to the same URL and get the error (please note I've shortened some of the error and left in the main areas)
Error: Cannot obtain Metadata from https://localhost/Service/service.svc If this is a Windows (R) Communication Foundation service to which you have access, please check that you have enabled metadata publishing at the specified address.
Metadata contains a reference that cannot be resolved: 'https://localhost/Service/service.svc'.
Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost'.
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
The remote certificate is invalid according to the validation procedure.HTTP GET Error
I exported the certificate from IIS and installed it on the local machine and user under the
Trusted Root Certification Authorities. Made no difference
So now I'm a little lost as whatever I've tried doesn't seem to work. Could anyone advise?
I'll give this a try ... there are several potential reasons for this behavior.
A) If you're client test tool is built within IE and your SSL cert is self-signed, you'll always have problems with IE not accepting the certificate, and adding the cert to your client's "trusted" group won't help. IE hates self-signed certs and going through MS's Import routine is a waste of time.
B) If your client test tool is a self-built application, you shouldn't need to add the certificate to the trusted group even if it's self-signed. But you might need to add this to your code (for testing) in order to avoid the self-signed certificate glitch:
System.Net.ServicePointManager.CertificatePolicy = New TrustAllCertificatePolicy()
C) Assuming you're using a self-signed certificate, be careful how you create the certificate. This was a problem for me until I came up with these commands:
rem creates root authority file and cert in currentuser\root and gives it the right to sign certs
makecert.exe -a sha1 -n CN=CAS_Temp_Authority %Host_Authority_Cert_Name% -sr LocalMachine -ss Root -sky signature -pe -r -sk MyNewKey -cy authority
rem creates ssl cert, puts it in the currentuser\root authority and signs it based on the other certificate
makecert.exe -n cn=%Host_URL% %Host_Cert_Name% -is root -ic %Host_Authority_Cert_Name% -sky exchange -pe -sv %Host_Cert_PrivateKey% -eku 1.3.6.1.5.5.7.3.1
rem make the pfx file that will allow you to copy certs around with private keys
pvk2pfx -pvk %Host_Cert_PrivateKey% -spc %Host_Cert_Name% -pfx %Host_Cert_PFX% -f
As you can imagine, the "authority" cert (*.cer file) generated from that goes into your "trusted root..." store, the other exchange cert goes into your Local Machine / My store, but you want to import it as the *.pfx file, not the *.cer file. At least that worked for me.
Lastly, if A) and B) don't help you might try changing your SecurityMode from TransportWithMessageCredential to regular Transport and see if that makes a difference.
Good luck. Sorting out these WCF/SSL issues is tough for everyone.
Sorry, I might be completely wrong but I noticed that you configured your behavour like this:
<behavior name="NewBehavior1">
<serviceMetadata httpGetEnabled="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="Service, Services" />
</serviceCredentials>
I suppose if you want to use https you have to configure it like this:
<serviceMetadata httpsGetEnabled="true"/>enter code here
(httpS enable for service metadata).
I'd try, but I'm not 100% sure it will work for you, depending on the rest of your configuration

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?

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.

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!