Calling WCF client proxies in Azure Functions

Azure Functions allow developers to write discrete units of work and run these without having to deal with hosting or application infrastructure concerns. Azure Functions are Microsoft’s answer to server-less computing on the Azure Platform and together with Azure ServiceBus, Azure Logic Apps, Azure API Management (to name just a few) has become an essential part of the Azure iPaaS offering.

The problem

Integration solutions often require connecting legacy systems using deprecating protocols such as SOAP and WS-*. It’s not all REST, hypermedia and OData out there in the enterprise integration world. Development frameworks like WCF help us deliver solutions rapidly by abstracting much of the boiler plate code away from us. Often these frameworks rely on custom configuration sections that are not available when developing solutions in Azure Functions. In Azure Functions (as of today at least) we only have access to the generic appSettings and connectionString sections of the configuration.

How do we bridge the gap and use the old boiler plate code we are familiar with in the new world of server-less integration?

So let’s set the scene. Your organisation consumes a number of legacy B2B services exposed as SOAP web services. You want to be able to consume these services from an Azure Function but definitely do not want to be writing any low level SOAP protocol code. We want to be able to use the generated WCF client proxy so we implement the correct message contracts, transport and security protocols.

In this post we will show you how to use a generated WCF client proxy from an Azure Function.

Start by generating the WCF client proxy in a class library project using Add Service Reference, provide details of the WSDL and build the project.

add_service_reference

Examine the generated bindings to determine the binding we need and what policies to configure in code within our Azure Function.

bindings

In our sample service above we need to create a basic http binding and configure basic authentication.

Create an Azure Function App using an appropriate template for your requirements and follow the these steps to call your WCF client proxy:

Add the System.ServiceModel NuGet package to the function via the project.json file so we can create and configure the WCF bindings in our function
project_json

Add the WCF client proxy assembly to the ./bin folder of our function. Use Kudo to create the folder and then upload your assembly using the View Files panelupload_wcf_client_assembly

In your function, add references to both the System.ServiceModel assembly and your WCF client proxy assembly using the #r directive

When creating an instance of the WCF client proxy, instead of specifying the endpoint and binding in a config file, create these in code and pass to the constructor of the client proxy.

Your function will look something like this

Lastly, add endpoint address and client credentials to appSettings of your Azure Function App.

Test the function using the built-in test harness to check the function executes ok

test_func

 

Conclusion

The suite of integration services available on the Azure Platform are developing rapidly and composing your future integration platform on Azure is a compelling option in a maturing iPaaS marketplace.

In this post we have seen how we can continue to deliver legacy integration solutions using emerging integration-platform-as-a-service offerings.

Implementing a WCF Client with Certificate-Based Mutual Authentication without using Windows Certificate Store

Windows Communication Foundation (WCF) provides a relatively simple way to implement Certificate-Based Mutual Authentication on distributed clients and services. Additionally, it supports interoperability as it is based on WS-Security and X.509 certificate standards. This blog post briefly summarises mutual authentication and covers the steps to implement it with an IIS hosted WCF service.

Even though WCF’s out-of-the-box functionality removes much of the complexity of Certificate-Based Mutual Authentication in many scenarios, there are cases in which this is not what we need. For example, by default, WCF relies on the Windows Certificate Store for accessing the own private key and the counterpart’s public key when implementing Certificate-Based Mutual Authentication.

Having said so, there are scenarios in which using the Windows Certificate Store is not an option. It can be a deployment restriction or a platform limitation. For example, what if you want to create an Azure WebJob which calls a SOAP Web Service using Certificate-Based Mutual Authentication? (At the time of writing this post) there is no way to store a certificate containing the counterpart’s public key in the underlying certificate store for an Azure WebJob. And just because of that, we cannot enjoy all the built-in benefits of WCF for building our client.

Here, they explain how to create a WCF service that implements custom certificate validation be defining a class derived from X509CertificateValidator and implementing an abstract “Validate” override method. Once defined the derived class, the CertificateValidationMode has to be set to “Custom” and the CustomCertificateValidatorType to be set to the derived class’ type. This can easily be extended to implement mutual authentication on the service side without using the Windows Certificate Store.

My purpose in this post is to describe how to implement a WCF client with Certificate-Based Mutual Authentication without using Windows Certificate Store by compiling the required sources and filling the gaps of the available documentation.

What to consider

