How to get WCF and Java clients to work. Expert help needed

4k views Asked by At

Background (a wee bit long):

A few years back I wrote for a customer a WCF service that is configured to use the basicHttpBinding with security mode = "Transport" with clientCredentialType = "Certificate", i.e. the clients are authenticating themselves using a (client) certificate. The service employs a custom AuthorizationManager that checks if the thumbprint of the incoming certificate is present in a predefined list of valid certificate thumbprints. If the incoming certificate is deemed valid, the operation is allowed to continue (if not, an exception is thrown).

The service has worked flawlessly for approximately four years and everyone's been happy. However, as so often happens requirements change and recently my customer has been approached by developers wishing to connect their applications to my customer's service. The only problem is that these developers are using some variant of Java as their preferred platform and now we are facing some serious problems. To make a long story short, no one has managed to get their Java implementation (e.g. Metro, Axis2) to work with the service as it is presently configured.

Last week we tried getting it to work with a client written in Java (Metro, JAX-WS) by changing the binding to the wsHttpBinding with security mode = "TransportWithMessageCredential" with Message clientCeredentialType = "UserName". I also added a custom UserNamePassWordValidatorType to the service credentials element in the config file.

I then commented out the custom AuthorizationManager since no certificate was coming from the client.

Lo' and behold, this time we got both SoapUI and the Java client proper to talk to the service.

(Btw, our service is self-hosted in a Windows Service)

Happy as can be, we decided to configure the service with two bindings, the existing basicHttpBinding that's been working without a glitch for a long time, and the newly tested wsHttpBinding. So we would have something like:

<services>
    <service name="SuperDuperService" behaviorConfiguration="superDuperBehaviour">
        <endpoint binding="basicHttpBinding" contract="ISuperDuperService" bindingConfiguration="SecureTransport"/>
        <endpoint binding="wsHttpBinding" address="stws" contract="ISuperDuperService" bindingConfiguration="SecureTransportAndSoap"/>
        <endpoint binding="mexHttpsBinding" address="mex" contract="IMetadataExchange" />
        <host>
            <baseAddresses>
                <add baseAddress="https://<url + port>/SuperDuperService"/>
            </baseAddresses>
        </host>
    </service>
</services>
<bindings>
    <basicHttpBinding>
        <binding name="SecureTransport" maxBufferSize="2065536" maxBufferPoolSize="524288" maxReceivedMessageSize="2065536">
            <security mode="Transport">
                <transport clientCredentialType="Certificate"/>
            </security>
            <readerQuotas maxDepth="32" maxStringContentLength="6553600" maxArrayLength="2065536"
                maxBytesPerRead="4096" maxNameTableCharCount="16384" />
        </binding>
    </basicHttpBinding>
    <wsHttpBinding>
        <binding name="SecureTransportAndSoap">
            <security mode="TransportWithMessageCredential">
                <message clientCredentialType="UserName"/>
            </security>
        </binding>
    </wsHttpBinding>
</bindings>
<behaviors>
    <serviceBehaviors>
        <behavior name="superDuperBehaviour">
            <serviceCredentials>
                <!-- The following element specifies the certificate use by this service for HTTPS (SSL) based transport security -->
                <serviceCertificate findValue="<some identifier>"
                        storeLocation="LocalMachine"
                        storeName="My"
                        x509FindType="FindBySubjectName" />
                <userNameAuthentication userNamePasswordValidationMode ="Custom" customUserNamePasswordValidatorType 

="SupportClasses.CustomUserNameValidator,SupportClasses"/>
            </serviceCredentials>
            <!-- The following element specifies how we're authorizing based on the client certificates received -->
            <serviceAuthorization serviceAuthorizationManagerType="SupportClasses.AuthorizationManager, SupportClasses"/>
            <serviceMetadata httpsGetEnabled="true"/>
            <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
    </serviceBehaviors>
</behaviors>

So where is the problem you may ask? Well, see that <serviceBehavior> element? As you well may know this element applies globally to the service meaning that during run-time both the CustomUserNameValidator and the AuthorizationManager will be called. The latter will complain that there is no certificate present when a client calls the service using the wsHttpBinding!

Argh!

Alternative solutions.

So far, these are the alternative solutions I have come up with:

Alternative 1) Create another Windows Service hosting our WCF service on a different URL. Then both services will have a separate configuration.

Alternative 2) Create two service implementations hosted in the same Windows Service and expose them both in the element each with their own binding and serviceBehaviour

Alternative 3) Figure out if it is at all possible to keep the current configuration and have the CustomUserNameValidator and the AuthorizationManager coexist peacefully

Sorry for this long post, but I needed to be thorough when providing the background for my question(s).

Question 1) Has anyone gotten WCF to work with Java clients using nontrivial configurations?

Question 2) Has anyone got a suggestion on how to solve alternative 3? (if at all possible)

Question 3) Which one, if any, of the above alternatives, would you recommend?

Question 4) Is there other alternatives you know of that I haven't thought of?

For the record, I've looked into the WCF interoperability tool, but I can't really see how it can help us. If anyone reading this has had good results using the interoperability "wizard",

please let me know.

Thanks in advance.

--norgie

1

There are 1 answers

1
Visual Stuart On

I would start by adding a second element in //services and configure that with a new @bindingConfiguration attribute and a distinct @address attribute. I think that will be simpler that alternatives 1, 2, or 3; or maybe it is alternative 3, I can't tell.

Web services exist to provide interoperation that is language-, vendor-, platform-, and vendor-neutral. WCF and Java interoperate in real world solutions every day.

Have you looked at this series on WCF and Java interoperation?

Also, it sounds like you would benefit from using some diagnostics tools that will let you see the different between the .NET/WCF client message and Java client messages. Use Fiddler or some other sniffer utility to see the messages on the wire. Turn on WCF tracing to see what WCF does once it receives the message.