Skip to content

Commit 16c4d5f

Browse files
authored
Merge pull request #152 from Keyfactor/76007-CSP_formatted_wrong_when_null
76007 CSP formatted wrong when null
2 parents b5e9446 + 0976981 commit 16c4d5f

File tree

4 files changed

+55
-156
lines changed

4 files changed

+55
-156
lines changed

IISU/PowerShellScripts/WinCertScripts.ps1

Lines changed: 55 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
# Update notes:
1+
# Version 1.3.0
2+
3+
# Summary
4+
# Contains PowerShell functions to execute administration jobs for general Windows certificates, IIS and SQL Server.
5+
# There are additional supporting PowerShell functions to support job specific actions.
6+
7+
# Update notes:
28
# 08/12/25 Updated functions to manage IIS bindings and certificates
39
# Updated script to read CSPs correctly using newer CNG Keys
410
# Fix an error with complex PFX passwords having irregular characters
511
# 08/29/25 Fixed the add cert to store function to return the correct thumbprint
612
# Made changes to the IIS Binding logic, breaking it into manageable pieces to aid in debugging issues
13+
# 09/16/25 Updated the Get CSP function to handle null values when reading hybrid certificates
714

815
# Set preferences globally at the script level
916
$DebugPreference = "Continue"
@@ -374,160 +381,6 @@ function Remove-KFCertificateFromStore {
374381
return $isSuccessful
375382
}
376383

