4

I have a simple WCF service setup that uses JSON. In this service I want to use client authentication with a client certificate. I've configured IIS 6 to require SSL and to require client certificates by setting the folder /site/services/wcf/json/. This setup is typically referred to as 2-way SSL.

But I am getting an exception whenever I try to test the page with the generated SSL certificate.

The SSL settings for the service 'None' does not match those of the IIS 'Ssl, SslNegotiateCert, SslRequireCert'.

Stack Trace: 

[NotSupportedException: The SSL settings for the service 'None' does not match those of the IIS 'Ssl, SslNegotiateCert, SslRequireCert'.]
   System.ServiceModel.Activation.HostedAspNetEnvironment.ValidateHttpsSettings(String virtualPath, Nullable`1& requireClientCertificate) +117347
   System.ServiceModel.Channels.HttpsChannelListener.ApplyHostedContext(String virtualPath, Boolean isMetadataListener) +97
   System.ServiceModel.Activation.HostedAspNetEnvironment.ApplyHostedContext(TransportChannelListener listener, BindingContext context) +84
   System.ServiceModel.Channels.HttpsTransportBindingElement.BuildChannelListener(BindingContext context) +93
   System.ServiceModel.Channels.BindingContext.BuildInnerChannelListener() +63
   System.ServiceModel.Channels.MessageEncodingBindingElement.InternalBuildChannelListener(BindingContext context) +67
   System.ServiceModel.Channels.WebMessageEncodingBindingElement.BuildChannelListener(BindingContext context) +49
   System.ServiceModel.Channels.BindingContext.BuildInnerChannelListener() +63
   System.ServiceModel.Channels.Binding.BuildChannelListener(Uri listenUriBaseAddress, String listenUriRelativeAddress, ListenUriMode listenUriMode, BindingParameterCollection parameters) +125
   System.ServiceModel.Description.DispatcherBuilder.MaybeCreateListener(Boolean actuallyCreate, Type[] supportedChannels, Binding binding, BindingParameterCollection parameters, Uri listenUriBaseAddress, String listenUriRelativeAddress, ListenUriMode listenUriMode, ServiceThrottle throttle, IChannelListener& result, Boolean supportContextSession) +337
   System.ServiceModel.Description.DispatcherBuilder.BuildChannelListener(StuffPerListenUriInfo stuff, ServiceHostBase serviceHost, Uri listenUri, ListenUriMode listenUriMode, Boolean supportContextSession, IChannelListener& result) +668
   System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost) +1228
   System.ServiceModel.ServiceHostBase.InitializeRuntime() +60
   System.ServiceModel.ServiceHostBase.OnBeginOpen() +27
   System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +50
   System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +318
   System.ServiceModel.Channels.CommunicationObject.Open() +36
   System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +184
   System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +615

I've tested that the certificates are installed properly. I created a basic virtual directory that required client auth. This virtual directory contains a simple .htm file. I've confirmed it requires https and that it challenges me for my client cert and when I proved a valid client cert it displays the .htm page and when I do not proved a valid cert it does not.

When applying these same settings in IIS to my WCF service I get the above exception. I attempted to configure the services to also require SSL and client auth, but I continue to get the exception above.

Here are my settings.

<system.serviceModel>
<!-- behaviors -->
<behaviors>
    <endpointBehaviors>
        <behavior name="jsonBehavior">
            <enableWebScript />
                <clientCredentials>
                <clientCertificate findValue="*.MyCompany.com" storeLocation="LocalMachine" x509FindType="FindBySubjectName" storeName="My" />
            </clientCredentials>
        </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
        <behavior name="">
            <serviceDebug includeExceptionDetailInFaults="true" />
            <serviceMetadata httpsGetEnabled="true" httpGetEnabled="false" />
            <serviceCredentials>
                    <serviceCertificate  findValue="*.MyCompany.com" storeLocation="LocalMachine" x509FindType="FindBySubjectName" storeName="My" />                                              
                </serviceCredentials>
        </behavior>
    </serviceBehaviors>
</behaviors>

<!-- bindings -->
<bindings>
    <webHttpBinding>
        <binding name="webBinding">
            <security mode="Transport">
                <transport clientCredentialType="Certificate"/>
            </security> 
        </binding>
    </webHttpBinding>
</bindings>

<!-- services -->

<services>
    <service name="Service1Json" behaviorConfiguration="">
        <endpoint address="https://www.MyCompany.com/site/services/wcf/json/Service1.svc"
            behaviorConfiguration="jsonBehavior"
            binding="webHttpBinding"
            bindingConfiguration="webBinding"
            contract="MyCompany.Services.Wcf.IService1" />
    </service>
    <service name="Service2Json" behaviorConfiguration="">
        <endpoint address="https://www.MyCompany.com/site/Services/WCF/json/Service2.svc"
            behaviorConfiguration="jsonBehavior"
            binding="webHttpBinding"
            bindingConfiguration="webBinding"
            contract="MyCompany.Services.Wcf.IService2" />
    </service>
    <service name="Service3Json" behaviorConfiguration="">
        <endpoint address="https://www.MyCompany.com/site/services/wcf/json/Service3.svc"
            behaviorConfiguration="jsonBehavior"
            binding="webHttpBinding"
            bindingConfiguration="webBinding"
            contract="MyCompany.Services.Wcf.IService3" />
    </service>
</services>

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

</system.serviceModel>
Lee Gillen
  • 143
  • 1
  • 9

1 Answers1

3

The problem actually ended up being more complex than I am describing above. And the resulting solution ended up being quite simple.

Not mentioned above in the original problem is that we have multiple endpoints: 1. SOAP 2. JSON. These need to be both secured with 2-way SSL.

Our mistakes were the following:

  1. Client and server credentials are not necessary.
  2. Service name must match that of the .svc file.
  3. Endpoint addresses must differ. So for our JSON endpoint we added "json". Also we removed the fully qualified URI from address and let WCF automatically generate it for us.

Here is the final config we ended up with:

<system.serviceModel>

    <!-- behaviors -->
    <behaviors>
        <endpointBehaviors>
            <behavior name="jsonBehavior">
                <enableWebScript />
            </behavior>
        </endpointBehaviors>
        <serviceBehaviors>
            <behavior name="">
                <serviceDebug includeExceptionDetailInFaults="true" />
                <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
            </behavior>
        </serviceBehaviors>
    </behaviors>

    <!-- bindings -->
    <bindings>
        <basicHttpBinding>
            <binding name="httpBinding">
                <security mode="None">
                </security>
            </binding>
        </basicHttpBinding>
        <webHttpBinding>
            <binding name="webBinding">
                <security mode="None">
                </security>
            </binding>
        </webHttpBinding>
    </bindings>

    <!-- services -->
    <services>
        <service name="MyCompany.Services.Wcf.Service1" behaviorConfiguration="">
            <endpoint address="json"
                behaviorConfiguration="jsonBehavior"
                binding="webHttpBinding"
                bindingConfiguration="webBinding"
                contract="MyCompany.Services.Wcf.IService1" />
            <endpoint address=""
                behaviorConfiguration=""
                binding="basicHttpBinding"
                bindingConfiguration="httpBinding"
                contract="MyCompany.Services.Wcf.IService1" />
        </service>

        <service name="MyCompany.Services.Wcf.Service2" behaviorConfiguration="">
            <endpoint address="json"
                behaviorConfiguration="jsonBehavior"
                binding="webHttpBinding"
                bindingConfiguration="webBinding"
                contract="MyCompany.Services.Wcf.IService2" />
            <endpoint address=""
                behaviorConfiguration=""
                binding="basicHttpBinding"
                bindingConfiguration="httpBinding"
                contract="MyCompany.Services.Wcf.IService2" />
        </service>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

</system.serviceModel>

Had we chosen to have SOAP not use 2-way SSL and JSON require 2-way SSL the config would have been a lot more complex.

Lee Gillen
  • 143
  • 1
  • 9