WCF Service for Java Client Using Service Certificate and Self Signed Client Certificate Supplied by Client Developer

StackOverflow https://stackoverflow.com/questions/13039355

Pregunta

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?

¿Fue útil?

Solución

After a long discussion in our comments, I would point you to the article Nine simple steps to X.509 certificates in WCF. I know it is just a link, but it is a very long article.

Seems that your issue is actually that your clients do not have a certificate that you can authenticate them with. In the scheme that you are shooting for, each client will need to have a private key that identifies them, and the server/service needs to have the corrisponding public key to verify the signature (at least installed in the key store).

Your current setup seems that each client has the public key for the server as a trusted server. So you can get them to connect and encrypt over SSL, and the client will trust the server, but the server actually doesn't know who the client is, or at least can't authenticate them without something to identify them. Your previous way, of username and password, is how they were identified before, but now with x.509 certificates, each person that had a username-password combo will need a unique certificate with a private key, in order to do the authentication.

Then, you will need to either map those users to a windows/LDAP account for easy administration and access control, or you will need to implement a custom validator (and possibly IIdentity, principal, and serivce credentials) in order to validate the private certificates and "log in" the user.

I hope this has been a bit more helpful to you. Here is one more on x509 custom auth and val

EDIT: In response to Update2, try setting the IIS site/application to "IGNORE User Certificates" under the SSL settings, and see if you get a different error, and vis-versa. Chances are if you have it set to require, but do not have ASP compatibility mode on in code, IIS is not passing the IIdentity through, and unless you have some sort of authentication scheme set up in IIS, it wont do anything for you. If you set this to ignore, you can quickly set up a custom validator and see the certificates come through, on which you can then build the chain and create some sort of authorization on them.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top