Manage IIS HTTPS Bindings with Powershell?

Howdy, I have a few customers using client certificates for HTTPS client authentication (to validate that the channel can be trusted, not to authenticate to the application fully), but for whom the CRL is only published via LDAP… and the LDAP server isn’t accessible to their IIS host.

The following instructions are clear and solve the problem, allowing us to disable certificate checking only on the HTTPS site in question: Disable Client Certificate Revocation (CRL) Check on IIS | Microsoft Docs

To recap, the commands are:

netsh http show sslcert

netsh http delete sslcert ipport=0.0.0.0:443

netsh http add sslcert ipport=0.0.0.0:443 certhash=40db5bb1bf5659a155258d1d007c530fcb8996c2
appid={4dc3e181-e14b-4a21-b022-59fc669b0914}
certstorename=My verifyclientcertrevocation=disable

but they use netsh to edit the bindings, which… doesn’t script well inside powershell. :slight_smile:

The problem is that the “certhash” and “appid” values can be dynamic (IIS is always that AppID, but the certhash will definitely change on each binding). So I want to script “list the bindings, capture the hash of the cert used for this binding, and insert it into the last command to create the new binding.”

The IISAdministration 10 cmdlets for powershell include “Get-IISSiteBinding”, “Remove-IISSiteBinding”, and “new-IISSiteBinding”, which appear to be the same commands under the hood as the netsh http commands, so all should be good, right?

New-IISSiteBinding does not have a parameter for “VerifyClientCertRevocation”: New-IISSiteBinding (IISAdministration) | Microsoft Docs

So, anyone have any ideas how I can script this in PowerShell? FWIW 100% of the systems I need to edit are 2012 R2 or 2016.

You can just use netsh for all or parts of what you are doing, and this approach is a common thing when PowerShell does not have something directly in it for X or Y. So, falling back to an older cmdline tool or popping into .Net namespaces is a thing.

All this does is set this Reg, so you could also use the Registry cmdlets, to set it.

Registry: HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters\SslBindingInfo Dword : DefaultSslCertCheckMode Value : 1

Value Meaning

0 Enables the client certificate revocation check
1 Client certificate is not to be verified for revocation.
2 Only cached certificate revocation is to be used
4 The DefaultRevocationFreshnessTime setting is enabled
0x10000 No usage check is to be performed


Also of note, With IIS there are 2 new SSL bindings viz. SNI Bindings and CCS Bindings.

Here’s what I use when I build IIS servers.

New-WebBinding -Name "Default Web Site" -Protocol https
$Binding = Get-WebBinding -Protocol https
$Cert = Get-ChildItem -Path Cert:\LocalMachine\My | where FriendlyName -Like "CERTNAMEFILTER"
$Binding.AddSslCertificate($Cert.GetCertHashString(), "My")

 

 

