ChatGPT解决这个技术问题 Extra ChatGPT

Powershell v3 Invoke-WebRequest HTTPS error

Using Powershell v3's Invoke-WebRequest and Invoke-RestMethod I have succesfully used the POST method to post a json file to a https website.

The command I'm using is

 $cert=New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("cert.crt")
 Invoke-WebRequest -Uri https://IPADDRESS/resource -Credential $cred -certificate $cert -Body $json -ContentType application/json -Method POST

However when I attempt to use the GET method like:

 Invoke-WebRequest -Uri https://IPADDRESS/resource -Credential $cred -certificate $cert -Method GET

The following error is returned

 Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
 At line:8 char:11
 + $output = Invoke-RestMethod -Uri https://IPADDRESS/resource -Credential $cred
 +           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest)      [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

I have attempted using the following code to ignore SSL cert, but I'm not sure if its actually doing anything.

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

Can someone provide some guideance on what might be going wrong here and how to fix it?

Thanks

So which one are you using? Invoke-RestMethod or Invoke-WebRequest?
Invoke-WebRequest. I use it as it returns the request/resposne headers unlike Invoke-RestMethod. However I have tried Invoke-RestMethod which takes identical parameters as well.
For what it's worth, the ServerValidationCallback thing is almost certainly a red herring, since the error you should get when you have an SSL validation problem shoudl SAY that: Invoke-WebRequest : The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. You could try exploring $Error[0].Exception.InnerException for more information...

L
Lee Grissom

This work-around worked for me: http://connect.microsoft.com/PowerShell/feedback/details/419466/new-webserviceproxy-needs-force-parameter-to-ignore-ssl-errors

Basically, in your PowerShell script:

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

$result = Invoke-WebRequest -Uri "https://IpAddress/resource"

Note, this answer is correct; however, the point made on another answer (stackoverflow.com/a/25163476/68432) is also valid. This solution won't work if you've done "[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}" beforehand.
You need to add the Type condition check as per Arthur Strutzenberg answer below or you will get an error saying the type already exists
Is there a security risk for using this in production?
5 years later, that's still the solution for PowerShell 5.1 (full .NET Framework). For PowerShell Core there's a -SkipCertificateCheck now.
@Amjad There is indeed a security risk using this in production - If somebody redirects your call to a malicious alternative endpoint (could be as simple as editing the hosts file on the machine calling it) then your call will be made regardless sending all info to the attacker
A
AndOs

Lee's answer is great, but I also had issues with which protocols the web server supported.
After also adding the following lines, I could get the https request through. As pointed out in this answer https://stackoverflow.com/a/36266735

$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols

My full solution with Lee's code.

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;
    }
}
"@
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

have you found better solution, coz if you have 40 scripts then you have to add it to each?. Seems not making any sense. Btw, thank for the answer
Lee's answer didn't work for me. I had to add the bits you referenced and IT WORKED!
Thank you thank you thank your showing me the SecurityProtocol global static property. Christ, I've just lost DAYS on checking certificates, trusts, network, routes, permissions, and shitload of other things trying to solve vague endpoint does not respond error when accessing one specific server via https (all other do work), just because that goddamn powershell 5.1 defaults to SSL3,TLS and it JUST BLOCKS GODDAMN TLS11 and TLS12 BY DEFAULT god how much I hate this crap I should have written that script in C#/Ruby/C++, or whatever else that is not powershell
@quetzalcoatl, how to change the defaults? Yours and AndOs helped me a lot, this sufficed: $AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12' and then [System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
@StanTastic: I think it's impossible to permanently change the defaults. I think it's hardcoded in the ServicePointManager source code. I never checked though, so maybe there is some way.
M
Maximilian Burszley

An alternative implementation in pure (without Add-Type of source):

#requires -Version 5
#requires -PSEdition Desktop

class TrustAllCertsPolicy : System.Net.ICertificatePolicy {
    [bool] CheckValidationResult([System.Net.ServicePoint] $a,
                                 [System.Security.Cryptography.X509Certificates.X509Certificate] $b,
                                 [System.Net.WebRequest] $c,
                                 [int] $d) {
        return $true
    }
}
[System.Net.ServicePointManager]::CertificatePolicy = [TrustAllCertsPolicy]::new()

Q
Qantas 94 Heavy

Did you try using System.Net.WebClient?

$url = 'https://IPADDRESS/resource'
$wc = New-Object System.Net.WebClient
$wc.Credentials = New-Object System.Net.NetworkCredential("username","password")
$wc.DownloadString($url)

Sunny, I receive the following when using that code: Exception calling "DownloadString" with "1" argument(s): "The remote server returned an error: (406) Not Acceptable." At line:4 char:1 + $wc.DownloadString($url) + ~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : WebException
Based on the API documentation of the REST service im using 406 indicates "that the accept header included in the request does not allow an XML or JSON response"
What response types are allowed if XML / JSON responses are not allowed ?
Is this a custom web service you are using ? Is there any publicly available documentation for the REST API ?
It is a ticketing system called EM7 I dont believe they have public docs. The service does accept JSON/XML response (works just fine if I use cURL) I believe the error is indicating that System.Net.WebClient isnt?
A
Arthur Strutzenberg