377-
function New-KFIISSiteBindingOLD {
378-
[CmdletBinding()]
379-
[OutputType([pscustomobject])]
380-
param (
381-
[Parameter(Mandatory = $true)]
382-
[string]$SiteName,
383-
[string]$IPAddress = "*",
384-
[int]$Port = 443,
385-
[string]$Hostname = "",
386-
[ValidateSet("http", "https")]
387-
[string]$Protocol = "https",
388-
[string]$Thumbprint,
389-
[string]$StoreName = "My",
390-
[int]$SslFlags = 0
391-
)
392-
393-
Write-Information "Entering PowerShell Script: New-KFIISSiteBinding" -InformationAction SilentlyContinue
394-
Write-Verbose "Entered New-KFIISSiteBinding with values SiteName: '$SiteName', IPAddress: '$IPAddress', Port: $Port, HostName: '$Hostname', Protocol: '$Protocol', Thumbprint: '$Thumbprint', Store Path: '$StoreName', SslFlags: '$SslFlags'"
395-
396-
$result = $null
397-
398-
# Check for existing binding conflict
399-
$conflicts = @(CheckExistingBindings -DesiredIP $IPAddress -DesiredPort $Port -DesiredHost $Hostname -TargetSiteName $SiteName)
400-
401-
if ($conflicts.Count -gt 0) {
402-
$conflictMessage = "Binding conflict detected with the following existing bindings:`n" + ($conflicts | ForEach-Object { " - Site: $($_.SiteName), IP: $($_.BindingIP), Port: $($_.BindingPort), Host: $($_.BindingHost)" }) -join "`n"
403-
404-
Write-Warning $conflictMessage -InformationAction SilentlyContinue
405-
406-
$result = New-ResultObject -Status Skipped -Code 100 -Step CheckBinding -Message $msg -ErrorMessage $conflictMessage
407-
408-
return $result
409-
}
410-
Write-Verbose "No binding conflicts found for SiteName: '$SiteName', IPAddress: '$IPAddress', Port: $Port, HostName: '$Hostname'"
411-
412-
$searchBindings = "${IPAddress}:${Port}:${Hostname}"
413-
$hasIISDrive = Ensure-IISDrive
414-
Write-Verbose "IIS Drive is available: $hasIISDrive"
415-
416-
if ($hasIISDrive) {
417-
418-
Write-Verbose "IIS Drive is available, using WebAdministration module."
419-
420-
$null = Import-Module WebAdministration
421-
$sitePath = "IIS:\Sites\$SiteName"
422-
if (-not (Test-Path $sitePath)) {
423-
$msg = "Site '$SiteName' not found in IIS drive."
424-
Write-Error $msg -InformationAction SilentlyContinue
425-
$result = New-ResultObject -Status Error -Code 201 -Step FindWebSite -ErrorMessage $msg -Details @{ SiteName = $SiteName; IPAddress = $IPAddress; Port = $Port; HostName = $Hostname }
426-
} else {
427-
$site = Get-Item $sitePath
428-
$httpsBindings = $site.Bindings.Collection | Where-Object {
429-
$_.bindingInformation -eq $searchBindings -and $_.protocol -eq "https"
430-
}
431-
432-
foreach ($binding in $httpsBindings) {
433-
try {
434-
$bindingInfo = $binding.GetAttributeValue("bindingInformation")
435-
$protocol = $binding.protocol
436-
437-
Write-Verbose "Calling Remove-WebBinding -Name $SiteName -BindingInformation $bindingInfo -Protocol $protocol -Confirm:$false"
438-
Remove-WebBinding -Name $SiteName -BindingInformation $bindingInfo -Protocol $protocol -Confirm:$false
439-
Write-Verbose "Completed removing the Web Binding"
440-
441-
} catch {
442-
$msg = "Error removing binding '$($binding.bindingInformation)': $_"
443-
Write-Warning $msg -InformationAction SilentlyContinue
444-
$result = New-ResultObject -Status Error -Code 201 -Step RemoveBinding -ErrorMessage $msg
445-
return $result
446-
}
447-
}
448-
449-
# Site2 then has Test1 cert assigned to it??
450-
try {
451-
Write-Verbose "Calling New-WebBinding -Name $SiteName -Protocol $Protocol -IPAddress $IPAddress -Port $Port -HostHeader '$Hostname' -SslFlags $SslFlags"
452-
New-WebBinding -Name $SiteName -Protocol $Protocol -IPAddress $IPAddress -Port $Port -HostHeader $Hostname -SslFlags $SslFlags
453-
} catch {
454-
$msg = "Error adding binding: $_"
455-
Write-Warning $msg -InformationAction SilentlyContinue
456-
$result = New-ResultObject -Status Error -Code 202 -Step AddBinding -ErrorMessage $msg
457-
return $result
458-
}
459-
460-
Write-Verbose "Calling Get-WebBinding -Name $SiteName -Protocol $Protocol, Where BindingInformation equals '$searchBindings'"
461-
$binding = Get-WebBinding -Name $SiteName -Protocol $Protocol | Where-Object {
462-
$_.bindingInformation -eq $searchBindings
463-
}
464-
465-
try
466-
{
467-
if ($binding) {
468-
Write-Verbose "Binding thumbprint $thumbprint to $binding.bindingInformation in store: $StoreName"
469-
$null = $binding.AddSslCertificate($Thumbprint, $StoreName)
470-
$result = New-ResultObject -Status Success -Code 0 -Step BindSSL
471-
} else {
472-
$result = New-ResultObject -Status Error -Code 202 -Step BindSSL -Message "No binding found for: $searchBindings"
473-
}
474-
}
475-
catch
476-
{
477-
$result = New-ResultObject -Status Error -Code 202 -Step BindSSL -Message $_
478-
}
479-
}
480-
} else {
481-
# SERVERMANAGER FALLBACK
482-
Write-Verbose "IIS Drive is not available, using ServerManager fallback."
483-
484-
Add-Type -Path "$env:windir\System32\inetsrv\Microsoft.Web.Administration.dll"
485-
$iis = New-Object Microsoft.Web.Administration.ServerManager
486-
$site = $iis.Sites[$SiteName]
487-
488-
if ($null -eq $site) {
489-
$msg = "Site '$SiteName' not found in ServerManager."
490-
Write-Error $msg -InformationAction SilentlyContinue
491-
$result = New-ResultObject -Status Error -Code 201 -Step FindWebSite -Message $msg -Details @{ SiteName = $SiteName; IPAddress = $IPAddress; Port = $Port; HostName = $Hostname }
492-
} else {
493-
$httpsBindings = $site.Bindings | Where-Object {
494-
$_.bindingInformation -eq $searchBindings -and $_.protocol -eq "https"
495-
}
496-
497-
foreach ($binding in $httpsBindings) {
498-
try {
499-
$site.Bindings.Remove($binding)
500-
} catch {
501-
$msg = "Error removing binding: $_"
502-
Write-Warning $msg -InformationAction SilentlyContinue
503-
$result = New-ResultObject -Status Error -Code 201 -Step RemoveBinding -ErrorMessage $msg
504-
return $result
505-
}
506-
}
507-
508-
$cleanThumbprint = $Thumbprint -replace '[^a-fA-F0-9]', ''
509-
$hashBytes = -split $cleanThumbprint -replace '..', '$& ' -split ' ' | Where-Object { $_ -ne '' } | ForEach-Object { [Convert]::ToByte($_, 16) }
510-
511-
try {
512-
$newBinding = $site.Bindings.Add($searchBindings, $Protocol)
513-
if ($Protocol -eq "https") {
514-
$newBinding.CertificateStoreName = $StoreName
515-
$newBinding.CertificateHash = [byte[]]$hashBytes
516-
$newBinding.SetAttributeValue("sslFlags", $SslFlags)
517-
}
518-
$iis.CommitChanges()
519-
$result = New-ResultObject -Status Success -Code 0 -Step BindSSL -Message "Binding and certificate successfully applied via ServerManager."
520-
} catch {
521-
$msg = "Error adding binding: $_"
522-
Write-Warning $msg -InformationAction SilentlyContinue
523-
$result = New-ResultObject -Status Error -Code 202 -Step BindSSL -ErrorMessage $msg
524-
}
525-
}
526-
}
527-
528-
return $result
529-
}
530-
531384
# IIS Functions
532385
function New-KFIISSiteBinding {
533386
[CmdletBinding()]
@@ -828,7 +681,7 @@ function Remove-KFIISSiteBinding {
828681
}
829682
}
830683

831-
# Called on a renewal to remove any certificats if not bound or used
684+
# Called on a renewal to remove any certificates if not bound or used
832685
function Remove-KFIISCertificateIfUnused {
833686
param (
834687
[Parameter(Mandatory = $true)]
@@ -1381,6 +1234,52 @@ function Test-CryptoServiceProvider {
13811234

13821235
# Function that takes an x509 certificate object and returns the csp
13831236
function Get-CertificateCSP {
1237+
param(
1238+
[System.Security.Cryptography.X509Certificates.X509Certificate2]$cert
1239+
)
1240+
1241+
try {
1242+
# Check if certificate has a private key
1243+
if (-not $cert.HasPrivateKey) {
1244+
return "No private key"
1245+
}
1246+
1247+
# Get the private key
1248+
$privateKey = $cert.PrivateKey
1249+
1250+
if ($privateKey -and $privateKey.CspKeyContainerInfo) {
1251+
# For older .NET Framework
1252+
$cspKeyContainerInfo = $privateKey.CspKeyContainerInfo
1253+
1254+
if ($cspKeyContainerInfo -and $cspKeyContainerInfo.ProviderName) {
1255+
return [string]$cspKeyContainerInfo.ProviderName
1256+
}
1257+
}
1258+
1259+
# For newer .NET Core/5+ or CNG keys
1260+
try {
1261+
$key = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
1262+
if ($key -and $key.GetType().Name -eq "RSACng") {
1263+
$cngKey = $key.Key
1264+
if ($cngKey -and $cngKey.Provider -and $cngKey.Provider.Provider) {
1265+
return [string]$cngKey.Provider.Provider
1266+
}
1267+
}
1268+
}
1269+
catch {
1270+
Write-Verbose "CNG key detection failed: $($_.Exception.Message)"
1271+
}
1272+
1273+
# Ensure we always return a string
1274+
return "Unknown provider"
1275+
1276+
}
1277+
catch {
1278+
return "Error retrieving CSP: $($_.Exception.Message)"
1279+
}
1280+
}
1281+
1282+
function Get-CertificateCSPV2 {
13841283
param (
13851284
[Parameter(Mandatory = $true)]
13861285
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Cert
-2 Bytes
Loading
29 Bytes
Loading
3 Bytes
Loading

0 commit comments

Comments
 (0)