I have created example about how to make a vRO Rest API request in powershell utilizing HoK (Holder of Key) token. It’s pretty much raw version. It’s not nice 😉 it depends on vsphere SDK libraries, as i did not have much time to make it work without them. The New-webserviceproxy can’t handle the soap security headers, so i am using the dlls from the SDK to make this possible. Feel free to make better version, and share. I saw a lot of people looking for invoking vro rest api methods using the hok token.
It would not be possible to write it without help of Chris Dent, Sushil Kavi, so many thanks to them for helping me out with making this example possible 😉
https://github.com/qlikq/ExampleVRORestMethodHoKtoken
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
In order to test it, you need to get vsphere SDK for webservices, as this is using the libraries from it. | |
new-webserviceproxy can't handle soap security headers. You also need PKCS#12 pfx certificate (in my example) | |
#> | |
#https://www.dorkbrain.com/docs/2017/09/02/gzip-in-powershell/ | |
Function ConvertTo-GZipString () { | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True)] | |
$String | |
) | |
Process { | |
$String | ForEach-Object { | |
$stream = [System.IO.MemoryStream]::new() | |
$writer = [System.IO.StreamWriter][System.IO.Compression.GZipStream]::new($stream, [System.IO.Compression.CompressionMode]::Compress) | |
$writer.Write($_) | |
$writer.Close() | |
[Convert]::ToBase64String([byte[]][char[]]$stream.ToArray()) | |
} | |
} | |
} | |
Add-Type -Path 'd:\vsphereWebServicesSDK67\ssoclient\dotnet\cs\samples\VMware.Binding.WsTrust\bin\Debug\VMware.Binding.WsTrust.dll' | |
Add-Type -Path 'd:\vsphereWebServicesSDK67\ssoclient\dotnet\cs\samples\VMware.Binding.WsTrust\bin\Debug\STSService.dll' | |
$certificatetobeadded = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 | |
#i have generated my certificate with 'greg3' password | |
$certificatetobeadded.Import('d:\vro\greg\greg3.pfx', 'greg3', [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet) | |
#[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls11 -bor [System.Net.SecurityProtocolType]::Tls12; | |
#[VMware.Binding.WsTrust.SamlTokenHelper]::SetupServerCertificateValidation() | |
$signingCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 | |
$signingCertificate.Import('d:\vro\greg\greg3.pfx', 'greg3', [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet) | |
$service = [VMware.Binding.WsTrust.SamlTokenHelper]::GetSTSService('https://vc001.greg.labs:7444/sts/STSService', 'administrator@vsphere.local', 'VMware1!', $signingCertificate) | |
$token = [VMware.Binding.WsTrust.SamlTokenHelper]::GetHokRequestSecurityTokenType() | |
$token.SignatureAlgorithm = [vmware.sso.SignatureAlgorithmEnum]::httpwwww3org200104xmldsigmorersasha256 | |
$response = $service.Issue($token) | |
$responsetoken = $response.RequestSecurityTokenResponse.RequestedSecurityToken | |
$responsetokenXML = $responsetoken.OuterXml | |
$encodedANDgzippedtoken = ConvertTo-GZipString -String $responsetokenXML | |
#I had new line like that before but `n works as well. | |
#$nl = (0x0A -as [char]) | |
$restmethod = 'GET' | |
$timestamp = [DateTimeOffset]::Now.ToUnixTimeSeconds().ToString() | |
$nonce = $timestamp + ':ass234' | |
[system.uri]$uri = 'https://vro816.greg.labs:443/vco/api/org/{id}/workflows?maxResult=3&queryCount=false' | |
$httprequesturi = '/' + $uri.AbsolutePath.split('/')[-1] + $uri.Query | |
$httprequesthost = $uri.Host | |
$httprequestport = $uri.Port | |
$noext = '' | |
$normalizedrequeststring = $timestamp + "`n" + $nonce + "`n" + $timestamp + "`n" + $restmethod + "`n" + $httprequesturi + "`n" + $httprequesthost + "`n" + $httprequestport + "`n" + $noext + "`n" | |
$popt = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable | |
$psigningCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 | |
$psigningCertificate.Import('d:\vro\greg\greg3.pfx', 'greg3', $popt) | |
#converted from c# to PS from https://stackoverflow.com/questions/7444586/how-can-i-sign-a-file-using-rsa-and-sha256-with-net | |
$privatekey = $psigningCertificate.PrivateKey | |
$privatekey1 = New-Object System.Security.Cryptography.RSACryptoServiceProvider | |
$privatekey1.ImportParameters($privatekey.ExportParameters($true)) | |
$enc = [system.Text.Encoding]::UTF8 | |
$data = $enc.GetBytes($normalizedrequeststring) | |
$sig = $privatekey1.SignData($data, "SHA256") | |
$base64sig = [Convert]::ToBase64String($sig) | |
#[bool]$isValid = $privateKey1.VerifyData($data, "SHA256", $sig) | |
$headervalue = 'SIGN token="{0}",nonce="{1}",signature_alg="RSA-SHA256",signature="{2}"' -f $encodedANDgzippedtoken, $nonce, $base64sig | |
$header = @{'Authorization' = $headervalue } | |
$answer = Invoke-WebRequest -Uri 'https://vro816.greg.labs:443/vco/api/org/{id}/workflows?maxResult=3&queryCount=false' -Headers $header | |
$answer.Content|convertfrom-json | |
<#TODO | |
Write it natively so that there is no need to load dlls from sdk. | |
#> | |
<# | |
Please find the details below shared by the engineering team. | |
The Authorization header has the following. | |
Authorization: SIGN token="…", | |
nonce="1589541389518:1761545587", | |
bodyhash="k9kbtCIy0CkI3/FEfpS/oIDjk6k=", | |
signature_alg="RSA-SHA256", | |
signature="…" | |
Description: | |
——- | |
token REQUIRED. The SAML2 token identifying the caller. The value is calculated as BASE64(GZIP(SAML2)). | |
nonce REQUIRED. A unique string generated by the client allowing the server to identify replay attacks and reject such requests. | |
The strings must be unique across all requests of a single client. The definition is as specified in Section 3.1 | |
of draft-ietf-oauth-v2-http-mac (http://tools.ietf.org/id/draft-ietf-oauth-v2-http-mac-00.txt) with one difference – the first component should be the current time expressed in | |
the number of milliseconds since January 1, 1970 00:00:00 GMT with no leading zeros. | |
bodyhash OPTIONAL. A hash value computed as described in Section 3.2 of draft-ietf-oauth-v2-http-mac (http://tools.ietf.org/id/draft-ietf-oauth-v2-http-mac-00.txt) over the entire HTTP request | |
entity body (as defined in Section 7.2 of RFC 2616(http://www.ietf.org/rfc/rfc2616.txt)). Note that the body hash may be missing only if there is no | |
request body, i.e. empty body. Otherwise it is required. | |
signature_alg REQUIRED. The signature algorithm used by the client to sign the request – "RSA-SHA256", "RSA-SHA384" and "RSA-SHA512" | |
signature REQUIRED. A message signature calculated over the normalized request as | |
BASE64(signature-algorithm(private key, request)). The request normalization is done | |
as defined in Section 3.3.1 of draft-ietf-oauth-v2-http-mac (http://tools.ietf.org/id/draft-ietf-oauth-v2-http-mac-00.txt) with two exception – (a) the body hash is included without | |
BASE64 applied and (b) no "ext" field is appended. All text based fields in the normalized request | |
are encoded in UTF-8. | |
#> |
4 comments
Hi,
​
I am new to vRO, and now trying to get a hok token for vRO 8.1 to use the rest call, and I read your posts, but still do not know how to use your code on Github. Do I need to download the vSphere sdk and vro sdk? Thank you so much!
Hi Hui, yes line 1-4 states that you need to obtain the webservices SDK , so that you have the dlls. Line 23,24 show you which files are being loaded from the webservices SDK. There is no VRO SDK used here.
Hi Grzegorz,
thanks for this, have you ever came up with simplier way of using this HOK token? I am upgrading our VRO to newest 8.10 version and it no longer supports basic auth for REST api calls. was wondering maybe there is better way of doing this.
Hi Andrius, not really no. I haven’t been into vRO lately to be honest 😉