Public/Get-SslCertificate.ps1

function Get-SslCertificate {
    [CmdletBinding()]

    <#
    .SYNOPSIS
        Retrieves the SSL/TLS certificate presented by a given HTTPS url.
 
    .DESCRIPTION
        Apteco PS Modules - SSL certificate retrieval
 
        Captures the leaf certificate returned during the TLS handshake with a url, without
        relying on the connection actually being trusted (validation always succeeds, since the
        goal here is to inspect the certificate, not to enforce trust).
 
        Inspired by https://github.com/PowerShell/PowerShell/issues/7092
 
    .PARAMETER Url
        The https url to connect to
 
    .EXAMPLE
        Get-SslCertificate -Url "https://www.apteco.de"
 
    .EXAMPLE
        $cert = Get-SslCertificate "https://www.apteco.de"
        $cert | Format-List
 
    .INPUTS
        String
 
    .OUTPUTS
        System.Security.Cryptography.X509Certificates.X509Certificate2
 
    .NOTES
        Author: florian.von.bracht@apteco.de
 
    #>


    param(
        [Parameter(
            Position=0,
            Mandatory=$true,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true
        )]
        [Alias('FullName')]
        [String]$Url
    )

    process {

        $code = @'
            using System;
            using System.Collections.Generic;
            using System.Net.Http;
            using System.Net.Security;
            using System.Security.Cryptography.X509Certificates;
 
            namespace AptecoTestCertificate
            {
                public class Utility
                {
                    public static Func<HttpRequestMessage,X509Certificate2,X509Chain,SslPolicyErrors,Boolean> ValidationCallback =
                        (message, cert, chain, errors) => {
                            var newCert = new X509Certificate2(cert);
                            var newChain = new X509Chain();
                            newChain.Build(newCert);
                            CapturedCertificates.Add(new CapturedCertificate(){
                                Certificate = newCert,
                                CertificateChain = newChain,
                                PolicyErrors = errors,
                                URI = message.RequestUri
                            });
                            return true;
                        };
                    public static List<CapturedCertificate> CapturedCertificates = new List<CapturedCertificate>();
                }
 
                public class CapturedCertificate
                {
                    public X509Certificate2 Certificate { get; set; }
                    public X509Chain CertificateChain { get; set; }
                    public SslPolicyErrors PolicyErrors { get; set; }
                    public Uri URI { get; set; }
                }
            }
'@


        # Differentiate between PS Core and Desktop
        if ( $PSEdition -ne 'Core' ) {
            Add-Type -AssemblyName System.Net.Http
            if ( -not ( "AptecoTestCertificate.Utility" -as [Type] ) ) {
                Add-Type $code -ReferencedAssemblies System.Net.Http
            }
        } else {
            if ( -not ( "AptecoTestCertificate.Utility" -as [Type] ) ) {
                Add-Type $code
            }
        }

        $captured = [AptecoTestCertificate.Utility]::CapturedCertificates
        $countBefore = $captured.Count

        $handler = [System.Net.Http.HttpClientHandler]::new()
        $handler.ServerCertificateCustomValidationCallback = [AptecoTestCertificate.Utility]::ValidationCallback
        $client = [System.Net.Http.HttpClient]::new($handler)

        try {
            [void]$client.GetAsync($Url).Result
        } finally {
            $client.Dispose()
            $handler.Dispose()
        }

        if ( $captured.Count -le $countBefore ) {
            throw "Could not capture a certificate for '$( $Url )'. Is it reachable and does it use HTTPS?"
        }

        $certificate = $captured[-1].Certificate

        # CapturedCertificates is a static list for the lifetime of the PowerShell session -
        # trim it back down so repeated calls do not grow it unbounded
        $captured.RemoveRange( 0, $captured.Count )

        return $certificate

    }

}