donderdag 12 januari 2012

Secure WCF Service with ADFS


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>