Bittrex API call with powershell fails with INVALID_SIGNATURE - rest

I haven't been successful in getting a response from the Bittrex API with the following powershell code:
Function Crypto($secret, $message)
{
$hmacsha = New-Object System.Security.Cryptography.HMACSHA512(,[System.Text.Encoding]::ASCII.GetBytes($secret))
$hashmessage = $hmacsha.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($message))
$signature = [Convert]::ToBase64String($hashmessage)
return $signature
}
$apiKey = "key"
$secretApiKey = "secret"
$nonce = [Math]::Round((([DateTime]::UtcNow - [DateTime]::new(1970, 1, 1, 0, 0, 0, 0, 'Utc')).TotalSeconds),0)
$uri = "https://bittrex.com/api/v1.1/account/getbalances?apikey=$apiKey&nonce=$nonce"
$signature = Crypto $secretApiKey $uri
Invoke-RestMethod -Uri $uri -Method Get -Headers #{"apisign"="$signature"}
The response is
success message result
------- ------- ------
False INVALID_SIGNATURE
Any ideas what's missing?

Funny enough I ran across this as I was working on the exact same thing. I have solved the problem -- after looking at enough material I figured out the resulting header needs to be the signed data in hex format without any dashes.
Code:
### Bittrex API Key Variables
$apikey = 'KEY-GOES-HERE'
$apisecret = 'SECRET-GOES-HERE'
$byte_key = [Text.Encoding]::ASCII.GetBytes($apikey)
$byte_secret = [Text.Encoding]::ASCII.GetBytes($apisecret)
### Build the URL String
$d1 = ("01/01/1970" -as [DateTime])
[int]$int_nonce = ((New-TimeSpan -Start $d1 -End ([DateTime]::UtcNow)).TotalSeconds -as [string])
[string]$nonce = ($int_nonce -as [string])
$url = "https://bittrex.com/api/v1.1/account/getbalances?apikey=$($apikey)&nonce=$($nonce)"
$utf8enc = New-Object System.Text.UTF8Encoding
$url_bytes = $utf8enc.GetBytes($url)
### SHA 512 HASH TO HEX
$sha512 = New-Object System.Security.Cryptography.HMACSHA512
$sha512.key = $byte_secret
$sha_data = $sha512.ComputeHash($url_bytes)
$sha_sig = [System.BitConverter]::ToString($sha_data) -replace "-";
### Add Headers
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("apisign",$sha_sig)
### Make the Request
$request = Invoke-WebRequest $url -Headers $headers | ConvertFrom-Json

Related

Get-content not producing an array that Invoke-restmethod can process

