Coinigy V2 授权问题 API

Issue with authorization on Coinigy V2 API

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

program.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;
}
}

以及私人通话的多个版本(以查看调用 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 键不适用于 V2 api。如果您升级订阅,您可能需要生成一个新密钥才能生效。

这里有一个 class 如果不能解决问题的话应该会有所帮助:

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