powershellpowershell-core

Net.HttpWebRequest has no certificates in PowerShell7


This code works on PS5, but not on PS7. It does not throw any error, it simply does not show the certificates

$url = "https://mcr.microsoft.com/v2/azure-app-service/samples/aspnethelloworld/manifests/latest"
$req = [Net.HttpWebRequest]::Create($url)
$req.GetResponse() | Out-Null
$req.ServicePoint.Certificate | Format-List

PS5 Output:

> $req.ServicePoint


BindIPEndPointDelegate :
ConnectionLeaseTimeout : -1
Address                : https://mcr.microsoft.com/v2/azure-app-service/samples/aspnethelloworld/manifests/latest
MaxIdleTime            : 100000
UseNagleAlgorithm      : True
ReceiveBufferSize      : -1
Expect100Continue      : True
IdleSince              : 1/7/2022 10:30:21 AM
ProtocolVersion        : 1.1
ConnectionName         : https
ConnectionLimit        : 2
CurrentConnections     : 2
Certificate            : System.Security.Cryptography.X509Certificates.X509Certificate
ClientCertificate      :
SupportsPipelining     : True

> $req.ServicePoint.Certificate | Format-List


Handle  : 2520270205792
Issuer  : CN=Microsoft Azure TLS Issuing CA 05, O=Microsoft Corporation, C=US
Subject : CN=mcr.microsoft.com, O=Microsoft Corporation, L=Redmond, S=WA, C=US

PS7 Output:

> $req.ServicePoint

BindIPEndPointDelegate :
ConnectionLeaseTimeout : -1
Address                : https://mcr.microsoft.com/v2/azure-app-service/samples/aspnethelloworld/manifests/latest
MaxIdleTime            : 100000
UseNagleAlgorithm      : True
ReceiveBufferSize      : -1
Expect100Continue      : True
IdleSince              : 07/01/2022 10:27:17
ProtocolVersion        : 1.1
ConnectionName         : https
ConnectionLimit        : 2
CurrentConnections     : 0
Certificate            :
ClientCertificate      :
SupportsPipelining     : True

Why is PowerShell 7 not returning Certificates in [Net.HttpWebRequest]. Are there any alternatives?


Solution

  • 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.

    To inspect the remote certificate using HttpClient, you need to leverage a certificate validation callback - in PowerShell 7 that would look something like this:

    $url = "https://mcr.microsoft.com/v2/azure-app-service/samples/aspnethelloworld/manifests/latest"
    
    # Create a (thread-safe) hashtable to hold any certificates discovered
    $certTable = [hashtable]::Synchronized(@{})
    
    # Create a handler
    $handler = [System.Net.Http.HttpClientHandler]::new()
    
    # Attach a custom validation callback that saves the remote certificate to the hashtable
    $handler.ServerCertificateCustomValidationCallback = {
      param(
        [System.Net.Http.HttpRequestMessage]$Msg,
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Cert,
        [System.Security.Cryptography.X509Certificates.X509Chain]$Chain,
        [System.Net.Security.SslPolicyErrors]$SslErrors
      )
    
      # Save the certificate
      $certTable[$Msg.RequestUri] = $Cert
    
      # Leave actual policy validation as-is
      return [System.Net.Security.SslPolicyErrors]::None -eq $SslErrors
    }.GetNewClosure()
    
    # Create a new http client with our custom handler attached
    $client = [System.Net.Http.HttpClient]::new($handler)
    
    # Prepare request message
    $request = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Get, $url)
    
    # Send request
    $response = $client.Send($request)
    
    # callback routine will now have populated the table with the certificate
    $certTable[$request.RequestUri]