As a follow-up to this question, instead of using a long array in the script I wanted to draw from a text file. So I replaced this:
$URLs = 'http://websiteone.com','http://websitetwo.com','http://websitethree.com'
with this
$URLs = Get-Content ./urlfile.txt
or (functionally the same as far I know) this
$URLs = #(Get-Content ./urlfile.txt)
But I end up with Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
Incorporating the great response form my last question, my foreach loop looks like this:
foreach($URL in $URLs) {
$BODY = #([pscustomobject]#{"client" = #{"clientId" = "company"; "clientVersion" = "1.0"}; "threatInfo" = #{"threatTypes" = "MALWARE","SOCIAL_ENGINEERING","THREAT_TYPE_UNSPECIFIED","UNWANTED_SOFTWARE","POTENTIALLY_HARMFUL_APPLICATION"; "platformTypes" = "ANY_PLATFORM"; "threatEntryTypes" = "URL","EXECUTABLE","THREAT_ENTRY_TYPE_UNSPECIFIED"; "threatEntries" = #{"url" = $URL}}})
$JSONBODY = $BODY | ConvertTo-Json
$Result = Invoke-RestMethod -Method 'POST' -Uri $Uri -Body $JSONBODY -Headers $HEADERS
if ( ([string]::IsNullOrEmpty($Result)) ) {} else {write-host $URL "ALERT: Safe browsing match!"}
}
... but this doesn't work if I create the array with the Get-Content cmdlet. If I run the script either way, then type $URLs, I get the exact same data returned. What am I doing wrong with get-content?
The Invoke-RestMethod cmdlet is there to make one Rest request at a time and can't take an array.
You will need to add a forEach loop to step through your $urls one at a time, something like this:
foreach($url in $urls){
$result = Invoke-RestMethod -Uri $url
#do something with $result
}
So to integrate into your sample from the previous question, you should have a urls.txt file which looks like this:
http://google.com
http://small.com
https://fast.com/
And then your code would look like this:
$URLs = get-content .\urls.txt
$HEADERS = #{ 'Content-Type' = "application/json" }
$GOOGLE_API_KEY='[API Key]'
$Uri = 'https://safebrowsing.googleapis.com/v4/threatMatches:find?key='+ $GOOGLE_API_KEY
foreach($URL in $URLs) {
$BODY = #([pscustomobject]#{"client" = #{"clientId" = "company"; "clientVersion" = "1.0"}; "threatInfo" = #{"threatTypes" = "MALWARE","SOCIAL_ENGINEERING","THREAT_TYPE_UNSPECIFIED","UNWANTED_SOFTWARE","POTENTIALLY_HARMFUL_APPLICATION"; "platformTypes" = "ANY_PLATFORM"; "threatEntryTypes" = "URL"; "threatEntries" = #{"url" = $URL}}})
$JSONBODY = $BODY | ConvertTo-Json
$result = Invoke-RestMethod -Method 'POST' -Uri $Uri -Body $JSONBODY -Headers $HEADERS
[pscustomObject]#{SiteName=$url;ThreatInfo=$result.Matches}
}
This would load up the list of $urls from your text file, then run a Rest Request on each, storing the result in $result. Finally, it will make a new PowerShell Object with the site name and show you if there are any matches from the Google SafeBrowsing API.
You'll need to run the command interactively and see which properties from $result are meaningful to you, but you can see all of the expected properties in the Google API Docs.
Edit
Found the bug. It turns out when we use Get-Content the object returned back retains some of the document formatting information from the original file! We can see this by inspecting $JSONBODY. We also see that the conversion to Json from [PSCustomObject is leaving a lot of cruft behind too.
To fix this, we should cast $URL into a string using the ToString() method and also ditch casting to [psCustomObject] too as shown below.
$BODY = #{
"client" = #{
"clientId" = "company"; "clientVersion" = "1.0"
};
"threatInfo" = #{
"threatTypes" = "MALWARE",
"SOCIAL_ENGINEERING",
"THREAT_TYPE_UNSPECIFIED",
"UNWANTED_SOFTWARE",
"POTENTIALLY_HARMFUL_APPLICATION"; "platformTypes" = "ANY_PLATFORM"; "threatEntryTypes" = "URL"; "threatEntries" = #{
"url" = $URL.ToString()
}
}
}
$JSONBODY = $BODY | ConvertTo-Json

using Invoke-RestMethod to post to a cosmosDB Returns a 400

