c# - Coinigy V2 API 授权问题

标签 c# http bitcoin cryptocurrency

我一直在从事一个项目,我曾经使用 v1 api 进行 coinigy 并且喜欢这个 API。现在问题出在新的 v2 API 上,我在尝试使用带有 hmac_sha256 签名的私有(private) API 时遇到了问题。我最初联系了 coinigy 支持,但运气不好无法让它完全正常工作(他们确实提供了帮助,但从未解决问题,这很奇怪)。我用 C# 构建了一个简单的测试项目,以使用他们的 api 文档执行私有(private)调用。

程序.cs:

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace coinigy_example
{
class Program
{

    static void Main(string[] args)
    {
        string api_key = "API_KEY";
        string api_secret = "API_SECRET";
        CoinigyApiPrivateCall call = new CoinigyApiPrivateCall(api_key, api_decret);


        string data = call.HttpPostRequest("private/exchanges/" + "BINA" + "/markets/" + "BTC" + "/" + "LTC" + "/ticker", "", null, "GET");
        string body = null;
        call.xcoinApiCall("private/exchanges/" + "BINA" + "/markets/" + "BTC" + "/" + "LTC" + "/ticker", "", ref body);


        try
        {
            JObject j = JObject.Parse(data);

            TickerDataCoinigy tt = new TickerDataCoinigy();
            tt.volume = (string)j["volume"];
            tt.last = (string)j["last"];
            tt.high = (string)j["high"];
            tt.low = (string)j["low"];
            tt.ask = (string)j["ask"];
            tt.bid = (string)j["bid"];

            Console.WriteLine("Binance, BTC-LTC ticker data:");

            Console.WriteLine("Volume: " + tt.volume);
            Console.WriteLine("Last: " + tt.last);
            Console.WriteLine("High: " + tt.high);
            Console.WriteLine("Low: " + tt.low);
            Console.WriteLine("ask: " + tt.ask);
            Console.WriteLine("bid: " + tt.bid);
        }
        catch(Exception i)
        {
            Console.WriteLine("");
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(data);
            Console.ForegroundColor = ConsoleColor.White;

        }

        Console.ReadLine();
    }


}

public class TickerDataCoinigy
{
    public string volume;
    public string last;
    public string high;
    public string low;
    public string ask;
    public string bid;
}
}

还有私有(private)电话的几个版本(以查看调用 api 的不同方式是否可行)。

