13

So the Microsoft.Web.Administration API is very easy to use to create HTTP and HTTPS bindings for sites:

using (ServerManager manager = new ServerManager())
{
    Site site = manager.Sites[siteName];
    site.Bindings.Clear();
    site.Bindings.Add("*:80:", "http");
    site.Bindings.Add("*:443:", "https");

    manager.CommitChanges();
}

But the HTTPS binding is pretty meaningless without the SSL certificate. How can I go about programatically choosing a cert file and using it with the HTTPS binding, using this API?

jww
  • 97,681
  • 90
  • 411
  • 885
dreadwail
  • 15,098
  • 21
  • 65
  • 96
  • The accepted answer is not a quality answer. It lacks code and tells you to go look somewhere else. it is closer to a comment than an answer. I think you should consider accepting another answer. – jww Apr 15 '18 at 20:53

7 Answers7

7

There is a method overload for adding Bindings that will add the certificate to HTTP.sys correctly, see: http://msdn.microsoft.com/en-us/library/bb355650(v=VS.90).aspx

Optionally you can actually set the binding settings:

binding.CertificateHash and binding.CertificateStoreName and when commiting it will register correctly with HTTP.sys: http://msdn.microsoft.com/en-us/library/microsoft.web.administration.binding_properties(v=VS.90).aspx

Carlos Aguilar Mares
  • 13,411
  • 2
  • 39
  • 36
  • 5
    I know this is an old question but I have noticed that if I set the `CertificateHash` and `CertificateStoreName` values for the binding and call `CommitChanges` everything supposedly succeeds but the binding is not set. Not sure why that seems to happen. – Fizz Dec 06 '16 at 14:15
  • 6
    @Fizz: I had to manually set `binding.BindingInformation = binding.BindingInformation;` (even though it looks like this doesn't make sense, this causes IIS to actually bind the certificate). – Devator Apr 24 '18 at 14:21
  • 1
    @Devator got any clue why this works? This also worked for me although you are right it doesn't make sense. – Umair Malhi May 09 '18 at 04:41
  • *IMPORTANT:* as noted by @Fizz you have to manually set the BindingInformation back to itself to get the CertificateHash and CertificateStoreName (among other properties) to actually be set. This seems like a bug to me and drove me CRAZY until i realized what Fizz was telling us to do. – mknopf Nov 26 '18 at 20:19
  • I attempted this and got HRESULT 0x80070520 A specified logon session does not exist. It may have been terminated. – Thomas Harris Feb 06 '20 at 13:31
  • @ThomasHarris Did you get a fix for this? I sometimes get that message.. sometimes not... :/ – Murphybro2 Feb 21 '20 at 13:23
  • No I gave up and just used regular http for local connections, used win-acme to get an ssl cert for external connections on IIS. – Thomas Harris Mar 09 '20 at 10:33
7

The Bindings.Add() method has an overload for passing in the SSL certificate. If you already have a SSL certificate, you can select it from the SSL certificate store like this:

var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly);
var certificate = store.Certificates.Find(X509FindType.FindByThumbprint, the thumbprint for the key", true);

var site = _mgr.Sites[name];
site.Bindings.Add("*:4043:", certificate[0].GetCertHash(), "MY");

Once you have run the code, you can check that it has worked by running this from the command line:

netsh http show sslcert
Helephant
  • 16,738
  • 8
  • 39
  • 36
7

As Helephant's answer is the best if you need the certificate hashes (i.e. multiple IPs on a single machine with various SSL certs), you'll need to know how to get the certificates/hashes. The few lines below demonstrate how to find the information out, as the MSDN documentation is so poor for this subject.

You can't remotely update an SSL binding using ServerManager.OpenRemote() - there appears to be a bug with this. Appcmd won't help you either.

If you want to convert a byte string back into a byte array (if you know the hash), here's how.

static void Main(string[] args)
{
    var store2 = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine);
    Console.WriteLine("TrustedPublisher:");
    PrintCerts(store2);
    Console.WriteLine(); 

    Console.WriteLine("MY:");
    store2 = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    PrintCerts(store2);
    Console.WriteLine();

    Console.WriteLine("CertificateAuthority:");
    store2 = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine);
    PrintCerts(store2);
    Console.WriteLine();
}

