• Multiple SSL certificates on Windows Azure Cloud Services

    by  • March 4, 2013 • English, Windows Azure • 32 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?


    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);

    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

    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:


    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://1drv.ms/1pQzY1p

    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

    32 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!

        • November 5, 2013 at 4:54 pm

          “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.”

          I have this problem too. The bindings are good in IIS but when I run the command “netsh http show sslcert”, the first binding has no host and SNI so my second website use this first certificate.

          How to deal with that? I can delete the binding at startup?


    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


      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,


      • 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];

      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: 0x80040154 (REGDB_E_CLASSNOTREG)).

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

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

    8. October 8, 2013 at 3:02 am

      Hi Vitor; fantastic resource! The code worked exactly as presented and the correct bindings are applied in IIS…

      I have two domains (A.com and B.com) – I applied the approach above for B.com and A.com was configured normally through ServiceDefinition:

      Even though B.com in IIS has the correct http://www.B.com certificate applied to it, it is served using the http://www.A.com certificate?

      Both certificates have been uploaded to the cloud service. We are running a 2 instance deployment. Both instances appear to have correct configuration as far as IIS is concerned.

      Wondering if this has to do with the endpoints defined for the Azure load balancer through the ServiceDefinition and whether A.com and B.com have to both be configured through SNI for this to work?

    9. February 16, 2014 at 1:10 am

      Thank you for the awesome tutorial!

      I’ve chosen a slightly different, simpler solution building on the one outlined here.

      Certifications are uploaded through the Management Portal to the certification store, not deployed with the application. Then for each of the certificates the necessary entries are added to the service definition and config files (but no bindings; also there will be a single SSL endpoint on port 443, using one of the certificates). In the role start all of the certificates are read from the store and bindings are automatically generated for them, based on the certificate’s subject names.

    10. February 16, 2014 at 10:45 pm

      Also, adding to what Will said: the funny thing is that your bindings, although correctly set up, wont work unless you add a default catch-all binding from the service defintion (i.e.: ). You MUST add this, and this will create a catch-all binding. Now you should remove that from the role startup. But if this is not set up first then all the other bindings won’t work! So yes, you have to add, then remove this.

      Also I noticed that the site stops after the bindings are set up so if this also happens for you make sure to start the site (also through ServerManager) after all the bindings are saved.

    11. Jim
      April 18, 2014 at 6:01 am

      Hi Vitor,

      Thanks for the well written and easy to follow sample code. It saved me a ton of time. I do think I found one bug in the code I wanted to note. On line 78 of CertificateService.cs I think the line should have been:

      certHash = CertificateManager.InstallCertificate(temporaryFile, certificateBinding.CertificatePassword);

      instead of

      CertificateManager.InstallCertificate(temporaryFile, certificateBinding.CertificatePassword);

      The problem this was causing for me is that on the first deploy to a new environment before the certificate existed in the store I was getting the binding added correctly, but the binding wasn’t linked to any certificate. Hopefully that helps if anyone else hits a problem on initial deploy to a new environment. Thanks again!

    12. June 26, 2014 at 1:00 pm

      Excellent article – we’ve been looking for a way to do this.

    13. BC
      August 1, 2014 at 9:38 pm

      Any chance of re-posting the code? It is not available on one drive any more.

    14. Jon Deo
      August 13, 2014 at 3:36 pm

      the article is pretty nice and exactly what I need.

      Could you please upload the source again?

      Thanks in advance.

    15. Joe
      August 19, 2014 at 6:06 pm

      Hi Vitor,

      I’m having trouble getting this to work. I’m finding that no matter what I do with SSL bindings in my WebRole.OnStart, all SSL connections to my web role are served with the certificate I’ve configured in my .csdef/.cscfg files. Even if I remote into the VM and manually remove the cert from IIS! I assume the Azure fabric must be doing this.

      If I remove the certificate from my csdef/cscfg files, then I can’t get any traffic on port 443 to hit my web role at all.

      Do you have any idea what could be wrong?

      Thanks much

      PS – the article’s link to the complete source code is broken. Can you please re-upload? Thanks.

    16. Christoph Herold
      August 26, 2014 at 3:49 pm

      Hi Vitor! Thanks for your solution. A colleague of mine checked it out a few months back, and now we actually need to implement it. I tried to download your sample code again, but the link isn’t working anymore. Can you maybe post an updated link to your solution? Thanks again!

      • September 5, 2014 at 5:01 am

        I apologize for the broken link. I updated the article and this is the new one: http://1drv.ms/1pQzY1p

        • Christoph Herold
          September 5, 2014 at 9:20 am

          Cool, thanks! I actually managed to get hold of the code from my colleague. Luckily, he still had it on his machine. The others probably weren’t so lucky, though. So, thanks again for sharing your work!

    17. Cameron
      August 28, 2014 at 1:40 pm

      Complete source code seems to be missing

    18. Pierpaolo
      September 1, 2014 at 4:28 pm

      The OneDrive link to download the complete source-code don’t work, is possible to have a working one ?

    Leave a Reply

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