The following worked worked for me (and uses the latest non deprecated means to interact with the SSL Certs/callback functionality), and doesn't attempt to load the same code multiple times within the same powershell session:

if (-not ([System.Management.Automation.PSTypeName]'ServerCertificateValidationCallback').Type)
{
$certCallback=@"
    using System;
    using System.Net;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    public class ServerCertificateValidationCallback
    {
        public static void Ignore()
        {
            if(ServicePointManager.ServerCertificateValidationCallback ==null)
            {
                ServicePointManager.ServerCertificateValidationCallback += 
                    delegate
                    (
                        Object obj, 
                        X509Certificate certificate, 
                        X509Chain chain, 
                        SslPolicyErrors errors
                    )
                    {
                        return true;
                    };
            }
        }
    }
"@
    Add-Type $certCallback
 }
[ServerCertificateValidationCallback]::Ignore();

This was adapted from the following article https://d-fens.ch/2013/12/20/nobrainer-ssl-connection-error-when-using-powershell/


A
Amar Helloween

Invoke-WebRequest "DomainName" -SkipCertificateCheck

You can use -SkipCertificateCheck Parameter to achieve this as a one-liner command ( THIS PARAMETER IS ONLY SUPPORTED ON CORE PSEDITION )


This parameters saved me. I use invoke-WebRequest in Azure and it doesn't support "Add-Type", only this can work. Thank you very much!
A
Aaron D

I found that when I used the this callback function to ignore SSL certificates [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

I always got the error message Invoke-WebRequest : The underlying connection was closed: An unexpected error occurred on a send. which sounds like the results you are having.

I found this forum post which lead me to the function below. I run this once inside the scope of my other code and it works for me.

function Ignore-SSLCertificates
{
    $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider
    $Compiler = $Provider.CreateCompiler()
    $Params = New-Object System.CodeDom.Compiler.CompilerParameters
    $Params.GenerateExecutable = $false
    $Params.GenerateInMemory = $true
    $Params.IncludeDebugInformation = $false
    $Params.ReferencedAssemblies.Add("System.DLL") > $null
    $TASource=@'
        namespace Local.ToolkitExtensions.Net.CertificatePolicy
        {
            public class TrustAll : System.Net.ICertificatePolicy
            {
                public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem)
                {
                    return true;
                }
            }
        }
'@ 
    $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
    $TAAssembly=$TAResults.CompiledAssembly
    ## We create an instance of TrustAll and attach it to the ServicePointManager
    $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
    [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll
}


S
Sunny Chakraborty

I tried searching for documentation on the EM7 OpenSource REST API. No luck so far.

http://blog.sciencelogic.com/sciencelogic-em7-the-next-generation/05/2011

There's a lot of talk about OpenSource REST API, but no link to the actual API or any documentation. Maybe I was impatient.

Here are few things you can try out

$a = Invoke-RestMethod -Uri https://IPADDRESS/resource -Credential $cred -certificate $cert 
$a.Results | ConvertFrom-Json

Try this to see if you can filter out the columns that you are getting from the API

$a.Results | ft

or, you can try using this also

$b = Invoke-WebRequest -Uri https://IPADDRESS/resource -Credential $cred -certificate $cert 
$b.Content | ConvertFrom-Json

Curl Style Headers

$b.Headers

I tested the IRM / IWR with the twitter JSON api.

$a = Invoke-RestMethod http://search.twitter.com/search.json?q=PowerShell 

Hope this helps.


Thank you for all your assistance. However the first command $a = Invoke-RestMethod (...) is the one which does not currently work for me. Works fine for a HTTP site but when you introduce HTTPS which EM7 does it returns the error described. That is for Invoke-RestMethod and Invoke-WebRequest. I am in the process of just using an Invoke-Command cmdlet and running curl.
J
Jeremy Cook

These registry settings affect .NET Framework 4+ and therefore PowerShell. Set them and restart any PowerShell sessions to use latest TLS, no reboot needed.

Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord 

See https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls#schusestrongcrypto


For anyone else seeing this answer, our experience is that this registry patch does, in fact, require a reboot to guarantee proper functionality. When trying to ensure a TLS 1.2 connection between a Windows box running a .NET app, SSL 3 was shown via network trace to be used with this registry value in place, but before a reboot; TLS 1.2 was invoked only after a reboot.
M
Mohit Dharmadhikari

Run this command

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname {your-site-hostname}

in powershell using admin rights, This will generate all certificates in Personal directory

To get rid of Privacy error, select these certificates, right click → Copy. And paste in Trusted Root Certification Authority/Certificates. Last step is to select correct bindings in IIS. Go to IIS website, select Bindings, Select SNI checkbox and set the individual certificates for each website.

Make sure website hostname and certificate dns-name should exactly match


A
Autonomic

If you run this as administrator, that error should go away