none
Securing WCF Service by mutual cert + username RRS feed

  • Question

  •  

    The scenario I'm trying to support is:

       1. The service is only available to a trusted source.
       2. A source is trusted by certificate.
       3. The certificate must already reside in the Trusted People cred store of the service app server. (negotiation=false)
       4. This establishes the trust relationship. From this point forward my service will trust any creds presented by the client.
       5. For the purposes of auditing we need to track the username of the user invoking the service call.
       6. We'd also like to resolve their username to our internal identity (TrustedServer's "chris" identity would resolve to a this server's "cdarrigo" identity).


    When I set up my service using wsHTTP and mutual certs, I achieved the level of trust I was after. However I lost any identity information provided. That is, the PrimaryIdentity was the client's cert, and not their username.

    I've configured an endpoint using custom binding and implemented UserNameForCertificate authorization. I wrote a custom UserNameValidator that only ensures the username supplied is not null (since the username is coming from a trusted source I dont need to reauthenticate it). It appears I have achieved the level of trust I needed (the client must still present a trusted cert), and the PrimaryIdentity resolves to the supplied user's name.

     

    I have 2 outstanding issues:
    1) The ServiceSecurtiyContext.Current.PrimaryIdentity.Principal now resolves to the username, and examining the claimset in the AuthorizationContext shows only the identity claim for the username. How can I get a reference to the client certificate? I need the cert + username to resolve to the identity in server's identity store.

     

    2) I could not figure out how include the server's public key hash in the published mex data. When I was using wsHTTP, I turned off negotiation and the hash was presented in the client config. Using my custom binding I don't see a way to specify the negotiation setting, and hence I had to manually insert the server's cert reference in my client app.config file.

    Here's my server's App.Config for your review

     

    Code Snippet

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
       
        <services>
          <service behaviorConfiguration="SecuredServiceBehavior" name="Service.SecuredService">
            <!--<endpoint name="BasicHTTP"
                      address="BasicHTTP"
                      contract="Service.ISecuredService"
                      binding="basicHttpBinding"
                      />-->
            <!--<endpoint name ="wsHTTP"
                      address="wsHTTP"
                      binding="wsHttpBinding"
                      bindingConfiguration="wsHTTP"
                      contract="Service.ISecuredService"/>-->
            <endpoint address="custom"
              binding="customBinding" contract="Service.ISecuredService" bindingConfiguration="UserNameForCertificateBinding" />


            <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
            <host>
              <baseAddresses>
                <add  baseAddress="http://localhost:6000/SecuredService"/>
              </baseAddresses>
            </host>
          </service>
        </services>
        <bindings>
          <!--<wsHttpBinding>
            <binding name="wsHTTP">
              <security mode="Message">
                <message clientCredentialType="Certificate"                     
                         establishSecurityContext="false"
                         negotiateServiceCredential="false"/>
              </security>
            </binding>
          </wsHttpBinding>-->
           <customBinding>
            <binding name="UserNameForCertificateBinding">
              <textMessageEncoding messageVersion="Soap12" writeEncoding="utf-8">
                <readerQuotas maxDepth="32" maxStringContentLength="999999999" maxArrayLength="999999999"
             maxBytesPerRead="4096" maxNameTableCharCount="999999999" />
              </textMessageEncoding>
              <security authenticationMode="UserNameForCertificate"
             messageProtectionOrder="SignBeforeEncrypt"
             requireDerivedKeys="true"  includeTimestamp="true" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
                  
              </security>
              <httpTransport authenticationScheme="Anonymous"/>
            </binding>
          </customBinding>
        </bindings>

        <behaviors>
          <serviceBehaviors>
            <behavior name="SecuredServiceBehavior" >
              <serviceMetadata httpGetEnabled="true"  />
              <serviceDebug includeExceptionDetailInFaults="true"/>
              <!--<serviceAuthorization principalPermissionMode="Custom"></serviceAuthorization>-->
              <serviceCredentials>
                <userNameAuthentication userNamePasswordValidationMode="Custom"
                                        customUserNamePasswordValidatorType="WCFServiceLibrary.CustomUserNameValidator, WCFServiceLibrary" />
                <clientCertificate>
                  <authentication revocationMode="NoCheck" certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine"/>
                </clientCertificate>
             
                <serviceCertificate findValue="400fcf536613789b0fdca7734e93a89ea58456c0" x509FindType="FindByThumbprint"
                                    storeLocation="LocalMachine" storeName="My"/>
              </serviceCredentials>
            </behavior>
          </serviceBehaviors>
        </behaviors>
       
      </system.serviceModel>
    </configuration>

     

     

    Thank you.
    Monday, April 21, 2008 9:36 PM

All replies