简体   繁体   中英

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?

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

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

As Helephant's answer is the best if you need the certificate hashes (ie 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

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

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;

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM