Public/Assert-SiteCertificate.ps1
function Assert-SiteCertificate{ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Currently using Write-Host because it supports -NoNewLine')] [CmdletBinding()] param( [Parameter (Mandatory = $true, HelpMessage = "The hostname to test -- this should not include any http(s) at the beginning of the hostname" )] $hostToTest, [Parameter (Mandatory = $true, HelpMessage = "The port number on which to test" )] $portToTest, [Parameter(Mandatory = $false, HelpMessage = "Optionally write debug information about the function's execution to a file and/or the event log" )] [Switch] $debugEnabled, [Parameter(Mandatory = $false, HelpMessage = "Optionally specify a directory to write a debug log file to" )] [string] $debugLogDirectory = $DEFAULT_DEBUG_LOG_DIRECTORY, [Parameter(Mandatory = $false, HelpMessage = "Optionally specify whether to log to the windows event log (EVT), a file (file) or both (both)" )] [ValidateScript({if($_ -in $VALIDATE_SET_DEBUG_MODE) { $true } else { throw "Parameter '$_' is invalid -- must be one of: $($VALIDATE_SET_DEBUG_MODE -join ",")"}})] [string] $debugMode = $DEFAULT_DEBUG_MODE, [Parameter(Mandatory = $false, DontShow = $true )] [switch]$mrao ) # check to see if the global debug environment variable is set if($null -ne $env:CERTIFICAT_DEBUG_ALWAYS){ $debugEnabled = $true } # check for any trailing https:// at the beginning of the hostname and remove it, just in case $hostToTest = $hostToTest.replace("https://", "") # Build a complete command of all parameters being used to run this function $ps5Command = "powershell.exe {import-module CertifiCat-PS -Force; $($MyInvocation.MyCommand) " $functionArgs = "" foreach($a in $PSBoundParameters.Keys){ if($PSBoundParameters[$a] -eq $true){ $functionArgs += "-$a " } else { $functionArgs += "-$a `"$($PSBoundParameters[$a])`" " } } $ps5Command += ("$functionArgs}") #begin building the function's return object $fro = [PSCustomObject]@{ FunctionName = $myinvocation.MyCommand; RunningPSVersion = $PSVersionTable.PSVersion.ToString(); PS5Command = $ps5Command; FunctionArguments = $functionArgs; FunctionSuccess = $true; Errors = @(); Certificate = $null; CertificateIssueDate = ''; CertificateExpirationDate = ''; CertificateThumbprint = ''; CertificateSerialNumber = ''; CertificatePublicKeyType = ''; CertificatePublicKeyBits = ''; HostTested = $hostToTest; PortTested = $portToTest; debugEnabled= $debugEnabled; debugLogDirectory = $debugLogDirectory; debugMode = $debugMode; } Write-FunctionBlock "[$($myinvocation.MyCommand)]" "Checking Site Certificate" Write-Host "-> Attempting to make an SSL connection to '$hostToTest' on port $portToTest..." -NoNewLine # See this post -- note that the typical way to do this with ServicePointManager ONLY works in PS 5 or older (.NET Framework) # Testing via an SSL Connection/TCP connection works in BOTH versions, so we'll just use that... # https://stackoverflow.com/questions/22233702/how-to-download-the-ssl-certificate-from-a-website-using-powershell # https://learn.microsoft.com/en-us/troubleshoot/azure/azure-monitor/log-analytics/windows-agents/ssl-connectivity-mma-windows-powershell try { $tcpSocket = New-Object Net.Sockets.TCPClient($hostToTest, $portToTest) $tcpStream = $tcpSocket.GetStream() $sslStream = New-Object -TypeName Net.Security.SSLStream($tcpStream, $false) $sslStream.AuthenticateAsClient($hostToTest) # a missing or invalid cert here will cause an exception to be thrown $tlsCert = New-Object -TypeName Security.Cryptography.X509Certificates.X509Certificate2($sslStream.RemoteCertificate) $tcpSocket.Close() Write-Ok $fro.CertificateIssueDate = $tlsCert.NotBefore $fro.CertificateExpirationDate = $tlsCert.NotAfter $fro.CertificateThumbprint = $tlsCert.Thumbprint $fro.CertificateSerialNumber = $tlsCert.SerialNumber $fro.CertificatePublicKeyType = $tlsCert.PublicKey.Key.SignatureAlgorithm $fro.CertificatePublicKeyBits = $tlsCert.PublicKey.Key.KeySize $fro.Certificate = $tlsCert Write-FunctionBlock "[$($myinvocation.MyCommand)]" "Completed successfully!" "green" if($mrao){ Write-CertifiCat $hostToTest $(get-date ($tlsCert.NotAfter) -format "MM/dd/yyyy") } # write debug information if desired if($debugEnabled){ Write-ACMEDebug $myInvocation.MyCommand $fro $true $debugMode $debugLogDirectory } return $fro } catch { Write-Fail $fro.Errors += "Unable to establish a TLS connection to $($hostToTest):$portToTest`n$($_.Exception.Message)" $fro.FunctionSuccess = $false # close the socket if it's open if($tcpSocket.Connected){ $tcpSocket.Close() } Write-FunctionBlock "[$($myinvocation.MyCommand)]" "Completed unsuccessfully!" "red" # write debug information if desired if($debugEnabled){ Write-ACMEDebug $myInvocation.MyCommand $fro $false $debugMode $debugLogDirectory } return $fro } } |