dreadwail
dreadwail

Reputation: 15410

Install SSL certificate programmatically using Microsoft.Web.Administration

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?

Upvotes: 13

Views: 12978

Answers (7)

Murphybro2
Murphybro2

Reputation: 2699

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;

Upvotes: 1

Prof Von Lemongargle
Prof Von Lemongargle

Reputation: 3768

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

Upvotes: 0

Hames
Hames

Reputation: 1789

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.

Upvotes: 0

Chris S
Chris S

Reputation: 65456

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

Upvotes: 7

Helephant
Helephant

Reputation: 17028

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

Upvotes: 7

Carlos Aguilar Mares
Carlos Aguilar Mares

Reputation: 13601

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

Upvotes: 7

Taylor Bird
Taylor Bird

Reputation: 8017

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

Upvotes: 2

Related Questions