private/functions/Get-AxisTlsCertificate.ps1

function Get-AxisTlsCertificate {
    <#
    .SYNOPSIS
    Returns the certificate an Axis device presents on its HTTPS port.

    .DESCRIPTION
    Opens a TLS connection with System.Net.Security.SslStream and a validation callback that accepts any
    certificate, then returns the remote certificate as X509Certificate2. Works on Windows PowerShell 5.1 and
    PowerShell 7. Used to verify that a newly installed certificate is really served and to refuse deleting the
    certificate that is currently bound to HTTPS. Returns $null (with a verbose message) when the port cannot
    be reached or no TLS handshake is possible.

    .PARAMETER Device
    The IP address or hostname of the Axis device.

    .PARAMETER Port
    TCP port, default 443.

    .PARAMETER TimeoutSec
    Connect timeout, default 10 seconds.
    #>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory)]
        [String]$Device,

        [Parameter()]
        [Int]$Port=443,

        [Parameter()]
        [Int]$TimeoutSec=10
    )

    #Device may carry an explicit port (host:port, e.g. behind a port-forward); it overrides -Port
    if($Device -match '^(?<host>[^:\[\]]+):(?<port>\d+)$') {
        $Device = $Matches.host
        if(!$PSBoundParameters.ContainsKey('Port')) { $Port = [int]$Matches.port }
    }

    $Client = $null
    $Ssl = $null
    Try {
        $Client = New-Object System.Net.Sockets.TcpClient
        $Async = $Client.BeginConnect($Device, $Port, $null, $null)
        if(!$Async.AsyncWaitHandle.WaitOne($TimeoutSec * 1000, $false)) {
            Throw "Connection to $($Device):$Port timed out"
        }
        $Client.EndConnect($Async)

        #Accept anything: we only want to see the certificate, not validate it
        $Callback = [System.Net.Security.RemoteCertificateValidationCallback]{
            Param($Sender, $Certificate, $Chain, $Errors)
            return $true
        }
        $Ssl = New-Object System.Net.Security.SslStream($Client.GetStream(), $false, $Callback)
        $Ssl.ReadTimeout = $TimeoutSec * 1000
        $Ssl.WriteTimeout = $TimeoutSec * 1000
        Try {
            #None = operating system defaults (TLS 1.2/1.3 on current systems)
            $Ssl.AuthenticateAsClient($Device, $null, [System.Security.Authentication.SslProtocols]::None, $false)
        }
        Catch {
            $Ssl.AuthenticateAsClient($Device, $null, [System.Security.Authentication.SslProtocols]::Tls12, $false)
        }

        $Remote = $Ssl.RemoteCertificate
        if(!$Remote) {
            return $null
        }
        return New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($Remote)
    }
    Catch {
        Write-Verbose "TLS probe of $($Device):$Port failed: $_"
        return $null
    }
    Finally {
        if($Ssl) { $Ssl.Dispose() }
        if($Client) { $Client.Close() }
    }
}