Reputation: 3036
Is there a way I can call Invoke-WebRequest
and for example expect a given public key of the certificate to validate that I trust the connection?
If not, is there another way to invoke the webrequest and only trust the webpage without trusting the issuer or ignoring the certificate check?
I want to load an webpage with Invoke-WebRequest
. I try to connect to the Webpage over https and the server offers an self-signed certificate.
Since I dont't have the Issuer-Certificate in my certificate-store Invoke-WebRequest
will throw the error:
Invoke-WebRequest : The remote certificate is invalid according to the validation procedure.
-SkipCertificateCheck
-Switch added in Powershell v6.0.0 ref-Certificate
-Parameter of Invoke-WebRequest
to specify this certificatePS: Since this problem appears in both Powershell <5 and Powershell Core, I am tagging both.
Upvotes: 10
Views: 27371
Reputation: 161
I need a trusted connection to my Icinga2 server and found a way to compare a single certificate.
This is the main code which stores a [X509Certificate2] certificate for comparison and set a callback function to validate the certificate.
function set-SSLCertificate {
param(
$Cert
)
if (-not("validateCert" -as [type])) {
add-type -TypeDefinition @"
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public static class ValidateCert {
static X509Certificate2 MyCert;
public static bool Validate(object sender,
X509Certificate cert,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) {
if (MyCert.Equals(cert)) {
return true;
} else {
return false;
}
}
public static RemoteCertificateValidationCallback GetDelegate(X509Certificate2 Cert) {
MyCert = Cert;
return new RemoteCertificateValidationCallback(ValidateCert.Validate);
}
}
"@
}
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = [validateCert]::GetDelegate($Cert)
}
You need a certificate as [X509Certificate2].
I use this function to convert a Base64 formatted certificate which is easy to store inside variables.
function get-x509 {
param(
[string]
$Cert64
)
$CertBin=[System.Convert]::FromBase64String(($Cert64.Trim(" ") -replace "-.*-",""))
[System.Security.Cryptography.X509Certificates.X509Certificate2]$CertBin
}
The whole thing to connect the SSL server:
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
$SecPass = ConvertTo-SecureString $Pass -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential($User, $SecPass)
$Cert = get-x509 $Cert64
set-SSLCertificate $Cert
Invoke-RestMethod -Uri $URL -Credential $Cred
To get the old behavior:
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null
Upvotes: 4
Reputation: 13453
Short answer, No you can't get around it, and when it comes to certificates, don't even try. The client will first look at the certificate, and the whole certificate chain to see if it is trusted. If any part of that chain (e.g. issuer) is not trusted, then it is not trusted. If this fundamental fact didn't happen, then there would be no way to revoke certificates.
For self signed certificates, since they are not trusted, you are right, there are really only 2 options that the client has:
-SkipCertificateCheck
switch.You can't get around this fundamental fact. I like to use the example: Self signed certificates are like delivering a bomb shaped object to a client with a sticker on it saying "Not a Bomb - Trust me, I'm @Paxz". Certificate chains are like while holding the bomb shaped object, you look at the delivery driver, and they look very shady, and at the same time don't look at all like @Paxz. You then have the choice of either ignoring the sticker, or accept that I can Trust @Paxz, and the shady delivery driver. You can't just shove the package through the front door without explicit consent. When you trust the whole chain, the sticker will say "Not a Bomb - Trust me, I've been verified by Bomb Experts", since you trust Bomb Experts, the client will accept the package without question.
Upvotes: 12