I am developing a WCF service based on a vendor's implementation to receive data from them. I have a working version of this service using one way SSL with the client using our server certificate. Because of a recent change to their implementation, I now need to authenticate their client with a certificate instead of username and password.
They develop in Java and I have no control over the client or the way they generate the certificate. They do not support the WSHttpBinding so I have to use the BasicHttpBinding. Their certificate is self-signed and will unfortunately be that way in production. I tried using Transport for the security and according to our server admin had their certificate installed appropriately on our server (I do not have access to our IIS server). I believe IIS was having an issue with the certificate because it is self-signed so I moved away from that implementation.
After a lot of research I determined that it would probably only work if I validated their certificate in the service itself by getting it from the OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets[0]
. This is my first WCF service so I believe I have it set up correctly but am getting the following error when testing from my WCF test client: The private key is not present in the X.509 certificate
. When I have the vendor test from their client they are getting An error occurred when verifying security for the message.
Do I have something configured incorrectly? Is this implementation supported?
Service Web.Config:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="FileReceiverServiceBehavior">
<useRequestHeadersForMetadataAddress/>
<serviceMetadata httpsGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="FileReceiverServiceBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate"/>
</security>
<readerQuotas maxStringContentLength="2147483647"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="FileReceiverServiceBehavior" name="FileReceiverService.FileReceiverService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="FileReceiverServiceBinding" contract="FileReceiverServiceSoap" bindingNamespace="http://www.openuri.org/" />
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
</configuration>
My WCF Test Client Config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="test">
<clientCredentials>
<clientCertificate findValue="Users"
x509FindType="FindBySubjectName"
storeLocation="LocalMachine"
storeName="My"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_FileReceiverServiceSoap" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Certificate" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://mydomain.com/vendor/FileReceiverService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_FileReceiverServiceSoap"
contract="ServiceReference1.FileReceiverServiceSoap" name="BasicHttpBinding_FileReceiverServiceSoap" behaviorConfiguration="test" />
</client>
</system.serviceModel>
</configuration>
Update:
The vendor sent me the correct certificate. It is a valid certificate issued by a commercial CA. When I view the certificate locally I can see the chain to the trusted root and the status shows 'OK'.
If I use security mode="Transport"
and transport clientCredentialType="Certificate"
I get: The HTTP request was forbidden with client authentication scheme 'Anonymous'
If I use security mode="TransportWithMessageCredential"
and message clientCredentialType="Certificate"
I get: The private key is not present in the X.509 certificate.
I think I may need to use strictly Transport so that it is interoperable with the Java client so I am focusing on the "The HTTP request was forbidden with client authentication scheme 'Anonymous'" error. Is that an issue with IIS settings or is it just not able to correctly validate the certificate when it gets to the server? The certificate is in Trusted People and I tried no client certificateValidationMode
but also PeerTrust
, ChainTrust
and PeerOrChainTrust
. Any help would be greatly appreciated.
Update2:
I am focusing on getting this to work with the following change to my original server configuration and matching change to my test client:
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
I still get: The HTTP request was forbidden with client authentication scheme 'Anonymous'. I found an explanation here that basically confirms that the client certificate could not be validated by the server. How do I have our server administrators configure this application in IIS and install the certificate so that this works correctly?