PowerShell 5.1中C#与PowerShell类证书验证回调的差异问题
解决PowerShell 5.1及更早版本中Invoke-WebRequest自签名证书验证错误
问题场景
在PowerShell 5.1或更早版本环境中,调用Invoke-WebRequest访问使用自签名证书的站点时,会抛出以下错误:
Invoke-WebRequest : The underlying connection was closed: An unexpected error occurred on a send.
测试发现一段嵌入C#代码的PowerShell脚本可解决该问题,代码如下:
$code = @" public class SSLHandler { public static System.Net.Security.RemoteCertificateValidationCallback GetSSLHandler() { return new System.Net.Security.RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; }); } } "@ Add-Type -TypeDefinition $code # 关闭证书验证检查 [System.Net.ServicePointManager]::ServerCertificateValidationCallback = [SSLHandler]::GetSSLHandler() # 执行请求 try { invoke-WebRequest -Uri myurl -UseBasicParsing } catch { # 异常处理逻辑 } finally { # 恢复证书验证检查 [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null }
问题:PowerShell类改写无效
尝试将上述C#类改写为原生PowerShell类,但设置[System.Net.ServicePointManager]::ServerCertificateValidationCallback后无法生效,代码对比如下:
可正常运行的C#实现
# Works $UnsafeWebRequest = @' public class UnsafeWebRequest { public static System.Net.Security.RemoteCertificateValidationCallback DangerousAcceptAnyServerCertificateValidator() { return new System.Net.Security.RemoteCertificateValidationCallback( (Sender, Certificate, Chain, PolicyErrors) => { return true; } ); } } '@ Add-Type -TypeDefinition $UnsafeWebRequest [System.Net.ServicePointManager]::ServerCertificateValidationCallback = [UnsafeWebRequest]::DangerousAcceptAnyServerCertificateValidator()
无法生效的PowerShell类实现
# Does not work class UnsafeWebRequest { static [System.Net.Security.RemoteCertificateValidationCallback] DangerousAcceptAnyServerCertificateValidator() { return [System.Net.Security.RemoteCertificateValidationCallback]{ param ( [System.Object] $Sender, [System.Security.Cryptography.X509Certificates.X509Certificate] $X509Certificate, [System.Security.Cryptography.X509Certificates.X509Chain] $X509Chain, [System.Net.Security.SslPolicyErrors] $SslPolicyErrors ) return $True } } } [System.Net.ServicePointManager]::ServerCertificateValidationCallback = [UnsafeWebRequest]::DangerousAcceptAnyServerCertificateValidator()
PowerShell原生解决方案
经分析后,找到两个无需嵌入C#的PowerShell原生解决方案:
解决方案1:通过LINQ表达式编译回调函数
function New-RemoteCertificateValidationCallbackHandler { [CmdletBinding()] [OutputType( [System.Net.Security.RemoteCertificateValidationCallback] )] Param () Begin { Add-Type -AssemblyName System.Net } Process { $LinqLambdaExpression = [System.Linq.Expressions.Expression]::Lambda( [System.Net.Security.RemoteCertificateValidationCallback], [System.Linq.Expressions.Expression]::Block( [System.Linq.Expressions.Expression]::Constant($True) ), [System.Linq.Expressions.ParameterExpression[]]( [System.Linq.Expressions.Expression]::Variable([System.Object]), [System.Linq.Expressions.Expression]::Variable([System.Security.Cryptography.X509Certificates.X509Certificate]), [System.Linq.Expressions.Expression]::Variable([System.Security.Cryptography.X509Certificates.X509Chain]), [System.Linq.Expressions.Expression]::Variable([System.Net.Security.SslPolicyErrors]) ) ) } End { $LinqLambdaExpression.Compile() } } [System.Net.ServicePointManager]::ServerCertificateValidationCallback = New-RemoteCertificateValidationCallbackHandler
解决方案2:实现ICertificatePolicy接口(已过时)
注意:
CertificatePolicy已被标记为过时,建议优先使用ServerCertificateValidationCallback方案
class TrustAllCertificatePolicy : System.Net.ICertificatePolicy { [System.Boolean] CheckValidationResult ( [System.Net.ServicePoint] $ServicePoint, [System.Security.Cryptography.X509Certificates.X509Certificate] $X509Certificate, [System.Net.WebRequest] $WebRequest, [System.Int32] $CertificateProblem ) { return $True } } [System.Net.ServicePointManager]::CertificatePolicy = [TrustAllCertificatePolicy]::new()
内容的提问来源于stack exchange,提问作者Kjell Computer
相关产品推荐
相关产品推荐

