public/Get-CertificateFromUrl.ps1
function Get-CertificateFromUrl { <# .SYNOPSIS Returns a host certificate and/or certificate chain for given host .DESCRIPTION Returns a host certificate and/or certificate chain for given host as an array of X509 certificates. If a certificate doesn't have a chain (self signed) only the certificate will be returned. .EXAMPLE PS C:\> Get-CertificateFromUrl -Hostname google.com Returns the host certificate and certificate chain of google.com .EXAMPLE PS C:\> Get-CertificateFromUrl -Hostname google.com -HostCertificateOnly Returns the host certificate google.com .EXAMPLE PS C:\> Get-CertificateFromUrl -Hostname google.com -ChainOnly Returns the certificate chain of google.com without the host certificate #> [CmdletBinding(DefaultParameterSetName = 'default', PositionalBinding = $true, HelpUri = 'https://github.com/macces/ImportCertificate', ConfirmImpact = 'Low')] [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2[]])] Param ( # Param1 help description [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [String]$Hostname, # Only returns the certificate chain without the host certificate [Parameter(Mandatory = $false, ParameterSetName = 'Chain')] [switch]$ChainOnly, # Only returns the host certificate without the certificate chain [Parameter(Mandatory = $false, ParameterSetName = 'Host')] [switch]$HostCertificateOnly ) begin {} process { $certificates = [System.Collections.Generic.List[System.Security.Cryptography.X509Certificates.X509Certificate2]]::new() # check if hostname or uri if ([System.Uri]::IsWellFormedUriString($Hostname, 'Absolute')) { $Hostname = ([uri]$Hostname).host } try { # depending if using windows PS or PS this works differently if ($PSVersionTable.PSVersion -le [version]::Parse("6.0")) { Add-Type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy $webRequest = [Net.WebRequest]::Create("https://$hostname") $response = $webRequest.GetResponse() $cert = $webRequest.ServicePoint.Certificate $response.dispose() $chain = [System.Security.Cryptography.X509Certificates.X509Chain]::new() $chain.build($cert) | Out-Null $chain.build($cert) | Out-Null # for some unknown reason you have to call this twice to get the whole chain #$certificates = $chain.ChainElements.Certificate $chain.ChainElements.Certificate.ForEach({ $certificates.add($_) }) } else { $Callback = { param($sender, $cert, $chain, $errors) return $true } $request = [System.Net.Sockets.TcpClient]::new($hostname, '443') $stream = [System.Net.Security.SslStream]::new($request.GetStream(), $true, $Callback) $stream.AuthenticateAsClient($hostname) $chain = [System.Security.Cryptography.X509Certificates.X509Chain]::new() $chain.ChainPolicy.RevocationMode = [System.Security.Cryptography.X509Certificates.X509RevocationMode]::NoCheck $chain.ChainPolicy.VerificationFlags = [System.Security.Cryptography.X509Certificates.X509VerificationFlags]::AllowUnknownCertificateAuthority $chain.Build($stream.RemoteCertificate) | Out-Null $chain.Build($stream.RemoteCertificate) | Out-Null #$certificates = $chain.ChainElements.Certificate $chain.ChainElements.Certificate.ForEach({ $certificates.add($_) }) | Out-Null } } catch { Throw "Could't find any cerificates" } if ($HostCertificateOnly.IsPresent) { return $certificates[0] } elseif ($ChainOnly.IsPresent) { if ($certificates.count -gt 1) { return $certificates[1..($certificates.count - 1)] } else { Write-Warning "$Hostname didn't return a chain." if ($certificates.issuer -eq $certificates.subject) { Write-Warning "It is a self signed certificate" } return } } return @(, $certificates) } end {} } |