CoinigyApiPrivateCall.cs

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace coinigy_example
{
public class CoinigyApiPrivateCall
{
    private string api_key;
    private string api_secret;

    public CoinigyApiPrivateCall(string api_key, string api_secret)
    {
        this.api_key = api_key;
        this.api_secret = api_secret;
    }

    public static double ConvertToUnixTimestamp(DateTime date)
    {
        DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        TimeSpan diff = date.ToUniversalTime() - origin;
        return Math.Floor(diff.TotalMilliseconds);
    }

    private string Hash_HMAC(string sKey, string sData)
    {
        byte[] rgbyKey = Encoding.ASCII.GetBytes(sKey);


        using (var hmacsha256 = new HMACSHA256(rgbyKey))
        {
            byte[] inf = hmacsha256.ComputeHash(Encoding.ASCII.GetBytes(sData));

            return (ByteToString(inf));
        }
    }

    private string ByteToString(byte[] rgbyBuff)
    {
        string sHexStr = "";


        for (int nCnt = 0; nCnt < rgbyBuff.Length; nCnt++)
        {
            sHexStr += rgbyBuff[nCnt].ToString("x2"); // Hex format
        }

        return (sHexStr);


    }

    private byte[] StringToByte(string sStr)
    {
        byte[] rgbyBuff = Encoding.ASCII.GetBytes(sStr);

        return (rgbyBuff);
    }



    public string HttpPostRequest(string url, string au, List<KeyValuePair<string, string>> postdata, string httpType)
    {
        var client = new HttpClient();

        //client.DefaultRequestHeaders.Add("User-Agent", ua);
        //client.DefaultRequestHeaders.Add("X-API-KEY", api_key);
        //client.DefaultRequestHeaders.Add("X-API-SECRET", api_secret);
        string time = Convert.ToString(ConvertToUnixTimestamp(DateTime.Now.ToUniversalTime()));
        FormUrlEncodedContent content = null;
        string data = null;
        if (postdata != null)
        {
            content = new FormUrlEncodedContent(postdata);
            var output = Newtonsoft.Json.JsonConvert.SerializeObject(postdata);
            data = api_key + time + httpType.ToUpper() + "/api/v2/" + url  + output;
        }
        else
        {
            data = api_key + time + httpType.ToUpper() + "/api/v2/" + url;
        }
        string sign = Hash_HMAC(api_secret, data);
        client.DefaultRequestHeaders.Add("X-API-SIGN", sign);
        client.DefaultRequestHeaders.Add("X-API-TIMESTAMP", time);

        HttpResponseMessage response = null;
        if (httpType.ToUpper() == "POST")
        {
            response = client.PostAsync("https://api.coinigy.com/api/v2/" + url, content).Result;
        }

        if (httpType.ToUpper() == "GET")
        {
            response = client.GetAsync("https://api.coinigy.com/api/v2/" + url).Result;
        }

        if (httpType.ToUpper() == "PUT")
        {
            response = client.PutAsync("https://api.coinigy.com/api/v2/" + url, content).Result;
        }

        if (httpType.ToUpper() == "DELETE")
        {
            response = client.DeleteAsync("https://api.coinigy.com/api/v2/" + url).Result;
        }



        return response.IsSuccessStatusCode
            ? response.Content.ReadAsStringAsync().Result
            : "ERROR:" + response.StatusCode + " " + response.ReasonPhrase + " | " + response.RequestMessage;
    }

    public JObject xcoinApiCall(string sEndPoint, string sParams, ref string sRespBodyData)
    {
        string sAPI_Sign = "";
        string sPostData = sParams;
        string sHMAC_Key = "";
        string sHMAC_Data = "";
        string sResult = "";
        double nNonce = 0;
        HttpStatusCode nCode = 0;


        sPostData += "&endpoint=" + Uri.EscapeDataString(sEndPoint);

        try
        {
            HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://api.coinigy.com/api/v2/" + sEndPoint);
            byte[] rgbyData = Encoding.ASCII.GetBytes(sPostData);


            nNonce = ConvertToUnixTimestamp(DateTime.Now.ToUniversalTime());

            sHMAC_Key = this.api_secret;
            sHMAC_Data = api_key + nNonce.ToString() + "GET" +  "/api/v2/"   + sEndPoint;
            //sHMAC_Data = sEndPoint + (char)0 + sPostData + (char)0 + nNonce.ToString();
            sResult = Hash_HMAC(sHMAC_Key, sHMAC_Data);
            //sAPI_Sign = Convert.ToBase64String(StringToByte(sResult));
            sAPI_Sign = sResult;

            Request.Headers.Add("X-API-SIGN", sAPI_Sign);
            Request.Headers.Add("X-API-TIMESTAMP", nNonce.ToString());

            Request.Method = "GET";
            Request.ContentType = "application/x-www-form-urlencoded";
            Request.UserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36";
            /*Request.ContentLength = rgbyData.Length;

            using (var stream = Request.GetRequestStream())
            {
                stream.Write(rgbyData, 0, rgbyData.Length);
            } */

            var Response = (HttpWebResponse)Request.GetResponse();

            sRespBodyData = new StreamReader(Response.GetResponseStream()).ReadToEnd();

            return (JObject.Parse(sRespBodyData));
        }
        catch (WebException webEx)
        {
            using (HttpWebResponse Response = (HttpWebResponse)webEx.Response)
            {
                nCode = Response.StatusCode;

                using (StreamReader reader = new StreamReader(Response.GetResponseStream()))
                {
                    sRespBodyData = reader.ReadToEnd();
                    try
                    {
                        return (JObject.Parse(sRespBodyData));
                    }
                    catch(Exception i)
                    {

                    }
                }
            }
        }

        return (null);
    }



}
}

如果有人对此有任何了解可以提供帮助,我将不胜感激。

编辑:我使用了 Joe 的代码来查看是否可以修复未经授权的问题,我得到了这个结果:

{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: 
System.Net.Http.StreamContent, Headers:
{
  Date: Sun, 29 Jul 2018 19:19:36 GMT
  Content-Length: 0
}}

