• Multiple SSL certificates on Windows Azure Cloud Services

    by  • March 4, 2013 • English, Windows Azure • 13 Comments

    A very common need for e-commerce platform developers is to use multiple SSL certificates for a single IIS Web Site.

    It happens mainly when the e-commerce platform is multi-tenant, in other words, a single IIS aplication/website contains different virtual stores for different clients/companies.

    So, to illustrate our problem, lets think about this scenario:

    We are an e-commerce platform provider with 4 clients, each one with their respective plan and urls:
    -Client 1 (Premium): www.store1.com
    -Client 2 (Silver): www.store2.com
    -Client 3 (Silver): www.store3.com
    -Client 4 (Free): www.store4.com

    The Premium plan offers a dedicated environment to the client´s store, with support for our clients own domains for both HTTP and HTTPS.
    The Silver plan offers a shared environment to the client´s store, with support for our clients own domains for both HTTP and HTTPS.
    The Free Plan offers a shared environment to the client´s store, with support for our clients own domains only for HTTP. The HTTPS is one of our sub-domains (ex: https://store4.ecommerce.com).

    Our application is already prepared to load the corrrect store according to the requested domain. If we receive an request for the domain www.store1.com we will load the Store 1 database and show the Store 1 to the user.

    How to set it up on Windows Azure?

    One way is to setup an enviroment similar as we would do on-premises, creating virtual machines, each one with their respective IP addresses and DNSs, using CNAMEs, wildcard SSL certificates and so on (IaaS scenario).

    But then we would miss one of the greatest powers of Windows Azure, the platform as a service of the Cloud Services (PaaS scenario).

    Using Cloud Services with Multiple SSL certificates

    To setup our scenario using Cloud Services and obtain all the benefits of automation and scalability, we will do the following:

    For Premium clients, we will treat each client store as an isolated application, each one having its own separated Cloud Service. This way we deliver the “dedicated environment” that we offer in the Premium plan. The process do use SSL on it is pretty straighfoward, we just need to create an HTTP endpoint with the respective SSL certificate. To use custom domains, all you need is to configure a CNAME record on your DNS server.

    For the Free clients, the process is even simpler. We will have only one application/Cloud Service that will handle all stores in the Free plan. All we need is to create a single HTTPS endpoint with a  SSL suubdomain wildcard certificate (in our case *.ecommerce.com). For each store, as we offer custom HTTP domains, we will need to configure a CNAME record in the DNS server. Our application, however, will have the intelligence to redirect the user to the respective secure subdomain everytime we need to use HTTPS (for exemple, in the checkout pages).
    Therefore, each store would have to different urls, such as: http://www.store4.com for HTTP and https://store4.ecommerce.com for HTTPS.

    And finally, for Silver clients, we have an special situation. We need to have all stores in the same shared application/CloudService and yet guarantee that every store has its own HTTP and HTTPS domain. To make it happpen, each store will have a CNAME record on the DNS server like we did before.
    For instance:
    www.store2.com     CNAME     ecommerce.cloudapp.net
    www.store3.com     CNAME     ecommerce.cloudapp.net

    And now, for each store to have its own secure domain, we need to have a SSL certificate for each one.

    The problem appears when we need to create the HTTPS endpoints, because it’s not possible to add more than one certificate for the same endpoint.

    One solution would be to use agregatted certificates, where a single certificate file contains dozens of certificates for different domains, but this option might be more expensive and you will have to do a new deploy everytime you need to add a new domain in the certificate file.

    Another solution, would be to create HTTPS endpoints in different ports, for instance, 443, 444, 445, but this might appear weird to the end user and might not work because of firewalls blocking non-standard ports, and there is also a limit of 25 external endpoints.

    And finally, there is a way of creating a single HTTPS endpoint in port 443 and using a new IIS 8.0 feature that allow us to create multiple HTTPS bindings on the same port for different domains. This feature is based on a SSL extension called Server Name Indication (SNI), in which the great majority of webbrowsers supports (in practice, only browsers running on Windows XP don’t). On the server side, SNI is only available on IIS 8 of Windows 8 or Windows Server 2012.

    To make it work, what we need to do is to install the certificates and create the bindings for the IIS website, flagging the SNI option. You can read a detailed explanation of this IIS feature and manual how-to of this configuration procedure here.

    But how to make it happen in a Cloud Service, which is stateless and that at every deploy, or when needed, the Windows Azure will “erase” the IIS configuration?

    Solution

    We need to automate the instalation of the certificates and and the creation of the IIS bindings in the startup process of our Web Role running in the Cloud Service.

    Installing a certificate

    We can install a certificate with only 5 lines of C# code

    var newCert = new X509Certificate2(“certificate.pfx”, “certificatePassword”, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
    var readWriteMyStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    readWriteMyStore.Open(OpenFlags.ReadWrite);
    readWriteMyStore.Add(newCert);
    readWriteMyStore.Close();

    Creating the HTTPS binding with the SNI on

    And with 5 more lines of code we create the binding (add the Microsoft.Web.Administration package via Nuget).

    Attention: SNI is only available on IIS 8 of Windows 8 or Windows Server 2012.

    var serverManager = new ServerManager();
    var site = serverManager.Sites[0];
    var binding = site.Bindings.Add(“:443:www.store2.com”, newCert.GetHash(), “My”);
    binding.SetAttributeValue(“sslFlags”, 1); //enables the SNI
    serverManager.CommitChanges();

    Remember that to make this code work, the role must run under elavated mode:
    Write the following option in your ServiceDefinition.csdef, inside <WebRole>
    <Runtime executionContext=”elevated” />

    The Complete Solution

    That would be enough to add the code in your Role.OnStart and setup your custom secure domains everytime the role starts.

    However, to make sure the role doesn’t need to be restared everytime que we have a new certificate, I have create a simple “job” that checks from time to time if we it needs to install certificates and create bindings.

    The solution is flexible enough to be incorporated in your code and you can store your certificates in a blob container or in a SQL database, for instance. All you need is to create your own provider that implements the ICertificateBindingStorage interface.

    In the code sample,  I have a container called  “certificates” that contains the certificates as blobs and their metadata contains additional information to install the certificate and create the binding.

    An example of a certificate in the blob storage:

    http://vitorciaramella.blob.core.windows.net/certificates/store2.pfx

    Metadata:
    PortNumber:     443
    HostHeader:      www.store2.com
    CertificateSubject:     CN=www.store2.com
    CertificatePassword:       Pa$$w0rd

    This way, everytime I have a new store (in the Silver plan), I only need to create a CNAME record and upload the respective certificate with the metadata to the blob container. A minute later the store will be ready to be used with the custom SSL domain.

    You can download the complete source-code here: http://sdrv.ms/Zasjbp

    And if you wish to know more about certificates and SNI, check this post: http://blogs.msdn.com/b/kaushal/archive/2012/09/04/server-name-indication-sni-in-iis-8-windows-server-2012.aspx

    Questions? Suggestions? -> Please leave your comments :-)

    Vitor Ciaramella

    13 Responses to Multiple SSL certificates on Windows Azure Cloud Services

    1. Will
      March 19, 2013 at 8:22 am

      After a few days of tinkering (ok maybe a week), I finally got this to work.

      1) You can’t do this if you’re running Windows 7. You need to be running Windows 8 or Windows Server 2012 (I think) in order to Deploy your cloud service to Windows Server 2012 O/S, which is required for the SNI to work. $160 later…

      2) Don’t try and change the method of connecting to your storage account to something else. It needs to use that “FromConfiguration” method, and not the method that loads from a connection string which, in my case, isn’t/wasn’t available when the startup and service code runs.

      3) It’s really a good idea to test and confirm that things are working 100% properly locally, using IIS Express, before even bothering with an upload to the cloud. Using forged host headers and a few other tricks, you can create a simple test environment. However, I did find it useful to add logic to “upsert” bindings when debugging, otherwise there was no easy way to clear out local bindings in IIS Express (that I could find). At some point during testing, my hashes were set to null, so this upserting was crucial for me. I did disable however once things got settled.

      4) You must configure an HTTPS endpoint for port 443 using a valid working certificate which also must be uploaded into the Portal for that service in order to deploy. However, for me, a “catch-all” binding was created in IIS for each of my instances for that secure endpoint using the default certificate from my project which prevented any of my other domains from being served the proper certificate, even though the certs were there and configured properly (thanks to the service). The dynamic certs were just lower in the bindings order, as it were.

      My solution, for lack of better options, was to look for that specific “catch all” SSL binding (SslFlags != SslFlags.Sni && Protocol == “https”) and remove it while we are checking for new certs to install. So far, it works!

      • March 20, 2013 at 3:47 am

        Thanks for the feedback, Will. I’m sorry you spent so much time on item 1. I mentioned in the post about IIS 8 but I agree it’s not clear enough. I’ll update it…. And please share your custom solution if possible.

        • Duncan
          May 13, 2013 at 2:31 am

          I am trying to implement your solution. I have been through the process of updrading to Azure tools V2.0 and upgrading the Azure OS to 2012. The code seems to work fine, except when we set the sslFlags to 1 i get an exception thrown :-

          System.ArgumentException: Value does not fall within the expected range.
          at Microsoft.Web.Administration.Interop.IAppHostProperty.get_Value()
          at Microsoft.Web.Administration.ConfigurationElement.GetPropertyValue(IAppHostProperty property)

          Can you help with some direction ?

          • Duncan
            May 15, 2013 at 8:42 pm

            The Host Name can not be *.X.com but x.com .. found the issue thanks.

          • May 20, 2013 at 2:14 pm

            Hi Duncan, sorry about the delay in answering this comment.
            You have already found the solution: the Host Name can not be *.X.com but x.com.
            Thank you for the feedback!

    2. Seamus Minogue
      June 11, 2013 at 2:50 pm

      Thanks for this, an extremely useful article

    3. Shez
      July 4, 2013 at 7:02 am

      Hi

      Vitor, can i use SNI feature on IIS 8 on Windows 7 or Windows Server 2008 R 2 ? kindly let me know about it, thanks in advance,

      regards,

      • September 19, 2013 at 12:58 pm

        AFAIK, it only works in IIS8 on Windows Server 2012 or in newer versions.

    4. August 11, 2013 at 4:54 am

      This is the sort of thing I have been hunting around for. Thanks for posting a link to it from the Azure docs (http://msdn.microsoft.com/en-us/library/windowsazure/ff795779.aspx) :)

      Our setup, which we’re hoping to publish live in the next couple of weeks, is quite similar but not in the ecommerce/shopping space. However we’ll be multi-tenant and wish to run a single ASP.Net app allowing for clientA.ourawesomesite.com and client.ourawesomesite.com to work as well as “vanity” domains such as awesome.clientA.com which are CNAME aliases to our site. I knew multi-SSL certificate via host headers was possible in IIS 8 but the Azure docs aren’t very forthcoming with configuring them.

      Anyway, thanks for the detailed writeup and sample code. I’ll be pouring over it with interest in the coming days. Cheers, Ian

    5. barf
      September 17, 2013 at 10:28 pm

      you say “On the server side, SNI is only available on IIS 8 of Windows 8 or Windows Server 2012.” but that is not true, Apache and other HTTP servers support SNI and it’s actually easier to get going on Linux and Apache anyway.

      • September 19, 2013 at 12:49 pm

        Hi Barf, you are right. I wrote this phrase with only IIS in mind…
        About Linux and Apache being “easier to get going”, it depends on the current infrastructure and technical skills…

    6. Slawek
      September 18, 2013 at 10:37 am

      var serverManager = new ServerManager())
      var site = serverManager.Sites[0];

      Error:
      Retrieving the COM class factory for component with CLSID {2B72133B-3F5B-4602-8952-803546CE3344} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0×80040154 (REGDB_E_CLASSNOTREG)).

    7. Slawek
      September 18, 2013 at 1:45 pm

      I had to reinstall IIS, and update system to manage with it

    Leave a Reply

    Your email address will not be published. Required fields are marked *