Dennis
Dennis

Reputation: 1784

How do I download the certificate from a web server that is self signed using PowerShell?

I've found several answer about how to download the certificate for a website in PowerShell using TcpClient.

function Get-RemoteCertificate {
       
    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate])]
    param (
      [Parameter(Mandatory, ValueFromPipeline)]
      [ValidateNotNull()]
      [Uri]$Uri
    )

    process {
        try {# connecting
            $TcpClient = [System.Net.Sockets.TcpClient]::new($Uri.Host, $Uri.Port)
    
            try {# getting SSL
                $SslStream = [System.Net.Security.SslStream]::new($TcpClient.GetStream())
                $SslStream.AuthenticateAsClient($Uri.Host)
                $SslStream.RemoteCertificate
            } finally {
                $SslStream.Dispose()
            }# end SSL
    
        } finally {
            $TcpClient.Dispose()
        }# end connect
    }
}

But as the TLS handshake will fail when the certificate isn't trusted, I can't download self signed certificates and I will get the error

Exception calling ".ctor" with "2" argument(s): "A connection attempt failed because the connected party did not 
properly respond after a period of time, or established connection failed because connected host has failed to 
respond

Is there a way to download self signed certificates without using OpenSSL?

Upvotes: 0

Views: 2672

Answers (2)

Brice
Brice

Reputation: 814

This answer is using an old HttpWebRequest that is depreciated and will not work with powershell 7. Taken from this great answer:

The HttpWebRequest API surface has not been fully ported to the newer versions of .NET/Core, as detailed in this Github issue:

HttpWebRequest is API which is obsolete - see https://github.com/dotnet/platform-compat/blob/master/docs/DE0003.md.
We ported only the most important parts of it to .NET Core. The recommended Networking API is HttpClient.

Without using your sample, you can achieve this simple task based on this answer

$ub = new-object System.UriBuilder -argumentlist 'https', '1.1.1.1', 443
$uri = $ub.Uri

# Disable the verification: 
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$req = [Net.HttpWebRequest]::Create($Uri.AbsoluteUri)
try {
    $req.GetResponse() | Out-Null
    # Export the file, or return it if you want to keep a function
    $req.ServicePoint.Certificate.Export("Cert") | Set-Content -Path "C:\temp\test.crt"  -Encoding Byte
} catch {
    Write-Host Exception while checking URL $url`: $_ -f Red
}

Upvotes: 2

Doug Maurer
Doug Maurer

Reputation: 8858

NOTE: This is considered dangerous and you should think about the possible negative side effects of using this technique.

You can use a different overload when instantiating [System.Net.Security.SslStream] that ignores cert errors as shown here.

function Get-RemoteCertificate {
    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate])]
    param (
        [Parameter(Mandatory,ValueFromPipeline)]
        [ValidateNotNull()]
        [Uri]$Uri
    )

    process {
        try {
            $TcpClient = [System.Net.Sockets.TcpClient]::new($Uri.Host, $Uri.Port)
            try {
                $SslStream = [System.Net.Security.SslStream]::new(
                    $TcpClient.GetStream(),
                    $True,
                    [System.Net.Security.RemoteCertificateValidationCallback]{$true}
                )
                $SslStream.AuthenticateAsClient($Uri.Host)
                $SslStream.RemoteCertificate
            } finally {
                $SslStream.Dispose()
            }
        } finally {
            $TcpClient.Dispose()
        }
    }
}

Upvotes: 1

Related Questions