Before we start thinking about coding, we need to consider the following:

  • The WCF client must have access to the client’s private key to be able to authenticate with the service.
  • The WCF client must have access to the service’s public key to authenticate the service.
  • Optionally, the WCF client should have access to the service’s certificate issuer’s certificate (Certificate Authority public key) to validate the service’s certificate chain.
  • The WCF client must implement a custom service’s certificate validation, as it cannot rely on the built-in validation.
  • We want to do this, without using the Windows Certificate Store.

Accessing public and private keys without using Windows Certificate Store

First we need to access the client’s private key. This can be achieved without any problem. We could get it from a local or a shared folder, or from a binary resource. For the purpose of this blog, I will be reading it from a local Personal Information Exchange (pfx) file. For reading a pfx file we need to specify a password; thus you might want to consider encrypting or implementing additional security. There are various X509Certificate2 constructor overloads which allow you to load a certificate in different ways. Furthermore, reading a public key is easier, as it does not require a password.

Implementing a custom validator method

On the other hand, implementing the custom validator requires a bit more thought and documentation is not very detailed. The ServicePointManager
class
has a property called “ServerCertificateValidationCallback” of type RemoteCertificateValidationCallback which allows you to specify a custom service certificate validation method. Here is defined the contract for the delegate method.

In order to authenticate the service, once we get its public key, we could do the following:

  • Compare the service certificate against a preconfigured authorised service certificate. They must be the same.
  • Validate that the certificate is not expired.
  • Optionally, validate that the certificate has not been revoked by the issuer (Certificate Authority). This does not apply for self-signed certificates.
  • Validate the certificate chain, using a preconfigured trusted Certificate Authority.

For comparing the received certificate and the preconfigured one we will use the X509Certificate.Equals Method. For validating that the certificate has not expired and not been revoked we will use the X509Chain.Build Method. And finally, to validate that the certificate has been issued by the preconfigured trusted CA, we will make use of the X509Chain.ChainElements Property.

Let’s jump into the code.

To illustrate how to implement the WCF client, what can be better than code itself J? I have implemented the WCF client as a Console Application. Please pay attention to all the comments when reading my code. With the provided background, I hope it is clear and self-explanatory.

