2

Basically i would like to translate the command curl as with same parameters as its currently in a linux server, but in Powershell, in order to upload a file:

curl -v -T $file -u user:password http://myurl --cacert /opt/keystores/ca_cert.pem

I've found a equivalent command to perform this task: "Invoke-WebRequest" for PowerShell 3.0+, but the problem is I don't know how to call it using a CA Cert file (.pem) and I haven't found any sample in Internet.

Thanks!

javiaf
  • 107
  • 3
  • 12
  • Is the constraint here that the certificate *must* be issued by one of the CA's in the `cacert` file, or simply that you can't import the `cacert` to the store and therefor have to reference the file directly? Last one doable, first one is a bit tricky – Mathias R. Jessen Apr 20 '16 at 14:02
  • Second, i can't import the cacert to the store and i have to reference the file directly. At the moment I've downloaded a Windows cURL from internet to perform this, but I'd like to be able to do all in Powershell (using .NET libs or whatever) without referencing external executables... – javiaf Apr 20 '16 at 14:17

2 Answers2

6

When you make a TLS connection in .NET, the peer certificate is validated against a RemoteCertificateValidationCallback function, governed by the ServicePointManager for the AppDomain in question.

Most examples of how to override the default validation in PowerShell will tell you to just do:

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

Don't do that! - it will bypass validation completely.

What you can do, is implement a proper callback function and invoke chain validation manually. Before you do this, you can add certificates not in the machine or user certificate store to the list of chains you can validate against:

$callback = {
    param(
        $sender,
        [System.Security.Cryptography.X509Certificates.X509Certificate]$certificate,
        [System.Security.Cryptography.X509Certificates.X509Chain]$chain,
        [System.Net.Security.SslPolicyErrors]$sslPolicyErrors
    )

    # No need to retype this long type name
    $CertificateType = [System.Security.Cryptography.X509Certificates.X509Certificate2]

    # Read the CA cert from file
    $CACert = $CertificateType::CreateFromCertFile("C:\path\to\ca.crt") -as $CertificateType

    # Add the CA cert from the file to the ExtraStore on the Chain object
    $null = $chain.ChainPolicy.ExtraStore.Add($CACert)

    # return the result of chain validation
    return $chain.Build($certificate)
}

# Assign your delegate to the ServicePointManager callback
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $callback

# Do your Invoke-WebRequest or WebClient call here

I don't know how to read multiple certificates from a PEM file into a certificate collection, so you'll have to add each ca cert one by one, sorry

Mathias R. Jessen
  • 106,010
  • 8
  • 112
  • 163
  • Thanks @MathiasRJessen, will test and tell you. After assigning the callback to the ServerCertificateValidationCallback, in principle i don't need to add Certificate parameters to the Invoke-WebRequest, right? – javiaf Apr 21 '16 at 13:17
  • Correct, it'll apply to all https requests for the remainder of that process/session lifetime :-) – Mathias R. Jessen Apr 21 '16 at 13:31
2

Your'e looking for Invoke-RestMethod.

It has a Get-PfxCertificate parameter which takes in a PFX certificate from a file. You can convert from pem to PFX file by checking this link

So your code will look something like this:

$cert = Get-PfxCertificate -FilePath c:\path\to\cert.pfx

$userPassB64 = [System.Convert]::ToBase64String([System.Text.ASCIIEncoding]::ASCII.GetBytes("username:password")

Invoke-WebRequest -Uri http://myurl -Method Post -Header @{Authentication "Basic " + $userPassB64} -InFile 'c:\path\to\uploadfile.txt'

Update

As Mathias points out the certificate option in the command is for client certs. The point of the --cacert option is to provide peer validation when the certificate is not in the default certificate bundle that curl uses. See here for related question on what issue this option solves.

I suggest trying the command as is to see if the SSL negotiation succeeds based on the CA's installed on the machine. If SSL negotiation fails then you could try downloading the certificate file from the target site and installing it in the local certificate store. Steps to do this are set out here.

Community
  • 1
  • 1
Avner
  • 3,482
  • 1
  • 29
  • 40
  • The `-Certificate` parameter takes a *client certificate* to authenticate the request with - **not** a CA cert to validate the peer against – Mathias R. Jessen Apr 20 '16 at 12:18
  • @MathiasR.Jessen, thanks and you are correct, so there is no equivalent using Invoke-RestMethod. I believe correct approach then would be to import the certificate into trusted CA in the computer store. I will update answer. – Avner Apr 20 '16 at 13:28
  • Correct, although there is one other way, if you must validate against a cacert file and can't import to a store - implement a custom `ICertificatePolicy` class and assign that to the `ServicePointManager` policy, writing up an answer as well – Mathias R. Jessen Apr 20 '16 at 13:48
  • Thanks guys. Thing is i need to translate literally the command to be executed in Powershell, so i need to use 100% the CA cert. Not sure if i understood this last very well about the ICertificatePolicy class @MathiasR.Jessen, can you explain deeper? Thanks! – javiaf Apr 20 '16 at 13:56
  • @javiaf no worries! Unfortunately there is no literal translation. The powershell equivalent of curl does not support this option. You can achieve the same effect though by importing the certificate from a web browser on the windows machine. Mathias has an alternative approach through code. Over to him for those details. – Avner Apr 20 '16 at 14:02