private/functions/Test-AxisCertificateAuthorityEST.ps1

function Test-AxisCertificateAuthorityEST {
    <#
    .SYNOPSIS
    Checks that an EST server is reachable and that the module holds credentials for it.

    .DESCRIPTION
    Performs the unauthenticated GET /.well-known/est[/label]/cacerts (RFC 7030 section 4.1.2,
    https://www.rfc-editor.org/rfc/rfc7030) and decodes the PKCS#7 answer. Also reports whether a stored
    credential (CredentialTarget) or a client certificate (ClientCertThumbprint) is available for /simpleenroll,
    and verifies the optional CAThumbprint pin. Nothing is enrolled. Used by New-AxisCertificateAuthority.

    .PARAMETER Authority
    The authority hashtable (Type='EST'). See Submit-AxisCsrToEST for the keys.
    #>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory)]
        [Hashtable]$Authority
    )

    $CAName = $Authority.Name
    if(!$CAName) { $CAName = $Authority.Server }

    if(!$Authority.Server) {
        return [pscustomobject]@{ Reachable = $false; Message = "Authority has no Server (expected 'https://host:port')" }
    }

    $BaseUri = "$($Authority.Server.TrimEnd('/'))/.well-known/est"
    $Label = $Authority.Label
    if(!$Label) { $Label = $Authority.Template }
    if($Label) { $BaseUri = "$BaseUri/$($Label.Trim('/'))" }
    $CaCertsUri = "$BaseUri/cacerts"

    $Param = @{
        Method = 'GET'
        Uri = $CaCertsUri
        UseBasicParsing = $true
        TimeoutSec = 30
        Headers = @{ Accept = 'application/pkcs7-mime' }
        Verbose = $false
        ErrorAction = 'Stop'
    }
    if($Authority.SkipCertificateCheck) {
        if($PSVersionTable.PSVersion.Major -ge 6) {
            $Param.Add('SkipCertificateCheck',$true)
        }
        else {
            Set-CertificateValidation -Disable
        }
    }

    Write-Verbose "GET $CaCertsUri"
    Try {
        $Response = Invoke-WebRequest @Param
        $Content = $Response.Content
        if($Content -is [Byte[]]) { $Content = [System.Text.Encoding]::ASCII.GetString($Content) }
        $Collection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
        $Collection.Import([Convert]::FromBase64String(([String]$Content -replace '[^A-Za-z0-9+/=]','')))
        $CaCerts = @($Collection)
    }
    Catch {
        return [pscustomobject]@{ Reachable = $false; Message = "EST /cacerts failed on $CAName ($CaCertsUri): $($_.Exception.Message)" }
    }
    if($CaCerts.Count -eq 0) {
        return [pscustomobject]@{ Reachable = $false; Message = "EST /cacerts on $CAName returned no certificates" }
    }

    $Notes = @("$($CaCerts.Count) CA certificate(s) from $CaCertsUri")
    if($Authority.CAThumbprint) {
        $Pin = ($Authority.CAThumbprint -replace '[^0-9A-Fa-f]','').ToUpper()
        if($CaCerts | Where-Object { $_.Thumbprint -eq $Pin }) {
            $Notes += "CAThumbprint pin matched"
        }
        else {
            return [pscustomobject]@{ Reachable = $false; Message = "EST /cacerts on $CAName did not contain the pinned CA certificate $Pin" }
        }
    }

    #Authentication material for /simpleenroll (checked locally; EST has no "whoami" operation)
    $HasAuth = $false
    if($Authority.ClientCertThumbprint) {
        if((Test-Path "Cert:\CurrentUser\My") -and (Get-ChildItem "Cert:\CurrentUser\My" | Where-Object { $_.Thumbprint -eq $Authority.ClientCertThumbprint })) {
            $Notes += "client certificate $($Authority.ClientCertThumbprint) present"
            $HasAuth = $true
        }
        else {
            $Notes += "WARNING: client certificate $($Authority.ClientCertThumbprint) not found in Cert:\CurrentUser\My"
        }
    }
    $CredentialTarget = $Authority.CredentialTarget
    if(!$CredentialTarget -and $Authority.Name) { $CredentialTarget = "AxisPowershell-CA-$($Authority.Name)" }
    if($CredentialTarget) {
        $Credential = Get-StoredCredential -Target $CredentialTarget
        if($Credential) {
            $Notes += "credential '$CredentialTarget' present (user $($Credential.UserName))"
            $HasAuth = $true
        }
        else {
            $Notes += "WARNING: no stored credential for target '$CredentialTarget'"
        }
    }
    if(!$HasAuth) {
        $Notes += "WARNING: no HTTP Basic credential or client certificate; /simpleenroll will most likely be refused"
    }

    return [pscustomobject]@{
        Reachable = $true
        Message = ($Notes -join "; ")
    }
}