Erwin Rooijakkers February 2016

Trusting certificate of HTTPS site in web request

I want to test if our web server (located on the intranet) is online (1) or offline (0) from the server containing it using PowerShell 2.0. For this I have a script that navigates to the page and check if a html-string is available on the page.

function Get-HeartBeat {
    $isAlive = $false

    try {
        $webclient = New-Object WebClient

        $userName = "xxx"
        $password = "xxx"
        $domain = "xxx"

        $url  = "ourUrl.com"
        $html = '<input type="submit">'

        $webclient.Credentials = New-Object System.Net.NetworkCredential($userName, $password, $domain)
        $webpage = $webclient.DownloadString($url)

        $isAlive = $webpage.Contains($html)
    } catch {
        # A WebException is thrown if the status code is anything but 200 (OK)
        Write-Host $_
        $isAlive = $false
    }

    return "$([Int32]$isAlive)"
}

Unfortunately this returns an error:

Exception calling "DownloadString" with "1" argument(s): "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel."

A way to trust our certificate is to create a type with a certificate policy as follows (modification of this answer):

Add-Type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;

    public class TrustOurCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint servicePoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) 
        {
            return certificate.Issuer.Equals("OUR ISSUER")
                && certificate.Subject.Contains("our application");
        }
    }
"@

[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustOurC        

Answers


Ansgar Wiechers February 2016

The correct way to handle certificate errors is to import the certificate chain (root and intermediat CA certificates) of the webserver's certificate into the local certificate store (as Trusted Root CA and Intermediate CA respectively). If the server certificate is self-signed you need to import the server certificate as a Trusted Root CA.

Another option, depending on what you actually need to check, might be to modify your algorithm. For instance, if you really need just a heartbeat (not verify that an actual request returns a specific result) you could simply try to establish a TCP connection to the port:

function Get-HeartBeat {
  $url  = 'ourUrl.com'
  $port = 443

  $clnt = New-Object Net.Sockets.TcpClient
  try {
    $clnt.Connect($url, $port)
    1
  } catch {
    0
  } finally {
    $clnt.Dispose()
  }
}

On more recent Windows versions you could use the Test-NetConnection cmdlet to the same end:

function Get-HeartBeat {
  $url  = 'ourUrl.com'
  $port = 443

  [int](Test-Net-Connection -Computer $url -Port $port -InformationLevel Quiet)
}

Post Status

Asked in February 2016
Viewed 1,116 times
Voted 13
Answered 1 times

Search




Leave an answer