Hi all,
I'm hoping someone will be able to point me in the right direction here. I have been checking out the API with PowerShell. After some trial and error, and the help of an older post here I am able to use the Swagger UI to get my token for subsequent queries, but I am unable to get this with PowerShell.
So far I have
What I am missing is the correct way to use the authorization string with PowerShell to get the bearer token.
When I run Invoke-restmethod -uri MyVSAAuthUrl -Method Get -headers @{'Authorization'='encodedauthstring'}
I get back a response that says "Header missing user param"
If add 'User'='me@mydomain' to the header I get the same response. If I send an empty header the response is something like "Header missing authorization"
Can anyone help me out?
Thanks
Liam
If you use a different tool (like Postman or another REST API client) does your authentication string work? I would recommend that you verify that your hashing process is accurate first.
If you are generating the authorization string correctly, you could also use Wireshark or Fiddler to intercept the network packets from Powershell and compare them to a successful authorization to see what is different between what PowerShell does and what a successful authorization looks like.
Hi liamkemp can you confirm that you are using the correct method to create the Auth header's payload. Please refer to this link for more information - help.kaseya.com/.../9040000
Hi Liam,
I figured out where you went wrong here. You're missing the text "Basic " in front of your base64 encoded string. So your PowerShell should look like this:
Invoke-restmethod -uri MyVSAAuthUrl -Method Get -headers @{'Authorization'='Basic encodedauthstring'}
I tested this and I got the REST response error { "ResponseCode": 4000002, "Status": "Failed", "Error": "None" } at first, but after adding "Basic " as detailed above, it worked.
Hope this helps.
Nate
nate-pgn - Thank you! Something so simple I had overlooked.
I now get the response 4010001 OK Username or password incorrect.
I know they are both correct when I pass them into the hash function, so I must has some issues in the function. I am using the objects SHA256Managed and SHA1Managed to complete the hashing.
Thanks for your assistance everyone.
I'd recommend using this post: community.kaseya.com/.../101201.aspx
Use the same dummy username and password as well as the same random number as the post, then echo out your variables at various points in the script. You can then compare your output with the outputs in the post and wherever yours deviates, that's where you need to figure out where your mistake is.
Please note, domain credentials are not supported by Kaseya, so if you're trying to use a domain user account, it won't work at all. Your account must be a local VSA username and password.
You could also post your code (minus any identifying information) and I could see if there are any obvious mistakes.
Hi Nate,
Sorry for the late response, I appreciate your assistance.
The code I've written is
function Hash1($textToHash){
$hasher = new-object System.Security.Cryptography.SHA1Managed
$toHash = [System.Text.Encoding]::UTF8.GetBytes($textToHash)
$hashByteArray = $hasher.ComputeHash($toHash)
foreach($byte in $hashByteArray){
$res +=$byte.ToString()
}
Return $res;
function Hash256($textToHash){
$hasher = new-object System.Security.Cryptography.SHA256Managed
$username = 'test@mydomain.com.au'
$password = 'supersecret'
$random = Get-Random
$RawSHA256Hash = Hash256($password)
$CoveredSHA256HashTemp = Hash256($password -join $username)
$CoveredSHA256Hash = Hash256($CoveredSHA256HashTemp -join $Random)
$RawSHA1Hash = Hash1($password)
$CoveredSHA1HashTemp = Hash1($password -join $username)
$CoveredSHA1Hash =($CoveredSHA1HashTemp -join $random)
$auth = "user=lpkemp@techmed.com.au,pass2=$CoveredSHA256Hash,pass1=$CoveredSHA1Hash,rand2=$random,rpass2=$RawSHA256Hash,rpass1=$RawSHA1Hash,twofapass=:undefined"
$b = [System.Text.Encoding]::UTF8.GetBytes($auth)
$token = [System.Convert]::ToBase64String($b)
Invoke-RestMethod -Method Get -Uri 'saas12.kaseya.net/.../auth& -Headers @{'Authorization' = "Basic $token"}
I've take the value of $token and put it through postman and get the same username or password is incorrect response, so i'm fairly certain i'm doing something wrong with the hashes. possibly using the wrong assemblies.
I'll walk through this in the same steps as this post (http://community.kaseya.com/xsp/f/114/p/22103/101201.aspx#101201) and hopefully we'll see where you went wrong.STEP 1 - Get a username, password, and random number:Random: 998877User: testuserPass: SomethingMoreSecureThanThistwofapass=:undefinedSTEP 2 - Hash Pass+User with SH256 and SHA1:Target Hashes (from the post):SHA1 Hash = 84d015ab5dad524a428ddbd1d96b27e35bef07b9SHA256 Hash = 9968fbf6aa432a498e1cc159f5a0cfe0501ba2bc14e51a29bead555194595c38PowerShell:$username = 'testuser'$password = 'SomethingMoreSecureThanThis'$random = '998877'$CoveredSHA1Hash = Hash1("$password$username")$CoveredSHA256Hash = Hash256("$password$username")PowerShell Hashes:SHA1 Hash = 132208211719317382746614121920921710739227912397185SHA256 Hash = 153104251246170674273142281938924516020722480271621882022926411901738581148899256Observations:
First, for your covered hashes, you are doing things like this:$password -join $usernameIf I'm reading the docs right, that's joining $password to nothing using $username as the delimiter -- obviously NOT what you want. You're better off just doing "$password$username" like I did above.
Second, after running the password+username through the hash functions, the output (as seen above) is not even in the same format as normal SHA1 and SHA256 hashes. I found this article (https://learn-powershell.net/2013/03/25/use-powershell-to-calculate-the-hash-of-a-file/), which uses the following method to hash data:[string]$hash = -join ([Security.Cryptography.HashAlgorithm]::Create($algorithm).ComputeHash($data) | ForEach { "{0:x2}" -f $_ })
So I did this instead:$data = [system.Text.Encoding]::UTF8.GetBytes("$password$username")[string]$SHA1Hash = -join ([Security.Cryptography.HashAlgorithm]::Create("SHA1").ComputeHash($data) | ForEach { "{0:x2}" -f $_ })[string]$SHA256Hash = -join ([Security.Cryptography.HashAlgorithm]::Create("SHA256").ComputeHash($data) | ForEach { "{0:x2}" -f $_ })Results:SHA1 Hash = 84d015ab5dad524a428ddbd1d96b27e35bef07b9SHA256 Hash = 9968fbf6aa432a498e1cc159f5a0cfe0501ba2bc14e51a29bead555194595c38MUCH BETTER!STEP 3 - Hash prior hash+random number with SHA256 and SHA1:Target Hashes (from the post):SHA1 Hash = a94e5f7c12d1599e538cffb61bc15ab8c7ab7cedSHA256 Hash = 687e5a021386639f86ba48aecfadbc162de3f28128e6e6772807d80cdf38615bMy Hashes:SHA1 Hash = a94e5f7c12d1599e538cffb61bc15ab8c7ab7cedSHA256 Hash = 687e5a021386639f86ba48aecfadbc162de3f28128e6e6772807d80cdf38615bYAY!STEP 4 - Build the authorization string:user=testuser,pass2=687e5a021386639f86ba48aecfadbc162de3f28128e6e6772807d80cdf38615b,pass1=a94e5f7c12d1599e538cffb61bc15ab8c7ab7ced,rand2=998877,rpass2=687e5a021386639f86ba48aecfadbc162de3f28128e6e6772807d80cdf38615b,rpass1=a94e5f7c12d1599e538cffb61bc15ab8c7ab7ced,twofapass=:undefinedMatches the forum post.STEP 5 - Encode the authorization string with Base64:dXNlcj10ZXN0dXNlcixwYXNzMj02ODdlNWEwMjEzODY2MzlmODZiYTQ4YWVjZmFkYmMxNjJkZTNmMjgxMjhlNmU2NzcyODA3ZDgwY2RmMzg2MTViLHBhc3MxPWE5NGU1ZjdjMTJkMTU5OWU1MzhjZmZiNjFiYzE1YWI4YzdhYjdjZWQscmFuZDI9OTk4ODc3LHJwYXNzMj02ODdlNWEwMjEzODY2MzlmODZiYTQ4YWVjZmFkYmMxNjJkZTNmMjgxMjhlNmU2NzcyODA3ZDgwY2RmMzg2MTViLHJwYXNzMT1hOTRlNWY3YzEyZDE1OTllNTM4Y2ZmYjYxYmMxNWFiOGM3YWI3Y2VkLHR3b2ZhcGFzcz06dW5kZWZpbmVkMatches the forum post.STEP 6 - HTTP GET with basic authorization header:Obviously I don't have access to your Kaseya system, and it look like yours is hosted, where mine is an on-prem instance. However, I tried this on my system and after putting in my own username and password I did get a token.Here is my final PowerShell script:function hash($algorithm, $text) { $data = [system.Text.Encoding]::UTF8.GetBytes($text) [string]$hash = -join ([Security.Cryptography.HashAlgorithm]::Create($algorithm).ComputeHash($data) | ForEach { "{0:x2}" -f $_ }) return $hash}$vsa_url = 'https://yourvsa.com/api/v1.0/auth'$username = 'testuser'$password = 'SuperSecretPassword'$random = Get-Random$SHA1Hash = hash "SHA1" "$password$username"$SHA256Hash = hash "SHA256" "$password$username"$SHA1Hash = hash "SHA1" "$SHA1Hash$random"$SHA256Hash = hash "SHA256" "$SHA256Hash$random"$auth = "user=$username,pass2=$SHA256Hash,pass1=$SHA1Hash,rand2=$random,rpass2=$SHA256Hash,rpass1=$SHA1Hash,twofapass=:undefined"$base64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($auth))Invoke-RestMethod -Method Get -Uri $vsa_url -Headers @{'Authorization' = "Basic $base64"}Hope this helps.
Now that you mention it, I don't know why I was trying to use -join like that at all!
In trying to make this readable to a scripting-averse colleague I have apparently lost my mind.
The only way -join would work is $password,$username -join "" which is clearly not intuitive or readable.
I'm missing WMF 5 on this computer, so I cant use invoke-restmethod. I'll try that hashing method out on another computer when I can.
Thanks for sticking with me through this.
Nate,
Thank you so much, you are fantastic.
I'm curious to know why the output from the hashing function I found are so different to yours, but that is for another day.
Cheers
nate-pgn - looking at this again, your authorisation string is in a different order than the documentation, and is using the SHA256 and SHA1 hashes twice, instead of the covered and the raw of each - This process is working, so is the documentation incorrect? Or have I misunderstood.
nate-pgn liamkemp The order of the parameters in the auth string is irrelevant. They are broken down into a dictionary during authentication so it really doesn't care what order is used, as long as they're all there.
As to the raw hashes, I believe they are only relevant when the usernames are complex ones such as email addresses. I think they are only evaluated if the raw hashes don't pass the auth tests.
That would make sense. It explains why my hashes work even though they deviate from the documentation. They aren't being used!