使用HTTPS协议的Powershell Invoke-RestMethod

17
我试图通过PowerShell与一个服务通信,但结果非常糟糕。我怀疑是证书的问题,我已经在Google上搜索了答案,并找到了两个选项,但都无法解决我的问题。我还尝试了不成功地将这两个选项结合起来。

选项1:

add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

$urlJSON = "https://internal.ad.local/path/api_jsonrpc.php"

#Create authentication JSON object using ConvertTo-JSON
$objAuth = (New-Object PSObject | Add-Member -PassThru NoteProperty jsonrpc '2.0' |
Add-Member -PassThru NoteProperty method 'user.authenticate' |
Add-Member -PassThru NoteProperty params @{user="user";password="password"} |
Add-Member -PassThru NoteProperty id '2') | ConvertTo-Json


Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"

选项2:

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

$urlJSON = "https://internal.ad.local/path/api_jsonrpc.php"

#Create authentication JSON object using ConvertTo-JSON
$objAuth = (New-Object PSObject | Add-Member -PassThru NoteProperty jsonrpc '2.0' |
Add-Member -PassThru NoteProperty method 'user.authenticate' |
Add-Member -PassThru NoteProperty params @{user="user";password="password"} |
Add-Member -PassThru NoteProperty id '2') | ConvertTo-Json


Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"

以下是错误信息:

Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At C:\Users\user\AppData\Local\Temp\46eaa6f7-62a0-4c10-88d1-79212d652bc9.ps1:24 char:1
+ Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

我想补充一点:

  • 通过浏览器可以直接访问该服务
  • 我尝试开启HTTP,也成功了
  • 该服务使用的证书是自签名的,但是通过根证书受信任(在IE或Chrome中没有发出警告)
  • 我进行了网络捕获,并确保数据包确实到达了服务器。

欢迎提出任何建议!

谢谢, 帕特里克

根据下面Tree先生的建议更新后的帖子:

Name                       : lambda_method
DeclaringType              :
ReflectedType              :
Module                     : RefEmit_InMemoryManifestModule
MethodHandle               :
Attributes                 : PrivateScope, Public, Static
CallingConvention          : Standard
IsSecurityCritical         : False
IsSecuritySafeCritical     : False
IsSecurityTransparent      : True
ReturnType                 : System.Boolean
ReturnParameter            :
ReturnTypeCustomAttributes : System.Reflection.Emit.DynamicMethod+RTDynamicMethod+EmptyCAHolder
MemberType                 : Method
MethodImplementationFlags  : NoInlining
IsGenericMethodDefinition  : False
ContainsGenericParameters  : False
IsGenericMethod            : False
IsPublic                   : True
IsPrivate                  : False
IsFamily                   : False
IsAssembly                 : False
IsFamilyAndAssembly        : False
IsFamilyOrAssembly         : False
IsStatic                   : True
IsFinal                    : False
IsVirtual                  : False
IsHideBySig                : False
IsAbstract                 : False
IsSpecialName              : False
IsConstructor              : False
CustomAttributes           :
MetadataToken              :

在Mr Tree的评论基础上进行更新2:

Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At C:\Users\user\AppData\Local\Temp\ff47910e-fd8e-4be8-9241-99322144976a.ps1:13 char:1
+ Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

1
在第二个示例中尝试使用 {$true} -as [Net.Security.RemoteCertificateValidationCallback]... 你是否已经将 CA 安装在 LocalMachine 或 CurrentUser 存储中? - Mr Tree
感谢您的帮助。CA已安装在我的CurrentUser和计算机帐户下的受信任根证书中。我将在我的上面发布的帖子中添加您发布的命令的结果。 - PatrikJ
请将以下与编程相关的内容从英语翻译成中文。仅返回已翻译的文本:(不确定要在那里寻找) - PatrikJ
1
如果从选项2中删除以下内容 [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true},然后重新启动PowerShell并重新运行脚本,您会得到什么? - Mr Tree
谢谢你的建议。我仍然遇到了之前相同的错误。帖子已经更新了详细信息。 - PatrikJ
4个回答

56

在解决另一问题的同时,我解决了这个谜团。相关的网络服务器仅支持TLS1.1和TLS1.2。PowerShell似乎默认不支持此功能。

要强制使用TLS1.2,您可以使用以下代码:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

1
帮了我很多。谢谢你的发布! - floyd
新的DellEMC Isilon模拟器从版本8.2.1开始似乎具有内置Apache Web服务器的TLS1.2。PatrickJ提供的这个更正使它们在新平台上运行。谢谢! - reverpie