我正在使用这段代码来执行 http 调用:

   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Text;
   using System.Threading.Tasks;
   using Coinigy_v2_api_2018.Functions;
   using System.Net.Http;

   namespace Coinigy_v2_api_test
   {
   class Program
   {
    static void Main(string[] args)
    {
        ApiRequest req = new ApiRequest();
        req.BaseUrl = "https://api.coinigy.com";
        req.Body = "";
        req.Method = "GET";
        req.Secret = "secret";
        req.Key = "key";
        req.Endpoint = "/api/v2/private/exchanges";

        string signature = req.Signature;
        HttpResponseMessage response = null;
        using (HttpClient client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("X-API-SIGN", signature);
            client.DefaultRequestHeaders.Add("X-API-TIMESTAMP", req.Timestamp);
            response = client.GetAsync(req.BaseUrl + req.Endpoint).Result;
        }
        string r = null;
        if (response.IsSuccessStatusCode)
        {
            r = response.Content.ReadAsStringAsync().Result;
        }

        Console.WriteLine(r);
        Console.ReadLine();


    }
}
}

再次提前感谢您!

最佳答案

API 文档可以在这里找到:https://api.coinigy.com/api/v2/docs/#/

2019-08-28:最新更新是端点在生成签名时必须要求查询参数未编码。

首先要检查的是确保您使用的是 V2 api key / secret 。旧的 V1 key 不适用于 V2 api。如果您升级订阅,您可能需要生成一个新 key 才能生效。

如果不能解决问题,这里有一个类应该可以提供帮助:

public class ApiRequest
{
    public string BaseUrl { get; set; }
    public string Endpoint { get; set; }
    public string Key { get; set; }
    public string Secret { get; set; }
    public string Method { get; set; }
    public string Body { get; set; }
    public string Timestamp { get; set; } = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();


    /// <summary>
    /// Each API request needs to be signed so the sender can be identified.
    /// This property generates the signature based on the current request.
    /// </summary>
    public string Signature
    {
        get
        {
            var asciiEncoding = new ASCIIEncoding();
            var hmac = new HMACSHA256(asciiEncoding.GetBytes(Secret));
            string signature = Key + Timestamp + Method.ToUpper() + Endpoint + (Body ?? string.Empty);
            byte[] signatureBytes = asciiEncoding.GetBytes(signature);
            byte[] hashedSignatureBytes = hmac.ComputeHash(signatureBytes);
            string hexSignature = string.Join(string.Empty, Array.ConvertAll(hashedSignatureBytes, hb => hb.ToString("X2")));

            return hexSignature;
        }
    }
}

如果您正确设置这些值,您应该能够生成正确的签名。

对于参数值,从类似这样的东西开始并与我生成的签名进行比较以确保您的签名功能正常工作:

BaseUrl   : https://api.coinigy.com
Endpoint  : /api/v2/private/exchanges?pythagoreanTheorem=a%5E2%2Bb%5E2%3Dc%5E2
Key       : keykeykeykeykeykeykeykeykeykeyke
Secret    : secretsecretsecretsecretsecretse
Method    : GET
Timestamp : 1532718830 (which is 2018-07-27T19:13:50.6694555Z)
Body      : (empty string)

Signature : B618C0B3C92632C701D7CEFC00AC9C8A0771989B21E00D61D4945F79239D2F87

这是一个额外的 python3 版本,它将执行整个过程:https://gist.github.com/phillijw/1f78c8bafdce3a71a0b2ef9d4f5942a1

关于c# - Coinigy V2 API 授权问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51528701/

相关文章:

java - 安卓错误: IllegalStateException

c# - 为什么总是需要在具有 IDisposable 成员的对象上实现 IDisposable?

http - HTTP PUT 请求是否需要包含正文?

c# - Dapper.NET 和 IQueryable

api - Postman 对 Laravel 中间件异常没有任何响应

html - 省略协议(protocol)(方案)的绝对 URL 以保留当前页面之一

c# - NBitcoin 并从 xpub key 生成地址

php - 为什么 walletnotify 会出现 3 次?

c# - MonoTouch 的 QR 库?

c# - Visual Studio 2015、Nuget 和 “same key has already been added.”