Updating a WCF Azure Service to SSL and Client Certificate authentication


 

I created a WCF Service in Azure and started to use it, it started very simple but we added new features that need to be protected, so I started the journey of taking my simple WCF Service and configure it to use SSL and client authentication, one of the requirements was minimal downtime, so I need a transition period where both endpoints were available (Secure and Non-Secure) until I was sure that all the clients were configured properly.

I used as base this article “Using Certificate Based Authentication to Consume a Windows Azure WCF Service from SharePoint 2010” from MSDN and did a set of needed adjustments to make it work in my environment.

Configure the Server

1. Add the new certificate to the Web Role

image

2. Add the new SSL endpoint

image

With this we have a new SSL secured endpoint, but any client can use it

Now lets configure the service , to do so you need to modify the web.config, in my case I was using httpBinding, I added two bindings under <system.serviceModel> the NormalBinding to make explicitly the non secure endpoint and the new SecureBinding

<system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="NormalBinding" />
        <binding name="SecureBinding">
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>

And now set the binding configuration to my service, both of them the Secure and the Normal to ensure both are available

    <services>
      <service name="MyWebRole.MyService">
        <endpoint binding="basicHttpBinding" bindingConfiguration="NormalBinding" contract="IService" />
        <endpoint binding="basicHttpBinding" bindingConfiguration="SecureBinding" contract="IService" />
      </service>
    </services>
Under <Configuration> configure the SSL negotiation 
  <system.webServer>
    <security>
      <access sslFlags="SslNegotiateCert" />
    </security>
  </system.webServer>

You can try to debug or deploy the Service and is very likely you will hit my major issue

“The SSL settings for the service ‘SslRequireCert’ does not match those of the IIS ‘None’”

To fix that you need to unlock the web.config, to do it modify the ServiceDefinition.csdef and add a task

  <WebRole name="BeaconServiceWebRole" vmsize="Small">
    <Startup>
      <Task commandLine="Startup.cmd" executionContext="elevated" taskType="simple">
      </Task>
    </Startup>

 

Create a Startup,cmd file in your web role and add this

%APPCMD% unlock config /section:system.webServer/security/access

Try to debug and deploy and should work

You will notice that those instructions are already in the article that I mention at the beginning, I guess the Azure SDK has changed since then and now you need to use the environment variable %APPCMD% to call the configuration, to be able to debug that I did a couple of things

  • Add this to the Startup.cmd
%APPCMD% set config -section:system.webServer/httpErrors -errorMode:Detailed 
It will allow you to see the detail of the errors, which will help you to understand the underlying issue
  • Debugging locally you can add logging to your startup.cmd commands so you can see if they are successful, the quick way is to send it to a file like
%APPCMD% unlock config /section:system.webServer/security/access >> C:\logging\unlock.log

With that information I was able to figure out that the unlock wasn’t being executed and found the new way to do it with the %APPCMD% variable

 

Configure the client

Modify the app.config or web.config of the client to add the new configuration, you will need a new behavior, binding configuration and update the endpoint to use https my config looks like this

 

  <system.serviceModel>
    <behaviors>
        <endpointBehaviors>
          <behavior name="CertBehavior">
            <clientCredentials>
              <clientCertificate findValue="REplacewihtyourownthumbprintXXXXXXXXXXXX"
                storeLocation="LocalMachine" x509FindType="FindByThumbprint" />
            </clientCredentials>
          </behavior>
        </endpointBehaviors>
      </behaviors>
      <bindings>
            <basicHttpBinding>
                <binding name="secureByCert">
                    <security mode="Transport">
                      <transport clientCredentialType ="Certificate" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://myservice.cloudapp.net/service.svc" binding="basicHttpBinding"
          bindingConfiguration="secureByCert" 
          behaviorConfiguration="CertBehavior" 
          contract="IService" name="securedEndpoint" />
        </client>
    </system.serviceModel>

For web sites running in IIS you need to ensure the user in the app pool have permissions , usually the IIS applications run under Network Service to grant permissions to that account:

  1. Open MMC.EXE
  2. Added the certificate snap-in for local computer
  3. Locate your certificate
  4. Right Click -> All Tasks -> Manage Private Keys
  5. Add the <LocalMachine>\NETWORK SERVICE account and assigned Read rights.

Remove the Non Secure Endpoint

When all the clients are updated you can remove the non secure endpoint, to do that just remove the non secure endpoint

        <endpoint binding="basicHttpBinding" bindingConfiguration="NormalBinding" contract="IService" />
The new configuration will be , with only the secure endpoint available
    <services>
      <service name="MyWebRole.MyService">
        <endpoint binding="basicHttpBinding" bindingConfiguration="SecureBinding" contract="IService" />
      </service>
    </services>

To avoid any downtime you can publish this new configuration to Staging in Windows Azure and swap it , the details are here Staging an application in Windows Azure


Comments (2)

  1. Hima Sindhu Kacham says:

    Hi Jaime,

    I've tried all the steps you mentioned above and deployed the service to Azure.

    I then tried to build an anonymous client by attaching a certificate to this call and I was able to successfully make a call to the service and get a response back.

    But, I want to allow only the clients whose certificates are already imported to the certificate store of my server. How do I block an anonymous client with an anonymous certificate from making a call?

    Any help is appreciated! Thanks in advance!

    Thanks and Regards,

    Hima Sindhu Kacham.

  2. Matt Ratliff says:

    If you're having issues getting this to work try this:

    %windir%system32inetsrvappcmd unlock config /section:system.webServer/security/access >> "%TEMP%StartupLog.txt" 2>&1

    EXIT /b 0

    The %TEMP% directory is something like this C:Resourcestemp{guid}.{role name}RoleTemp

    If the error message says "The filename, directory name, or volume label syntax is incorrect." then you probably created your .cmd file in Visual Studio. VS for some reason adds a system character at the beginning of the command line that causes issues. Try opening the file in Notepad, copying and re-pasting the text then save it. That fixed it for me.