I am trying to post a document to cosmosdb using powershell, I have followed the steps in this post https://www.systemcenterautomation.com/2018/06/cosmos-db-rest-api-powershell/ and I am still getting a 400 error everytime
Add-Type -AssemblyName System.Web
# generate authorization key
Function Generate-MasterKeyAuthorizationSignature
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$verb,
[Parameter(Mandatory=$true)][String]$resourceLink,
[Parameter(Mandatory=$true)][String]$resourceType,
[Parameter(Mandatory=$true)][String]$dateTime,
[Parameter(Mandatory=$true)][String]$key,
[Parameter(Mandatory=$true)][String]$keyType,
[Parameter(Mandatory=$true)][String]$tokenVersion
)
$hmacSha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacSha256.Key = [System.Convert]::FromBase64String($key)
$payLoad = "$($verb.ToLowerInvariant())`n$($resourceType.ToLowerInvariant())`n$resourceLink`n$($dateTime.ToLowerInvariant())`n`n"
$hashPayLoad = $hmacSha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($payLoad))
$signature = [System.Convert]::ToBase64String($hashPayLoad);
[System.Web.HttpUtility]::UrlEncode("type=$keyType&ver=$tokenVersion&sig=$signature")
}
Function Post-CosmosDocuments{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$EndPoint,
[Parameter(Mandatory=$true)][String]$DBName,
[Parameter(Mandatory=$true)][String]$CollectionName,
[Parameter(Mandatory=$true)][String]$MasterKey,
[String]$Verb="POST",
[Parameter(Mandatory=$true)][String]$JSON
)
$Verb = "POST"
$ResourceType = "docs";
$ResourceLink = "dbs/$DBName/colls/$CollectionName"
$dateTime = [DateTime]::UtcNow.ToString("r")
$authHeader = Generate-MasterKeyAuthorizationSignature -verb $Verb -resourceLink $ResourceLink -resourceType $ResourceType -key $MasterKey -keyType "master" -tokenVersion "1.0" -dateTime $dateTime
$header = #{authorization=$authHeader;"x-ms-version"="2017-02-22";"x-ms-date"=$dateTime}
$contentType= "application/json"
$queryUri = "$EndPoint$ResourceLink/docs"
$result = Invoke-RestMethod -Method $Verb -ContentType $contentType -Uri $queryUri -Headers $header -Body $JSON
return $result.statuscode
}
$CosmosDBEndPoint = "https://<omitted>.documents.azure.com:443/"
$DBName = "database"
$CollectionName = "container"
$MasterKey = "<omitted>=="
$SomeObject = [PSObject]#{ id = 1 ; Application = "Ops"; Environment = "Dev"; adKey = "555-555-5555"; }
Post-CosmosDocuments -EndPoint $CosmosDBEndPoint -MasterKey $MasterKey -DBName $DBName -CollectionName $CollectionName -JSON ($SomeObject | ConvertTo-Json)
returns a 400 every time, I am not sure what is wrong with the request.
Based on the Powershell script samples, I see you are missing the headers:
x-ms-documentdb-partitionkey
User-Agent
2018-12-31 as x-ms-version
Adding the response body that you are getting would also help.
it is working now
I am not sure why I was getting a 400. perhaps I the timing on when I created the collection was off.

How to Pass JSON Parameter with POST method on powershell 2.0?