@Darwin thanks, but how would I disable certificate checking with your method? I like it better than random registry keys, if I could figure it out. The problem with pulling the cert from the local store, rather than the original binding, is that in auto-enroll environments, a computer may have 2 or more certificates in the local store (My lab root CA has 3: 1 for the LDAP cert, one for the CA cert, and one for the CA HTTPS /certsrv website cert.

 

@postanote The documentation isn’t clear - is this the structure for all bindings, or just the registry key for the default binding? IE: I can enumerate bindings in the registry and change the key underneath them appropriately? I won’t be able to get on a test box till Friday to look.
And yes, I could use netsh to set the value, but I need to read the correct certificate out, in order to push it back, and netsh doesn’t generate output I have been able to successfully capture in powershell variables, so that I can pull the right thumbprint with select-string.

 

Thanks!

My code is just applying the cert I got from our corporate CA to the Default Web Site. If you know the name of the cert (CERTNAMEFILTER in my code), you can apply it to a particular website instead of “Default Web Site.” But I’m not sure how auto-enroll will impact that.

Autoenroll isn’t the only case where you may have problems here, it just is a form of the generic problem “how do you know precisely which certificate to use for any given site, programatically?”

Here are the certs, and some properties of them, for example, from my lab Windows 2016 DC, which is also my lab’s enterprise root CA and hosts the certificate autoenrollment HTTPS site. You’ll see there are 2 certs with the same CN - one of them is the LDAP autoenrolled certificate (Template = Domain Controller), and one is the IIS website autoenrolled certificate (Template = modified Web Server (2003+ version)).

So, again, in your line 3: how do I know which cert to use? The advantage of the “netsh http show sslcert” is that I can filter on the IIS binding in question, pull the precise correct thumbprint, and reuse that same thumbprint, without needing to know anything else about the certificate. The problem is that I can’t capture the netsh output.

In a single test so far, @Postanote 's method changes the default behavior when a new binding is created, so I’d need to modify that, then delete the old binding, create the new, and then set the default value back to the original, to preserve security on future websites on the same server.

PS C:\Users\rob-adm> gci cert:\localmachine\my | foreach { 
  if ($_.HasPrivateKey) { 
    Write-Host "`n" $_.Subject;
    foreach ($use in $_.EnhancedKeyUsageList) { 
      write-Host $use ;
    };  
    foreach ($ext in $_.Extensions) { 
      $ext | Select-Object -property *;
    } ;   
  };
}

 CN=dc01.test.corp
Server Authentication (1.3.6.1.5.5.7.3.1)

 CN=test-DC01-CA, DC=test, DC=corp

 CN=dc01.test.corp
Client Authentication (1.3.6.1.5.5.7.3.2)
Server Authentication (1.3.6.1.5.5.7.3.1)

 CN=dc01.test.corp, OU=Domain Controllers, O=TEST, L=New York, S=NY, C=US
Server Authentication (1.3.6.1.5.5.7.3.1)
Critical Oid                              RawData
-------- ---                              -------
   False System.Security.Cryptography.Oid {30, 18, 0, 87...}
   False System.Security.Cryptography.Oid {48, 10, 6, 8...}
    True System.Security.Cryptography.Oid {3, 2, 5, 160}
   False System.Security.Cryptography.Oid {4, 20, 125, 255...}
   False System.Security.Cryptography.Oid {48, 18, 130, 16...}
   False System.Security.Cryptography.Oid {48, 22, 128, 20...}
   False System.Security.Cryptography.Oid {48, 129, 245, 48...}
   False System.Security.Cryptography.Oid {48, 130, 1, 4...}
   False System.Security.Cryptography.Oid {3, 2, 1, 134}
    True System.Security.Cryptography.Oid {48, 3, 1, 1...}
   False System.Security.Cryptography.Oid {4, 20, 127, 193...}
   False System.Security.Cryptography.Oid {2, 1, 0}
   False System.Security.Cryptography.Oid {30, 32, 0, 68...}
   False System.Security.Cryptography.Oid {48, 20, 6, 8...}
    True System.Security.Cryptography.Oid {3, 2, 5, 160}
   False System.Security.Cryptography.Oid {48, 105, 48, 14...}
   False System.Security.Cryptography.Oid {4, 20, 186, 80...}
   False System.Security.Cryptography.Oid {48, 22, 128, 20...}
   False System.Security.Cryptography.Oid {48, 129, 245, 48...}
   False System.Security.Cryptography.Oid {48, 130, 1, 4...}
   False System.Security.Cryptography.Oid {48, 51, 160, 31...}
    True System.Security.Cryptography.Oid {3, 2, 5, 160}
   False System.Security.Cryptography.Oid {48, 10, 6, 8...}
   False System.Security.Cryptography.Oid {48, 105, 48, 14...}
   False System.Security.Cryptography.Oid {30, 18, 0, 87...}
   False System.Security.Cryptography.Oid {4, 20, 7, 227...}
   False System.Security.Cryptography.Oid {48, 22, 128, 20...}
   False System.Security.Cryptography.Oid {48, 129, 245, 48...}
   False System.Security.Cryptography.Oid {48, 130, 1, 4...}