using System;
using System.Configuration;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace MutualAuthClient
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Starting...");

                // Set the ServerCertificateValidationCallback property to a
                // custom method.
                ServicePointManager.ServerCertificateValidationCallback +=
                                                CustomServiceCertificateValidation;

                // We will call a service which expects a string and echoes it
                // as a response.
                var client = new EchoService.EchoServiceClient
                                            ("BasicHttpBinding_IEchoService");

                // Load private key from PFX file.
                // Reading from a PFX file requires specifying the password.
                // You might want to consider adding encryption here.
                Console.WriteLine("Loading Client Certificate (Private Key) from File: "
                                    + ConfigurationManager.AppSettings["ClientPFX"]);
                client.ClientCredentials.ClientCertificate.Certificate =
                                    new X509Certificate2(
                                    ConfigurationManager.AppSettings["ClientPFX"],
                                    ConfigurationManager.AppSettings["ClientPFXPassword"],
                                    X509KeyStorageFlags.MachineKeySet);

                // We are using a custom method for the Server Certificate Validation
                client.ClientCredentials.ServiceCertificate.Authentication.
                                CertificateValidationMode =
                                        X509CertificateValidationMode.None;

                Console.WriteLine();
                Console.WriteLine(String.Format("About to call client.Echo"));
                string response = client.Echo("Test");
                Console.WriteLine();
                Console.WriteLine(String.Format("client.Echo Response: '{0}'", response));
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(
                    String.Format("Exception occurred{0}Message:{1}{2}Inner Exception: {3}"
                                   , Environment.NewLine, ex.Message, Environment.NewLine,
                                   ex.InnerException));
            }

        }

        private static bool CustomServiceCertificateValidation(
                object sender, X509Certificate cert, X509Chain chain,
                SslPolicyErrors error)
        {
            Console.WriteLine();
            Console.WriteLine("CustomServiceCertificateValidation has started");

            // Load the authorised and expected service certificate (public key)
            // from file.
            Console.WriteLine("Loading Service Certificate (Public Key) from File: "
                                + ConfigurationManager.AppSettings["ServicePublicKey"]);
            X509Certificate2 authorisedServiceCertificate = new X509Certificate2
                    (ConfigurationManager.AppSettings["ServicePublicKey"]);

            // Load the trusted CA (public key) from file.
            Console.WriteLine("Loading the Trusted CA (Public Key) from File: "
                                + ConfigurationManager.AppSettings["TrustedCAPublicKey"]);
            X509Certificate2 trustedCertificateAuthority = new X509Certificate2
                    (ConfigurationManager.AppSettings["TrustedCAPublicKey"]);

            // Load the received certificate from the service (input parameter) as
            // an X509Certificate2
            X509Certificate2 serviceCert = new X509Certificate2(cert);

            // Compare the received service certificate against the configured
            // authorised service certificate.
            if (!authorisedServiceCertificate.Equals(serviceCert))
            {
                // If they are not the same, throw an exception.
                throw new SecurityTokenValidationException(String.Format(
                    "Service certificate '{0}' does not match that authorised '{1}'"
                    , serviceCert.Thumbprint, authorisedServiceCertificate.Thumbprint));
            }
            else
            {
                Console.WriteLine(String.Format(
                    "Service certificate '{0}' matches the authorised certificate '{1}'."
                    , serviceCert.Thumbprint, authorisedServiceCertificate.Thumbprint));
            }

            // Create a new X509Chain to validate the received service certificate using
            // the trusted CA
            X509Chain chainToValidate = new X509Chain();

            // When working with Self-Signed certificates,
            // there is no need to check revocation.
            // You might want to change this when working with
            // a properly signed certificate.
            chainToValidate.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
            chainToValidate.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
            chainToValidate.ChainPolicy.VerificationFlags =
                                    X509VerificationFlags.AllowUnknownCertificateAuthority;

            chainToValidate.ChainPolicy.VerificationTime = DateTime.Now;
            chainToValidate.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);

            // Add the configured authorised Certificate Authority to the chain.
            chainToValidate.ChainPolicy.ExtraStore.Add(trustedCertificateAuthority);

            // Validate the received service certificate using the trusted CA
            bool isChainValid = chainToValidate.Build(serviceCert);

            if (!isChainValid)
            {
                // If the certificate chain is not valid, get all returned errors.
                string[] errors = chainToValidate.ChainStatus
                    .Select(x => String.Format("{0} ({1})", x.StatusInformation.Trim(),
                            x.Status))
                    .ToArray();
                string serviceCertChainErrors = "No detailed errors are available.";

                if (errors != null && errors.Length > 0)
                    serviceCertChainErrors = String.Join(", ", errors);

                throw new SecurityTokenValidationException(String.Format(
                        "The chain of service certificate '{0}' is not valid. Errors: {1}",
                        serviceCert.Thumbprint, serviceCertChainErrors));
            }

            // Validate that the Service Certificate Chain Root matches the Trusted CA.
            if (!chainToValidate.ChainElements
                .Cast<X509ChainElement>()
                .Any(x => x.Certificate.Thumbprint ==
                                    trustedCertificateAuthority.Thumbprint))
            {
                throw new SecurityTokenValidationException(String.Format(
                        "The chain of Service Certificate '{0}' is not valid. " +
                        " Service Certificate Authority Thumbprint does not match " +
                        "Trusted CA's Thumbprint '{1}'",
                        serviceCert.Thumbprint, trustedCertificateAuthority.Thumbprint));
            }
            else
            {
                Console.WriteLine(String.Format(
                    "Service Certificate Authority '{0}' matches the Trusted CA's '{1}'",
                    serviceCert.IssuerName.Name,
                    trustedCertificateAuthority.SubjectName.Name));
            }
            return true;
        }
    }
}
 


And here is the App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="ClientPFX" value="certificates\ClientPFX.pfx" />
<add key="ClientPFXPassword" value="********" />
<add key="TrustedCAPublicKey" value="certificates\ServiceCAPublicKey.cer" />
<add key="ServicePublicKey" value="certificates\ServicePublicKey.cer" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IEchoService">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://server/EchoService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IEchoService"
contract="EchoService.IEchoService" name="BasicHttpBinding_IEchoService" />
</client>
</system.serviceModel>
</configuration>


In case you find difficult to read my code from WordPress, you can read it from GitHub on the links below:

I hope you have found this post useful, allowing you to implement a WCF client with Mutual Authentication without relying on the Certificate Store, and making your coding easier and happier! : )

SharePoint Online Web Service Authentication using WCF Client-side behaviour

With the release SharePoint in 2013 and the ever increasing numbers taking up the SharePoint Online offering, it’s a good time to start looking at some of the challenges when moving to these platforms.

SharePoint has traditionally been a presentation technology with its own unique SharePoint development model utilising SharePoint designer and custom Web Part development. With the latest release, SharePoint 2013 that development model has been challenged by a new autonomous development model where the complexities and constraints of SharePoint as a development and deployment platform has been replaced by a service oriented integration platform for multiple independently hosted applications.

SharePoint has always supported a rich and expanding set of services with every release. As part of the move to the new development model a lot of work has gone into improving the “Client side” access into SharePoint by wrapping services in client side APIs and delivered some as easy to consume REST/oData endpoints, but the functionality of those services is still limited and has not yet evolved to match the power of the native SharePoint Server API nor the native SharePoint Web Services.

When moving from an on-premise SharePoint to SharePoint Online web service clients that were previously written against SharePoint Web Services (like UserProfile.asmx or Lists.asmx) will stop working because the authentication model has changed. A web service client that previously used Active Directory accounts to access SharePoint will need to become “claims aware” to use SharePoint Online. This involves syncing active directory accounts up to the MS Online Active Directory and authenticating at the MS Online Security Token Service (STS) before accessing SharePoint.

We hit this problem where a client had built a set of jobs, utilities and components that request and update SharePoint through services which then broke when migrating to SharePoint Online. The solution to updating these clients is well covered by this blog by Wictor Wilén. What this involves is opening up the source code for each client and inserting the appropriate authentication code using the MSOnlineClaimsHelper to do get the authentication cookies before calling the service.

The problem is we really didn’t want to pick through someone else’s code and find all the places to insert the authentication code. Surely there is an easier way to inject this boilerplate code without having to recode all the clients? Turns out there is, if those clients are written using WCF. What we can do is write a WCF Client side behaviour to detect when authentication needs to be done, make the appropriate requests to MS Online STS, attach the cookies and then allow the SharePoint service request proceed as normal.

To use the ClaimsHelper, first set up the Uris of the SharePoint resources you want to access and the corresponding username/passwords using a CredentialCache and set it to the MsOnlineClaimsHelper.CredentialCache.

            CredentialCache credentialCache = new CredentialCache();
            credentialCache.Add(_hostUri, “Basic”new NetworkCredential(“peter.reid@kloud.com.au”“blah”));
            MsOnlineClaimsHelper.CredentialCache = credentialCache;
Then there are two ways to use this library. Either manually request the cookies or attach to the outgoing request like this:

            UserProfile.UserProfileServiceSoapClient userProfile = new UserProfile.UserProfileServiceSoapClient(“UserProfileServiceSoap”, _hostUri + “_vti_bin/userprofileservice.asmx”);
            using (new OperationContextScope(userProfile.InnerChannel))
            {
                _helper.AddAuthCookies(OperationContext.Current);
                UserProfile.PropertyData[] data = userProfile.GetUserProfileByName(userName);
                return Guid.Parse(GetPropertyValue(data, “UserProfile_GUID”));
            }
Or automatically do the same by adding the WCF client side behaviour like this:

 <!--use behaviour to implement authentication-->
    <behaviors>
      <endpointBehaviors>
        <behavior name="CookieBehavior">
          <CookieBehaviorExtension/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <extensions>
      <behaviorExtensions>
        <add name="CookieBehaviorExtension" type="ClaimsHelper.CookieBehaviourExtension, ClaimsHelper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <client>
      <endpoint behaviorConfiguration="CookieBehavior" binding="basicHttpBinding" bindingConfiguration="BasicBinding"
               contract="UserProfile.UserProfileServiceSoap" name="UserProfileServiceSoap" />
    </client>

Which enables you to use a standard web service client call knowing that the authentication conversation is happening under the hood by the WCF client side behaviour.

            UserProfile.UserProfileServiceSoapClient userProfile
                    = new UserProfile.UserProfileServiceSoapClient("UserProfileServiceSoap", _hostUri + "_vti_bin/userprofileservice.asmx");
            UserProfile.PropertyData[] data = userProfile.GetUserProfileByName(userName);
            return Guid.Parse(GetPropertyValue(data, "UserProfile_GUID"));

Check out the code
We will use this library and build on this functionality in later blogs that address some other issues when moving to SharePoint Online.