Difference between Invoke-WebRequest, HttpClient Class and WebRequest Class

Azure Tester 15 Reputation points
2023-07-13T13:18:15.0333333+00:00

Hello everyone!

I am trying to send a Client-Certificate authenticated HTTPS-Request to a Web Server.

The only way I was able to get a "200" Response from this Server was with the following code using the Webrequest Class:

using namespace System.Security.Cryptography.X509Certificates

$certPath = '.\client_cert.pfx'
$URI = 'https://example-api.com/api'

$cert = New-Object X509Certificate2($certPath, $certPassword)

# write-host for debugging
Write-Host "Cert Has Private Key: $($cert.HasPrivateKey)"

$request = [System.Net.WebRequest]::Create($URI)
$request.Method = "GET"
$request.ClientCertificates.Add($cert)

$response = $request.GetResponse()
$responseStream = $response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($responseStream)
$responseContent = $reader.ReadToEnd()

$reader.Close()
$responseStream.Close()
$response.Close()

$responseContent

The server responds with "200 - OK" and returns the information wanted.

However I have read that this class is obsolete and that I should rather use the HttpClient Class.

I tried to replicate the above request like so:

using namespace System.Security.Cryptography.X509Certificates
using namespace System.Security.Authentication
using namespace System.Net.Http
using namespace System.Net.Security

$certPath = '.\client_cert.pfx'
$URI = 'https://example-api.com/api'

$cert = New-Object X509Certificate2($certPath, $certPassword)

Write-Host("Cert has private key: ", $cert.HasPrivateKey)

$handler = New-Object HttpClientHandler

$handler.ClientCertificateOptions.Manual
$handler.ClientCertificates.Add($cert)

# write-host for debugging
Write-Host("Client Certificate Options: ", $handler.ClientCertificateOptions)
Write-Host("Client Certificates: ", $handler.ClientCertificates)

$client = New-Object HttpClient($handler)
$client.HttpClientHandler
$response = $client.GetAsync($URI).GetAwaiter().GetResult()
$response

Unfortunately, the server answers with "403 - Forbidden" even though I use the exact same certificate file.

I also tried replicating the call with the built-in cmdlet Invoke-Webrequest like so:

using namespace System.Security.Cryptography.X509Certificates

$certPath = '.\client_cert.pfx'
$URI = 'https://example-api.com/api'

$cert = New-Object X509Certificate2($certPath, $certPassword)

Write-Host("Cert has private key: ", $cert.HasPrivateKey)

$response = Invoke-Restmethod -Method "GET" -URI $URI -Certificate $cert
#$response2 = Invoke-Restmethod -Method "GET" -URI $URI -CertificateThumbprint $cert.Thumbprint

$response

But this also always yields "403 - Forbidden".

Can someone tell me where the mistake is? Because I am clearly missing it.

Why does the obsolete Webrequest Class work, while Invoke-Webrequest Cmdlet and HttpClient Class don't?

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,962 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,091 questions
PowerShell
PowerShell
A family of Microsoft task automation and configuration management frameworks consisting of a command-line shell and associated scripting language.
2,659 questions
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.