在 Invoke-WebRequest 调用中信任自签名证书

Trusting self-signed certificate in Invoke-WebRequest call

问题

有没有一种方法可以调用 Invoke-WebRequest,例如期望证书的给定 public 密钥来验证我是否信任该连接?

如果不是,是否有另一种方法可以调用 webrequest 并且只信任网页而不信任颁发者或忽略证书检查?

问题

我想加载 Invoke-WebRequest 的网页。我尝试通过 https 连接到网页,服务器提供了一个自签名证书。

因为我的证书库中没有颁发者证书 Invoke-WebRequest 会抛出错误:

Invoke-WebRequest : The remote certificate is invalid according to the validation procedure.

已知解决方法

  1. 使用 Powershell v6.0.0 中添加的 -SkipCertificateCheck-开关 ref
  2. 添加新的证书策略以信任所有证书ref
  3. 信任颁发者证书

我尝试过但没有成功的方法

  1. 在不信任颁发者证书的情况下将网页证书添加到证书库
  2. 将网页证书添加到证书存储并使用 Invoke-WebRequest-Certificate 参数指定此证书

PS:由于这个问题出现在 Powershell <5 和 Powershell Core 中,我标记了两者。

简短的回答,不,你无法绕过它,当涉及到证书时,甚至不要尝试。客户端首先会查看证书和整个证书链,看它是否可信。如果该链的任何部分(例如发行者)不可信,那么它就是不可信的。如果这个基本事实没有发生,那么就没有办法吊销证书。

对于自签名证书,由于它们不受信任,您是对的,客户端实际上只有 2 个选项:

  1. 忽略证书来源并使用 -SkipCertificateCheck 开关盲目连接。
  2. 接受颁发者和全链可以信任并导入证书

你无法回避这个基本事实。我喜欢用这个例子:自签名证书就像把一个形状为 object 的炸弹交给客户,上面贴着 "Not a Bomb - Trust me, I'm @Paxz" 的标签。证书链就像拿着炸弹形状的object,你看看交付driver,看起来很黑,同时一点也不像@Paxz。然后你可以选择忽略这个标签,或者接受我可以信任@Paxz,以及不正当的交付 driver。未经明确同意,您不能直接将包裹推过前门。当您信任整个链条时,贴纸会显示 "Not a Bomb - Trust me, I've been verified by Bomb Experts",因为您信任 Bomb Experts,所以客户会毫无疑问地接受包裹。

我需要一个到我的 Icinga2 服务器的可信连接,并找到了一种比较单个证书的方法。

这是存储 [X509Certificate2] 证书进行比较并设置回调函数来验证证书的主要代码。

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)
}

您需要 [X509Certificate2] 证书。

我用这个函数来转换一个Base64格式的证书,很容易存储在变量里面。

function get-x509 {
    param(
        [string]
            $Cert64
    )

    $CertBin=[System.Convert]::FromBase64String(($Cert64.Trim(" ") -replace "-.*-",""))

    [System.Security.Cryptography.X509Certificates.X509Certificate2]$CertBin
}

连接 SSL 服务器的全部内容:

[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

要获得旧行为:

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null