How to add extra x-signature header with WebClient? - vb.net

This is my current code.
Public Shared Function downloadString1(url As String, post As String, signature As String) As String
Dim wc = New CookieAwareWebClient
Dim result = wc.UploadString(url, post)
Return result
End Function
I need to insert post, url, and I should add x signature header I think
Now with wc.UploadString, I can do posts. But how do I add that extra x-signature header with WebClient?
This is the API instruction for yobit
https://yobit.net/en/api/

Related

Setting multiple headers within httpclient on vb.net

I am working on sending data to Spiff (https://app.getguru.com/card/iXpEBagT/How-to-send-data-to-Spiffs-API) but I'm having trouble getting the headers they want added. From the linked site here is their example:
curl -X PUT -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'Signature: t=1606767397,v1=DIGEST' -d '{"Id":"Thor","nickname":"Strongest Avenger"}' https://app.spiff.com/_webhook/mapping/123
So using VB.Net (Core 6) I'm attempting to do this with a HttpClient but keep getting a Status Code 400: Bad Response. Here is my sample code:
Public Async Function SendRequest(uri As Uri, data As String, signature As String) As Task(Of JsonObject)
Dim httpClient = New HttpClient()
httpClient.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Signature", signature)
Dim content = New StringContent(data, Encoding.UTF8, "application/json")
Dim response = New HttpResponseMessage
response = Await httpClient.PutAsync(uri, content)
response.EnsureSuccessStatusCode()
End Function
I'm guessing the BadRequest is due to the extra Signature header but I can't figure out what I'm doing wrong. I'm using the default request headers since the program executes, sends the data, then ends so I have no need to keep the httpclient around or reuse it. I tried using a WebRequest but on top of being depreciated I had the same issues. I thought maybe I'm messing up the signature as they use HMAC verification but I'm pretty sure that is correct also based on their documentation:
Private Sub SendDataButton_Click(sender As Object, e As EventArgs) Handles SendDataButton.Click
Dim myKey As String = HMACKeyTextBox.Text
Dim myUnixTime As Long = New DateTimeOffset(Date.Now.ToUniversalTime).ToUnixTimeSeconds
Dim myData as String = "{""PrimaryKey"":""2""}"
Dim myPreDigestString = $"{myUnixTime}.{myData}"
Dim myDigest As String = GetHMACSHA256Hash(myPreDigestString, myKey)
Dim myURI As New Uri(URITextBox.Text)
Dim mySignature As String = $"t={myUnixTime},v1={myDigest}"
Dim result_post = SendRequest(myURI, myData, mySignature)
End Sub
Which is based on some other posts on here. Am I adding the headers wrong or does this look correct? The test data is what they are expecting from me for testing, a single record setting a PrimaryKey. I should be getting back a 200 OK.

Convert an unknown structure to an untyped Object in VB.NET

I'd like to convert an unknown basic structure to an Object (no type here).
I'm building a library that will be used by many users to extract data from my system but don't want to do a new function for everyone of them. They have to know what will be the result.
In vb, it is possible to create an Object with some properties and use it as it is a regular Class like so:
Dim myObj as New With { .name = "Matt", .age = "28" }
MsgBox( myObj.name & " is now " & myObj.age & " years old.")
So far, so good.
Next step : my user will give me some instructions that I need to extract data from various DBs, and I've no idea of what the result will be.
What I know after the execution is a list of String containing the columns of the result set and, of course a (set of) rows.
And here is the problem of course
My function (for a single row) so far:
Public Function GetData(ByVal instructions as String) as Object ' User is supposed to know what will be inside, instructions is as XML describing DB, table, query, ...
' Do what is needed to retrieve data
' Here I have a variable cols As List(Of String) ' e.g. ("BP", "NAME", "VAT")
Dim o As New With ???
Return o
End Function
What I've tried: build a fake JSon on the fly, and try to Deserialize to Object.
But even if it seems to work, I (and the user) can't access the property as in my top piece of code like:
MsgBox(o.BP)
I know that I could do
Public Function GetData(Of T As {New})(ByVal instructions as String) As T
Dim o As T
' Use some Reflexion to TryInvokeMember of T
Return o
End Function
But I wanted to remove the hassle to create a class to use my code.
Plus, My librairy will be use in a webservice and the class of the user is then unknown.
One approach could be - to use Dictionary(Of String, Object)
Public Function GetData(instructions as String) As Dictionary(Of String, Object)
Dim data = ' Load data
Dim columns As String() = { "BP", "NAME", "VAT" }
Return columns.ToDictionary(
Function(column) column,
Function(column) data.GetByColumnName(column)
)
End Function
` Usage
Dim result = GetDate("instructions: ['BP', 'NAME']")
' Because user knows it is Integer
Dim bpValue = DirectCast(result.Item("BP"), Integer)
Thanks to #GSerg, #Fabio and a few other searches about ExpandoObject, I did it !
Imports System.Dynamic
Dim o As Object = New ExpandoObject()
For Each col In cols
DirectCast(o, IDictionary(Of String, Object)).Add(col, row.GetString(col))
Next

Action in controller with two parameters - how to set up route and controller?

I have this controller:
Public Class UsersController
Inherits ApiController
Public reportsjsonfilepath = System.AppDomain.CurrentDomain.BaseDirectory & "\reports.json"
<HttpGet>
<Route("")>
Public Function Index() As HttpResponseMessage
Log.Information("Main index requested at {0}", DateTime.Now)
Dim response As StringBuilder = New StringBuilder
response.Append("Index requested at: " & DateTime.Now)
response.Append("<br>")
response.Append("Hello, this is a test WebApi server!")
Dim raspuns = String.Join("/n", response.ToString)
Dim raspunsindex = Request.CreateResponse(Of String)(HttpStatusCode.OK, raspuns)
raspunsindex.Content.Headers.ContentType = New MediaTypeHeaderValue("text/html")
Return raspunsindex
End Function
<HttpGet>
<Route("users")>
Public Function Users() As HttpResponseMessage
Log.Information("Users index requested at {0}", DateTime.Now)
Dim response As StringBuilder = New StringBuilder()
Dim dictionarusers As IDictionary(Of String, String) = GetUsersList()
Dim i As Integer = 0
For Each entry As KeyValuePair(Of String, String) In dictionarusers
i = i + 1
response.Append(i)
response.Append(" - ")
response.Append(entry.Value)
response.Append("<br>")
Next
Dim raspuns = Request.CreateResponse(Of String)(HttpStatusCode.OK, response.ToString)
raspuns.Content.Headers.ContentType = New MediaTypeHeaderValue("text/html")
Return raspuns
End Function
<HttpGet>
Public Function GetQlikLink(username As String, reportId As Integer) As HttpResponseMessage
QlikLink.GetLink(username, reportId)
End Function
End Class
The routes are set up like this:
Public Module RoutesConfig
<Extension()>
Sub MapDefinedRoutes(ByVal config As HttpConfiguration)
config.Routes.MapHttpRoute(name:="Relevance", routeTemplate:="api/{controller}", defaults:=New With {
.id = RouteParameter.[Optional]
})
config.Routes.MapHttpRoute("QlikLink", "api/{controller}/{action}/{id}", New With {
.id = RouteParameter.[Optional]
})
End Sub
End Module
Now, when I go http://localhost:9000/relevance, the index kicks in OK. Same for http://localhost:9000/relevance/users. But how one must set-up the action and the route to get something by getting the params from the request? How the parameters are sent: ?username=somestring&?id=2? I am talking about function GetQlikLink, the last one from the Controller.
Any hint will be appreciated! Thanks a lot!
For actions where the parameters are different to those defined in the routing configuration, then the query string keys should be the same as the function parameter names.
http://..../GetQlikLink?username=xyz&reportId=1234
I would recommend reading up about the different types of routing.
The config which you have shown looks like convention based routing.
However, you also appear to be adding routing attributes to your action methods.
Convention based routing lets you define route templates, and the pattern of things like {controller} and {action} must match the names of the controller and actions.
Routing and Action Selection in ASP.NET Web API
The other articles in this section also describe attribute routing.

How Should I compute signatures in API?

I want to create API for yobit. For now, I will do something very simple, namely get info.
In Python, the code sort of looks like this
url = 'https://yobit.net/tapi'
values['method'] = method
values['nonce'] = str(int(time.time()))
body = urlencode(values)
signature = hmac.new(self.secret, body, hashlib.sha512).hexdigest()
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Key': self.key,
'Sign': signature
}
I basically want to replace code hmac.new(self.secret, body, hashlib.sha512).hexdigest() with vb.net equivalent.
This my current code
Public Overrides Sub readbalances()
Dim secret = _secret
Dim url = "https://yobit.net/tapi"
Dim key = New Tuple(Of String, String)("Key", _apiKey)
Dim parameters = "method=getInfo&nonce=" + Str(Int(DateTime.Now))
Dim sig = New System.Security.Cryptography.HMACSHA512().ToString 'What should I put here?
Dim sigtupple = New Tuple(Of String, String)("Sign", sig)
Dim result = CookieAwareWebClient.downloadString1("url", {key, sigtupple}, parameters)
End Sub
What should I do?
Content of downloadString1 is the following but let's just say it performs as expected
Public Shared Function downloadString1(url As String, headers As Tuple(Of String, String)(), postdata As String) As String
Dim wc = New CookieAwareWebClient
For Each header In headers
wc.Headers.Add(header.Item1, header.Item2)
Next
Dim result = wc.UploadString(url, postdata)
Return result
End Function

YoBit tapi problems with authetincation

I am trying to write simple application for myself and when i try to call
getInfo method i always get a error into the response. Key, sign, method or nonce is incorrect. I found a number of examples but i still can't find mistake in my code. Could anyone help me with it?
The code works fine for hitbtc. I know yobit is a bit different but I think I have accomodate that.
My code:
Protected Shared Function readStrings(signatureheader As String, host As String, pathandQuery As String, post As String, secret As String, hasher As System.Security.Cryptography.HMAC, otherHeaders As Tuple(Of String, String)()) As String
'apikey=98998BEEB8796455044F02E4864984F4
'secret=44b7659167ffc38bb34fa35b5c816cf5
hasher.Key = exchanges.getBytes(secret)
Dim url = host + pathandQuery ' url = "https://yobit.net/tapi/"
Dim wc = New CookieAwareWebClient()
Dim sigHash2 = ""
If post = "" Then
sigHash2 = CalculateSignature2(pathandQuery, hasher)
Else
'post = "method=getInfo&nonce=636431012620"
sigHash2 = CalculateSignature2(post, hasher) 'sighash2= "ece0a3c4af0c68dedb1f840d0aef0fd5fb9fc5e808105c4e6590aa39f4643679af5da52b97d595cd2277642eb27b8a357793082007abe1a3bab8de8df24f80d2"
End If
wc.Headers.Add(signatureheader, sigHash2) ' SignatureHeader ="Sign"
Dim response = ""
For Each oh In otherHeaders ' otherHeaders =(0) {(Key, 98998BEEB8796455044F02E4864984F4)} System.Tuple(Of String, String)
wc.Headers.Add(oh.Item1, oh.Item2)
Next
'- wc.Headers {Sign: ece0a3c4af0c68dedb1f840d0aef0fd5fb9fc5e808105c4e6590aa39f4643679af5da52b97d595cd2277642eb27b8a357793082007abe1a3bab8de8df24f80d2 Key: 98998BEEB8796455044F02E4864984F4 } System.Net.WebHeaderCollection
'url = "https://yobit.net/tapi/"
'post = "method=getInfo&nonce=636431012620"
If post = "" Then
response = wc.DownloadString(url)
Else
response = wc.UploadString(url, post) 'response = response "{"success":0,"error":"invalid key, sign, method or nonce"}" String
End If
Return response
End Function
The code has been tested succesfully for hitbtc.
So the crypto part is correct. I put it here anyway for completeness
Protected Shared Function CalculateSignature2(text As String, hasher As System.Security.Cryptography.HMAC) As String
Dim siginhash = hasher.ComputeHash(exchanges.getBytes(text))
Dim sighash = exchanges.getString(siginhash)
Return sighash
End Function
So,
for sanity check
This code works
Public Overrides Sub readbalances()
Dim response = readStrings("X-Signature", "https://api.hitbtc.com", "/api/1/trading/balance?nonce=" + exchanges.getNonce().ToString + "&apikey=" + _apiKey, "", _secret, New System.Security.Cryptography.HMACSHA512(), {})
End Sub
With yobit things are different. I got to use post instead of get. I got to add more headers. However, I think I have fixed that.
It doesn't work.
The python function for yobit API is this I just need to translate that to vb.net which I think I have done faithfully
API Call Authentication in Python ( Working PHP example )
I think the mistake is around here
request_url = "https://yobit.net/tapi";
request_body = "method=TradeHistory&pair=ltc_btc&nonce=123";
signature = hmac_sha512(request_body,yobit_secret);
http_headers = {
"Content-Type":"application/x-www-form-urlencoded",
"Key":yobit_public_key,
"Sign":signature
}
response = http_post_request(request_url,request_body,http_headers);
result = json_decode(response.text);
There the stuff that I copied is method=getInfo&nonce=636431012620 which is what I put in post.
So that seems right.
Looks like it works.
I just need to change the nonce so that it's between 0 to 2^31
So this is the error
post = "method=getInfo&nonce=636431012620
The nonce shouldn't be that big. At most it should be
2147483646
Also though not documented, I must add
content type as one of the header. This is the final solution
Dim nonce = exchanges.getNonce().ToString
Dim content = hashObject("", nonce, "method=getInfo&nonce=")
Dim sighash = computeSig(content)
Dim result = CookieAwareWebClient.downloadString1("https://yobit.net/tapi/", content, {New Tuple(Of String, String)("Key", _apiKey), New Tuple(Of String, String)("Sign", sighash), New Tuple(Of String, String)("Content-Type", "application/x-www-form-urlencoded")})
So I added New Tuple(Of String, String)("Content-Type", "application/x-www-form-urlencoded") as one of the headers
Protected Overridable Function computeSig(content As String) As String
Dim hasher = New System.Security.Cryptography.HMACSHA512(System.Text.Encoding.UTF8.GetBytes(_secret))
Return CalculateSignature2(content, hasher)
End Function
Public Shared Function CalculateSignature2(content As String, hasher As System.Security.Cryptography.HMAC) As String
Dim siginhash = hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes(content))
Dim sighash = exchanges.getString(siginhash) 'convert bytes to string
Return sighash
End Function
Public Shared Function downloadString1(url As String, post As String, otherHeaders As Tuple(Of String, String)()) As String
Dim wc = New CookieAwareWebClient()
For Each oh In otherHeaders
wc.Headers.Add(oh.Item1, oh.Item2)
Next
Dim response = String.Empty
Try
If post = "" Then
response = wc.DownloadString(url)
Else
response = wc.UploadString(url, post)
End If
Catch ex As Exception
Dim a = 1
End Try
Return response
End Function