Coinbase Exchange API with PowerShell - 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

Related

powershell cannot bind error on if else block

I am working on a requirement where I have to check if the api call needs to be looped over or not. I am using the below code to accomplish this requirement. If I take out the if else block and write for either loop no loop things work as expected.
PoSh:
$Loop = "1" # 0 for no looping 1 for looping
if ($Loop -eq 1) {
$Header = #{
"authorization" = "Bearer $token"
}
#make REST API call
$Parameters = #{
Method = "GET"
Headers = $Header
ContentType = "application/json"
Body = $BodyJson
}
$startYear = 2014
$endYear = 2022
$Data = {for($year=$startYear; $i -le $endYear; $year=$year+1) {Invoke-RestMethod -Uri "https://api.mysite.com/v1/data/year/Year/" + [string]$year #Parameters -DisableKeepAlive -ErrorAction Stop}} | ConvertTo-Json
}
else {Write-Output "No loop"
$Header = #{
"authorization" = "Bearer $token"
}
#make REST API call
$Parameters = #{
Method = "GET"
Headers = $Header
ContentType = "application/json"
Body = $BodyJson
}
$Data = Invoke-RestMethod -Uri "https://api.mysite.com/v1/data" #Parameters -DisableKeepAlive -ErrorAction Stop | ConvertTo-Json
}
Error:
Cannot bind parameter because parameter 'Uri' is specified more than once. To provide multiple values to parameters that can accept multiple values, use the array syntax.
I have of course no idea what your https://api.mysite.com/v1/data would return and if it is actually needed to convert the returned data to Json at all, but continuing from my comments, try
# make this a Boolean value for clarity
$Loop = $true # $false for no looping $true for looping
# splatting Hashtable for REST API call
$Parameters = #{
Method = "GET"
Headers = #{ "Authorization" = "Bearer $token" }
ContentType = "application/json"
Body = $BodyJson
# you can incorporate these parameters as well
DisableKeepAlive = $true
ErrorAction = 'Stop'
}
if ($Loop) {
Write-Host "Start looping.."
$startYear = 2014
$endYear = 2022
# use the $() subexpression to combine the various outputs and convert that to Json
$Data = $(for ($year = $startYear; $year -le $endYear; $year++) {
Invoke-RestMethod -Uri "https://api.mysite.com/v1/data/year/Year/$year" #Parameters
}) | ConvertTo-Json
}
else {
Write-Host "No loop"
$Data = Invoke-RestMethod -Uri "https://api.mysite.com/v1/data" #Parameters | ConvertTo-Json
}
P.S. The error you saw in your code was caused by the wrong variable you used in the for loop with $i -le $endYear instead of $year -le $endYear. That and the fact that you put the whole loop inside a scriptblock made variables $startYear and $endYear invisible..

Unable to Get Blob using powershell

I have been trying to GET Blob using the script below but it gives an error highlighted below. I am using the script from this link and the $signatureString is used according to the this link. In a way, what i understand from technet that the authorization header should have the following
Convert the PS data to JSON
$json = $authHeader | ConvertTo-Json
$method = "GET"
$headerDate = (get-date -format r).ToString()
$headers = #{"x-ms-version"="$headerDate"}
$StorageAccountName = ""
$StorageContainerName = "users"
$StorageAccountKey = ""
$Url = "https://$StorageAccountName.blob.core.windows.net/$StorageContainerName/******/*****.rdp"
$body = "Hello world"
$xmsdate = (get-date -format r).ToString()
$headers.Add("x-ms-date",$xmsdate)
$contentLength = $body.Length
$headers.Add("Content-Length","$contentLength")
$headers.Add("x-ms-blob-type","BlockBlob")
$contentType = "application/json"
#$headers.Add("Content-Type","$contentType")
$signatureString = "$method$([char]10)$([char]10)$([char]10)$contentLength$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)$([char]10)"
#Add CanonicalizedHeaders
$signatureString += "x-ms-blob-type:" + $headers["x-ms-blob-type"] + "$([char]10)"
$signatureString += "x-ms-date:" + $headers["x-ms-date"] + "$([char]10)"
$signatureString += "x-ms-version:" + $headers["x-ms-version"] + "$([char]10)"
#Add CanonicalizedResource
$uri = New-Object System.Uri -ArgumentList $url
$signatureString += "/" + $StorageAccountName + $uri.AbsolutePath + $([char]10) + "restype:container"
$dataToMac = [System.Text.Encoding]::UTF8.GetBytes($signatureString)
$accountKeyBytes = [System.Convert]::FromBase64String($StorageAccountKey)
$hmac = new-object System.Security.Cryptography.HMACSHA256((,$accountKeyBytes))
$signature = [System.Convert]::ToBase64String($hmac.ComputeHash($dataToMac))
$headers.Add("Authorization", "SharedKey " + $StorageAccountName + ":" + $signature);
write-host -fore green $signatureString
write-host -fore green $headers
Invoke-RestMethod -Uri $Url -Method $method -headers $headers
Please someone assist me on this.
I am sorry! yes it was not that descriptive. here is the screenshot
InvalidHeaderValue-Incorrect format
I am actually trying to access azure blob storage using powerShell. today, i tried other script to do the same but I get a different error, please see the script and screenshot of that error message below.
$storageAccount = ""
$accesskey = ""
$version = "2017-04-17"
$resource = "tets"
$table_url = "https://$storageAccount.blob.core.windows.net/$resource"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$stringToSign = "$GMTTime`n/$storageAccount/$resource"
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Convert]::FromBase64String($accesskey)
$signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
$signature = [Convert]::ToBase64String($signature)
$headers = #{
'x-ms-date' = $GMTTime
Authorization = "SharedKeyLite " + $storageAccount + ":" + $signature
"x-ms-version" = $version
Accept = "application/json;odata=fullmetadata"
}
$item = Invoke-RestMethod -Method GET -Uri $table_url -Headers $headers -ContentType application/json
return $item.value
Here is the error message I get.
AuthenticationFailedServer
Consider using the Azure PowerShell modules to access blobs rather than raw API requests. All of the authentication details should be handled for you already.

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.

Bittrex API call with powershell fails with INVALID_SIGNATURE

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