ORDINA BLOGT

Secure WCF Service with ADFS

This article describes how to secure a WCF Service by ADFS.

  • 12 januari 2012

Introduction

This article describes how to secure a WCF Service by ADFS.

ADFS

Create a Relying Party in ADFS. Use these settings:

 Display name = Name of the service
 Profile = AD FS 2.0 Profile
 Certificate = encryption certificate
 Configure URL = uncheck both options
 Identifiers = complete url of the service, for example https://www.hostname.com/service1.svc
 Auth = Permit all users
 

Certificates

These are the needed certificates:
 
SSL certificate for the WCF service in IIS

  • Create this with CertSrv. Make sure the CN Name is the same as the hostname used for the WCF Service in IIS.
  • Import without private key into Trusted People store

Encryption certificate for the relying party in ADFS

  • Import with private key into Personal store
  • Give rights to IIS_IUSRS

Credentials certificate for the client

  • Import with private key into Personal Store
  • Give rights to the windows account the application is running under, or app pool the website is running under

Console Application code

 string userCertificateThumbprint =  "[thumbprint of Credentials certificate for the client]";
 
var channelFactory =
       new ChannelFactory<ISync>("ServiceReference1.ISync");
 
// Set certificate for user
 channelFactory.Credentials.ClientCertificate.SetCertificate(
    StoreLocation.LocalMachine,
    StoreName.My,
    X509FindType.FindByThumbprint,
    userCertificateThumbprint);

var wcfClient = channelFactory.CreateChannel();
 
var res = wcfClient.Foo();
 

Active Directory

Create a user in Active Directory.
Login to CertSrv and with the created user, and create a certificate. It will be automatically set in AD.
The created certificate will be stored in the Personal store of the Current User. It should be imported into the Personal store of Local Computer this way:

  • Open MMC
  • Go to the Personal Store of Current User
  • Export the created certificate including it's private key
  • Go to the Personal Store of Local Computer
  • Import the certificate
  • Make a note of the thumbprint, it should be put into the Console Application config later.
     

WCF Service configuration

Add this code to the app.config of the WCF Service:
 
<configSections>
     <section
        name="microsoft.identityModel"
        type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
 </configSections>
 
<system.serviceModel>
    <services>
        <service
           name="[Name of the service]"
           behaviorConfiguration="federationBehavior">
 
          <endpoint
              address=""
              binding="customBinding"
              contract="[Interface implemented by the WCF Service]"
             <!-- example: Wcf.IService1 -->
            
             bindingConfiguration="WifActiveFedBinding" />
 
          <endpoint
              address="mex"
              binding="mexHttpsBinding"
              contract="IMetadataExchange" />
        </service>
    </services>
 

   <behaviors>
        <serviceBehaviors>
           <behavior name="federationBehavior">
              <federatedServiceHostConfiguration />
              <serviceMetadata httpsGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="true" />
 

             <serviceCredentials>
                 <!-- Encryption certificate for the relying party in ADFS -->
                 <serviceCertificate
                    findValue="[thumbprint]"
                    storeLocation="LocalMachine"
                    storeName="My"
                    x509FindType="FindByThumbprint" />
             </serviceCredentials>
           </behavior>
        </serviceBehaviors>
    </behaviors>
 
   <extensions>
        <behaviorExtensions>
           <add
              name="federatedServiceHostConfiguration"
              type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        </behaviorExtensions>
    </extensions>
 
 <bindings>
   <customBinding>
    <binding name="WifActiveFedBinding">
 
    <security 

     authenticationMode="SecureConversation"       

   messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10"
    requireSecurityContextCancellation="false">

      <secureConversationBootstrap
       authenticationMode="IssuedTokenOverTransport"
       messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10">
 
      <issuedTokenParameters>
        <additionalRequestParameters>
         <AppliesTo>
          <EndpointReference>
           <!-- Base address of the WCF Service. -->
           <!-- Use a domain name, don't use an ip address. -->
           <!-- Domain name must be same as defined in certificate -->
           <Address>https://[DomainName]/</Address>
          </EndpointReference>
         </AppliesTo>
        </additionalRequestParameters>
       </issuedTokenParameters>
 
     </secureConversationBootstrap>
     </security>
 
    <httpsTransport />
 
   </binding>
   </customBinding>
  </bindings>
 </system.serviceModel>
 
<microsoft.identityModel>
  <service>
   <audienceUris>
    <!-- All endpoints that need to be secured. -->
    <add value="https://[DomainName]/[ServicePath]" />
    <!-- example: https://www.mywcfservice.com/wcf.service1.svc -->
   </audienceUris>
 
  <issuerNameRegistry
    type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
 
   <trustedIssuers>
     <!--Signing certificaat of ADFS -->
     <add
      thumbprint="thumbprint of signing certificaat of ADFS"
      name="http://[adfs server]/adfs/services/trust" />
    </trustedIssuers>
   </issuerNameRegistry>
  </service>
 </microsoft.identityModel>

Console Application - app.config

<configSections>
    <section       name="microsoft.identityModel"
       type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
 </configSections>
 
<system.serviceModel>
    <bindings>
       <customBinding>
          <binding name="WifActiveFedClientBinding">
         <security
    authenticationMode="SecureConversation"
 messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10">
   
            <secureConversationBootstrap
     authenticationMode="IssuedTokenOverTransport"
     messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10">
    
    <issuedTokenParameters keyType="SymmetricKey" tokenType="">
    
                <additionalRequestParameters>
                   <trust:SecondaryParameters
                     xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
                     <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>
                     <AppliesTo xmlns="http://schemas.xmlsoap.org/ws/2004/09/policy">
                       <EndpointReference xmlns="http://www.w3.org/2005/08/addressing">
                        
 <Address>http://mandatorybutdoesntmatter/</Address>
                       </EndpointReference>
                     </AppliesTo>
                   </trust:SecondaryParameters>
                 </additionalRequestParameters>
    
                <issuer
 address = "https://[adfs hostname]/adfs/services/trust/13/certificatemixed"
      binding="ws2007HttpBinding"
      bindingConfiguration="certificateTrust" />
     
              </issuedTokenParameters>
 
            </secureConversationBootstrap>
           </security>
 
          <httpsTransport/>
   
         </binding>
       </customBinding>
  
   <ws2007HttpBinding>
    <binding name="certificateTrust" >
           <security mode="TransportWithMessageCredential">
             <message
     clientCredentialType="Certificate"  establishSecurityContext="false" />
           </security>
         </binding>
       </ws2007HttpBinding>
     </bindings>
 
    <client>
       <endpoint
          address="https://[DomainName]/[ServicePath]"
          <!-- example: https://www.mywcfservice/wcf.service1.svc   -->
          binding="customBinding"
          bindingConfiguration="WifActiveFedClientBinding"
          contract="ServiceReference1.ISync"
          name="ServiceReference1.ISync">
      <identity>
           <certificateReference
             storeName="My"
             storeLocation="LocalMachine"
             x509FindType="FindByThumbprint"
             findValue="[thumbprint of SSL certificate for the WCF service in IIS]" />
         </identity>
       </endpoint>
     </client>
 
  </system.serviceModel>
 
</configuration>