I am starting to get a little bit desperate. When sending an HTTPS-Request from my local machine using the following code, everything works fine and I get my expected response from the webserver.
# Get the certificate as secret (includes public and private key, so careful) from Azure Key Vault
$pfxCert = Get-AzKeyVaultSecret -VaultName $vaultName -Name $pfxCertName -AsPlainText
# Decode the Base64-encoded secret value to a byte array
$pfxBytes = [System.Convert]::FromBase64String($pfxCert)
# Output the plain text value for debugging
Write-Host("pfxBytesLength: ", $pfxBytes.Length)
# Create a X509Certificate2 object from the .pfx file and password
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(,[byte[]]$pfxBytes)
# Check if the certificate has a private key
Write-Host("Cert has private key: ", $cert.HasPrivateKey)
# Create a web request object
$request = [System.Net.WebRequest]::Create($URI)
$request.Method = "GET"
# Add the client certificate to the request
$request.ClientCertificates.Add($cert)
# Send the request and get the response
$response = $request.GetResponse()
# Get the response stream
$responseStream = $response.GetResponseStream()
# Create a reader to read the response content
$reader = New-Object System.IO.StreamReader($responseStream)
# Read the response content as a string
$responseContent = $reader.ReadToEnd()
# Close the reader, response stream, and response objects
$reader.Close()
$responseStream.Close()
$response.Close()
# Output the response content
$responseContent
However, when I want to run this code in an Azure Function (S1, Windows, Powershell 7.2) which works as my client, I get an error message saying:
[Error] EXCEPTION: The SSL connection could not be established, see inner exception.
Message: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot
I am clueless how to fix this problem.
I tried different approaches to overcome the issue:
- Completely deactivating remote server certificate validation using
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
- Implementing a custom SSL validation callback like so (I uploaded the ca-certs to the app filesystem)
# Create an array of CA certificate filepaths
$caCertificates = @(
"C:\home\ca-certificates\issuing_3.cer",
"C:\home\ca-certificates\intermediate_2.cer",
"C:\home\ca-certificates\root_1.cer"
)
# Load the CA certificates and create X509Certificate2 objects
$caCertificateObjects = @()
foreach ($caCertificatePath in $caCertificates) {
$caCertificateBytes = [System.IO.File]::ReadAllBytes($caCertificatePath)
$caCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(,$caCertificateBytes)
$caCertificateObjects += $caCertificate
Write-Host("Added ", $caCertificate.Thumbprint, " to certificates")
}
# Create an X509Chain object and add the CA certificates to the chain
$certificateChain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
$certificateChain.ChainPolicy.ExtraStore.AddRange($caCertificateObjects)
# Output the ExtraStore contents
$certificateChain.ChainPolicy.ExtraStore
# Define the callback function for certificate validation
$certificateValidationCallback = {
param(
[System.Object]$sender,
[System.Security.Cryptography.X509Certificates.X509Certificate]$certificate,
[System.Security.Cryptography.X509Certificates.X509Chain]$certificateChain,
[System.Net.Security.SslPolicyErrors]$sslPolicyErrors
)
# Validate the remote server certificate against the custom CA certificate chain
$isValid = $certificateChain.Build($certificate)
if ($isValid) {
return $true # Accept the remote server certificate
} else {
return $false # Reject the remote server certificate
}
}
# Register the callback function for certificate validation
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $certificateValidationCallback
- I tried to add the .pfx certificate through the Azure Portal by navigating to the Function App and under "Settings"->"Certificates" added the .pfx file from the Azure Key Vault under "bring your own certificates (.pfx)". Added the "WEBSITE_LOAD_CERTIFICATES" application setting and set it to "*". Finally I tried to send the HTTPS-Request via the Invoke-Restmethod Cmdlet like so
Invoke-Restmethod -Method "GET" -URI $URI -CertificateThumbprint $certThumbprint
I also tried to skip the certificate check when using Invoke-Restmethod using -SkipCertificateCheck but to no avail.
I am now using the HttpClient class and it is still not working:
# Get the certificate as secret (includes public and private key, so careful) from Azure Key Vault
$pfxCert = Get-AzKeyVaultSecret -VaultName $vaultName -Name $pfxCertName -AsPlainText
Write-Host("pfxCertType: ", $pfxCert.GetType())
Write-Host("pfxCertLength: ", $pfxCert.Length)
# Decode the Base64-encoded secret value to a byte array
$pfxBytes = [System.Convert]::FromBase64String($pfxCert)
# Output the plain text value for debugging
Write-Host("pfxBytesLength: ", $pfxBytes.Length)
$keyStorageFlags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet
# Create a X509Certificate2 object from the .pfx file and password
$cert = New-Object X509Certificate2([byte[]]$pfxBytes, "", $keyStorageFlags)
$handler = New-Object HttpClientHandler
# Gets or sets a value that indicates if the certificate is automatically picked from the certificate store
# or if the caller is allowed to pass in a specific client certificate.
$handler.ClientCertificateOptions.Manual
$handler.ClientCertificates.Add($cert)
Write-Host("Client Certificate Options: ", $handler.ClientCertificateOptions)
Write-Host("Client Certificates: ", $handler.ClientCertificates)
$client = New-Object HttpClient($handler)
$response = $client.GetAsync($URI).GetAwaiter().GetResult()
$responseContent = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
Write-Host $response
$responseContent
Nothing worked for me. I am still stuck on the UntrustedRoot Error.
Does anyone have an idea what I am doing wrong?
Thank you so much for your time!