I have a Powershell code that is work very fine in powershell version 3.
I need to run this code in powershell 2.0 too. But Invoke-WebRequest not supported in PS version 2.0.
Please help me!
$params = "metrics[]=failed:count"
$failed = (Invoke-WebRequest -Uri http://localhost:9000/stats -Method POST -Body $params -ContentType "application/json").Content
$x = $failed | ConvertFrom-Json
Untested, but I think this may help:
$params = "metrics[]=failed:count"
$result = #{}
try{
$request = [System.Net.WebRequest]::Create('http://localhost:9000/stats')
$request.Method = 'POST'
$request.ContentType = 'application/json'
$request.Accept = "application/json"
$body = [byte[]][char[]]$params
$upload = $request.GetRequestStream()
$upload.Write($body, 0, $body.Length)
$upload.Flush()
$upload.Close()
$response = $request.GetResponse()
$stream = $response.GetResponseStream()
$streamReader = [System.IO.StreamReader]($stream)
$result['StatusCode'] = $response.StatusCode
$result['StatusDescription'] = $response.StatusDescription
$result['Content'] = $streamReader.ReadToEnd()
$streamReader.Close()
$response.Close()
}
catch{
throw
}
# I suggest checking $result.StatusCode here first..
$x = $result.Content | ConvertFrom-Json

Coinspot API with PowerShell

I'm struggling to access the Coinspot API from PowerShell. No matter what I do I always get the "no nonce" error back from the API:
$VerbosePreference = 'Continue'
$key = ''
$secret = ''
$epoc_start_date = ("01/01/1970" -as [DateTime])
[int]$nonce = ((New-TimeSpan -Start $epoc_start_date -End ([DateTime]::UtcNow)).TotalSeconds -as [string])
$baseUrl = 'www.coinspot.com.au/api'
$resourcePath = '/my/orders'
$url = 'https://{0}{1}&nonce={2}' -f $baseUrl, $resourcePath, $nonce
$encoded = New-Object System.Text.UTF8Encoding
$url_bytes = $encoded.GetBytes($url)
# create hash
$hmac = New-Object System.Security.Cryptography.HMACSHA512
$hmac.key = [Text.Encoding]::ASCII.GetBytes($secret)
$sha_result = $hmac.ComputeHash($url_bytes)
#remove dashes
$hmac_signed = [System.BitConverter]::ToString($sha_result) -replace "-";
$headers = #{
sign = $hmac_signed
key = $key
'content-type' = 'application/json'
}
$result = Invoke-RestMethod -Uri $url -Method Post -Headers $headers
$result
Alternatively I have already tested this:
$VerbosePreference = 'Continue'
$key = ''
$secret = ''
$epoc_start_date = ("01/01/1970" -as [DateTime])
[int]$nonce = ((New-TimeSpan -Start $epoc_start_date -End ([DateTime]::UtcNow)).TotalSeconds -as [string])
$baseUrl = 'www.coinspot.com.au/api'
$resourcePath = '/my/orders'
$url = 'https://{0}{1}' -f $baseUrl, $resourcePath
$body = #{
nonce = $nonce
}
$encoded = New-Object System.Text.UTF8Encoding
$body_bytes = $encoded.GetBytes($body)
# create hash
$hmac = New-Object System.Security.Cryptography.HMACSHA512
$hmac.key = [Text.Encoding]::ASCII.GetBytes($secret)
$sha_result = $hmac.ComputeHash($body_bytes)
#remove dashes
$hmac_signed = [System.BitConverter]::ToString($sha_result) -replace "-";
Invoke-RestMethod -Uri $url -Method Post -Headers #{sign = $hmac_signed ; key = $key ; 'content-type' = 'application/json' } -Body $($body | ConvertTo-Json)
The second gives me an invalid status error.
I have a feeling there's something wrong with my header.
Coinspot support responded:
Apologies for this.
Our current API system is way out of date and needs to be updated.
We know that we need to support the developers as best as we can but our current dev team are very busy with other things at the
moment.
They are aware of this and plan to update it as soon as possible, but right now there is no ETA for this.
Very sorry the inconvenience.

Coinbase Exchange API with PowerShell