0
最近我经历了很多痛苦,以克服类似的情况。我正在使用试用版SaaS服务创建概念验证。该服务具有自签名SSL证书,因此我想在尝试调用POST方法时忽略证书错误(类似于curl的“-k”参数)。经过长时间的挣扎,我发现它需要 同时 - (a) 调用以忽略证书验证错误和 (b) 显式设置TLS 1.2作为安全协议。我认为后者是因为该服务可能会拒绝使用其他协议进行连接尝试。(我花了太多时间尝试不同的变化方式,如各种SO线程中建议的那样,但是独立地...)
这是有效的代码... 重要提示:证书验证绕过仅适用于原型/概念验证。我们不打算在生产环境中这样做(您也不应该!)。
$defaultSecurityProtocol = $null
try
{   
    #BUGBUG, TODO: Disabling cert validation for the duration of this call...('trial' version cert is self-signed.)
    #Remove this after the PoC.
    [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 
    #Cache the previous protocol setting and explicitly require TLS 1.2 
    $defaultSecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol
    [System.Net.ServicePointManager]::SecurityProtocol = `
                [System.Net.SecurityProtocolType]::Tls12

    if (-not [String]::IsNullOrWhiteSpace($authZHeaderValue))
    {
        $response = Invoke-WebRequest `
                    -Uri $webhookUrl `
                    -Method "Post" `
                    -Body $eventJson `
                    -Header @{ $authZHeaderName = $authZHeaderValue} 
    }
    else 
    {
        $response = Invoke-WebRequest `
                    -Uri $webhookUrl `
                    -Method "Post" `
                    -Body $eventJson
    }
}
catch 
{
    $msg = $_.Exception.Message
    $status = $_.Exception.Status
    $hr = "{0:x8}" -f ($_.Exception.HResult)
    $innerException = $_.Exception.InnerException
    #Just issue a warning about being unable to send the notification...
    Write-Warning("`n`t[$status] `n`t[0x$hr] `n`t[$msg] `n`t[$innerException]")
}
finally 
{
    # Set securityProtocol and CertValidation behavior back to the previous state.
    [System.Net.ServicePointManager]::SecurityProtocol = $defaultSecurityProtocol
        [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null
}

另外还要补充的是,随着各种漏洞被发现并得到修复,首选安全协议也在不断变化。此外,不同的系统(客户端操作系统和服务器/服务中的SSL/TLS堆栈)通常需要跟上最新/最安全选项的步伐。因此,哪个标志能够起作用将取决于客户端和服务器系统以及时间因素(例如TLS1.2可能几个月后就不再是首选)。理想情况下,应该不需要指定任何标志。更多信息请参阅this MSDN document中的“备注”部分。

0
这是一个完整的 Powershell 脚本,对我来说很有效:
try {
    # Configure API request payload
    $url = "https://my.server.com/api/countries"
    $method = "POST"
    $headers = @{
                  "Content-Type" = "application/json"
               }
    $body = '{
              "name": "India"
            }'
    # Reading request body from json file
    # $body = Get-Content request.json | Out-String
    $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 "C:\ClientCert.pfx", "Password1"
 
    # Call the API
    Invoke-RestMethod -Uri $url -Method $method -Headers $headers -Body $body -Certificate $certificate
    
    Write-Host "`n#################### SUCCESS ####################`n"
} catch {
    Write-Host "`n#################### ERROR ####################`n"

    # Getting response code
    $responseCode = $_.Exception.Message
    
    # Getting response body
    if ($_.Exception.Response -ne $null) {
        $responseStream = $_.Exception.Response.GetResponseStream()
        $streamReader = New-Object System.IO.StreamReader($responseStream)
        $streamReader.BaseStream.Position = 0
        $streamReader.DiscardBufferedData()
        $responseBody = $streamReader.ReadToEnd()
    }
    
    Write-Host "Response Code - " $responseCode
    Write-Host "Response Body - " $responseBody
    
    Write-Host "`n###############################################`n"
}

0

看起来您正在尝试使用Invoke-RestMethod调用json API。根据文档

-ContentType

指定Web请求的内容类型。

如果省略此参数且请求方法为POST,Invoke-RestMethod会将内容类型设置为"application/x-www-form-urlencoded"。否则,在调用中未指定内容类型。

要使用json主体,您需要使用Invoke-RestMethod -ContentType 'application/json' <other args>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接