static string PrintHash(byte[] cert)
{
    StringBuilder builder = new StringBuilder();

    foreach (byte b in cert)
    {
        builder.AppendFormat("{0:x2}", b);
    }

    return builder.ToString();
}

static void PrintCerts(X509Store store)
{
    store.Open(OpenFlags.OpenExistingOnly);
    foreach (var cert in store.Certificates)
    {
        Console.Write("{0} - {1}", cert.FriendlyName, PrintHash(cert.GetCertHash()));
        Console.WriteLine();
    }
}

Example output:

MY:
www.awesomesite.com - cc2b5fc8216a949b58aadc21089c12b2c090f6bd

Community
  • 1
  • 1
Chris S
  • 64,770
  • 52
  • 221
  • 239
2

The namespace doesn't contain an API for this, so you have to use its ConfigurationMethod to invoke an extension to the Win API that performs this function. Something like:

string certificateHash = <hash>
string certificateStore = <storename>  #my, localmachine, etc

ConfigurationMethod method = binding.Methods["AddSslCertificate"];
ConfigurationMethodInstance mi = method.CreateInstance();
mi.Input.SetAttributeValue("certificateHash", certificateHash);
mi.Input.SetAttributeValue("certificateStoreName", certificateStore);
mi.Execute();
Taylor Bird
  • 7,767
  • 1
  • 25
  • 31
  • Actually found out that the API does handle it. See here: http://msdn.microsoft.com/en-us/library/bb355650(VS.90).aspx – dreadwail Mar 03 '11 at 00:30
  • Looks like CarlosAg has the link as well in his answer. – dreadwail Mar 03 '11 at 00:32
  • Now in 2020 is work with IIS v10. My big error, 3 days to trash, is very very important understand "mi.Input.SetAttributeValue("certificateStoreName", certificateStore);" When I make it by my self, if put in "certificateStoreName" the certificate name, and is my big error. The correct is STORE name, not certificate name. – Anton Nikolayevich Jul 17 '20 at 10:05
1

I feel like it's important to highlight Devator's comment in this answer. There appears to be a bug that prevents the certificate changes from taking place and I would never have solved it without seeing this comment.

If you set the binding information to itself, this causes IIS to bind the certificate. A quick example is shown below:

binding.BindingInformation = binding.BindingInformation;

Murphybro2
  • 2,207
  • 1
  • 22
  • 36
0

Adding to Taylor Bird's comment but using powershell:

    ...
Write-Verbose ("Remove old certificate [ {0} ]... " -f $SiteHttpsBinding.GetAttributeValue("certificateHash"))
$BindingMethod=$SiteHttpsBindings.Methods["RemoveSslCertificate"]
$BindingMethodInstance=$BindingMethod.CreateInstance()
$BindingMethodInstance.Execute()
Write-Verbose ("Add new certificate [ {0} ]..." -f $AfterThumbprint)
$BindingMethod=$SiteHttpsBindings.Methods["AddSslCertificate"]
$BindingMethodInstance=$BindingMethod.CreateInstance()
$BindingMethodInstance.Input.SetAttributeValue("certificateHash", $AfterThumbprint)
$BindingMethodInstance.Input.SetAttributeValue("certificateStoreName", "My")
$BindingMethodInstance.Execute()
...

The above code snippet is useful for updating certificates without needing to remove the whole binding. I've used it for adding and updating ssl bindings on local and remote machines.

Thanks Mr. Bird for supplying the WinAPI reference.

Hames
  • 1,569
  • 1
  • 9
  • 6
0

If you landed here and are wanting to use PowerShell to accomplish this, I put a fairly complete answer here. The simplest answer, if you know the hash of the certificate that you want to use is to do this:

cd IIS:\SslBindings
get-item Cert:\LocalMachine\My\XFX2DX02779XFD1F6F4X8435A5X26ED2X8DEFX95 | New-Item 0.0.0.0!443
Community
  • 1
  • 1
Prof Von Lemongargle
  • 3,658
  • 31
  • 29