I feel like I'm really close to being able to use the Coinbase Exchange API with PowerShell, but I'm having trouble creating a valid signature. Requests that don't require a signature, like /time and /products, work great.
Here's what I have so far.
$api = #{
"endpoint" = 'https://api.gdax.com'
"url" = '/account'
"method" = 'GET'
"body" = ''
"key" = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
"secret" = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
"passphrase" = 'xxxxxxxx'
}
# Base64 encoding/decoding functions derived from
# http://vstepic.blogspot.com/2013/02/how-to-convert-string-to-base64-and.html
function Base64-Encode($string) {
$conversion = [System.Text.Encoding]::ASCII.GetBytes($string)
return [System.Convert]::ToBase64String($conversion)
}
function Base64-Decode($string) {
$conversion = [System.Convert]::FromBase64String($string)
return [System.Text.Encoding]::ASCII.GetString($conversion)
}
# HMAC SHA256 code derived from
# http://www.jokecamp.com/blog/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/
function hmac($message, $secret) {
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($secret)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
$signature = [Convert]::ToBase64String($signature)
return $signature
}
function Submit-Request($request) {
$unixEpochStart = Get-Date -Date "01/01/1970"
$now = Get-Date
$timestamp = (New-TimeSpan -Start $unixEpochStart -End $now.ToUniversalTime()).TotalSeconds.ToString()
# create the prehash string by concatenating required parts
$prehash = $timestamp + $request.method.ToUpper() + $request.url + $request.body
$signature_b64 = hmac -message $prehash -secret (Base64-Decode $request.secret)
$header = #{
"CB-ACCESS-KEY" = $request.key
"CB-ACCESS-SIGN" = $signature_b64
"CB-ACCESS-TIMESTAMP" = $timestamp
"CB-ACCESS-PASSPHRASE" = $request.passphrase
"Content-Type" = 'application/json'
}
$uri = $request.endpoint + $request.url
if ($request.method.ToUpper() -eq 'POST') {
$response = Invoke-RestMethod -Method $request.method -Uri $uri -Headers $header -Body $request.body
} else {
$response = Invoke-RestMethod -Method $request.method -Uri $uri -Headers $header
}
return $response
}
$api.method = 'GET'
$api.url = '/account'
$response = Submit-Request $api
Write-Output $response
After reviewing some C# code in the Coinbase community, I was able to revise my code and get it working. Decoding of the secret key did not need to go to a string format, which is what was happening when I called Base64-Decode before passing the secret to the HMAC function. I followed the C# example and decoded it directly in the HMAC function without making a string out of it. Another change I made was to make the timestamp match the format retrieved from /time, using 3 decimal places instead of 5.
Here's my revised code. I hope it will be of help to others.
$api = #{
"endpoint" = 'https://api.gdax.com'
"url" = '/account'
"method" = 'GET'
"body" = ''
"key" = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
"secret" = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
"passphrase" = 'xxxxxxxx'
}
# Base64 encoding/decoding functions derived from
# http://vstepic.blogspot.com/2013/02/how-to-convert-string-to-base64-and.html
function Base64-Encode($string) {
$conversion = [System.Text.Encoding]::ASCII.GetBytes($string)
return [System.Convert]::ToBase64String($conversion)
}
function Base64-Decode($string) {
$conversion = [System.Convert]::FromBase64String($string)
return [System.Text.Encoding]::ASCII.GetString($conversion)
}
# HMAC SHA256 code derived from
# http://www.jokecamp.com/blog/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/
function hmac($message, $secret) {
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Convert]::FromBase64String($secret)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
$signature = [Convert]::ToBase64String($signature)
return $signature
}
function Submit-Request($request) {
$unixEpochStart = Get-Date -Date "01/01/1970"
$now = Get-Date
$timestamp = (New-TimeSpan -Start $unixEpochStart -End $now.ToUniversalTime()).TotalSeconds
# round timestamp to 3 decimal places and convert to string
$timestamp = ([math]::Round($timestamp, 3)).ToString()
# create the prehash string by concatenating required parts
$prehash = $timestamp + $request.method.ToUpper() + $request.url + $request.body
$signature_b64 = hmac -message $prehash -secret $request.secret
$header = #{
"CB-ACCESS-KEY" = $request.key
"CB-ACCESS-SIGN" = $signature_b64
"CB-ACCESS-TIMESTAMP" = $timestamp
"CB-ACCESS-PASSPHRASE" = $request.passphrase
"Content-Type" = 'application/json'
}
$uri = $request.endpoint + $request.url
if ($request.method.ToUpper() -eq 'POST') {
$response = Invoke-RestMethod -Method $request.method -Uri $uri -Headers $header -Body $request.body
} else {
$response = Invoke-RestMethod -Method $request.method -Uri $uri -Headers $header
}
return $response
}
$api.method = 'GET'
$api.url = '/accounts'
$response = Submit-Request $api
Write-Output $response