这个coinmex API有什么问题?

时间:2019-03-28 18:37:08

标签: vb.net rest api

Protected Overrides Function getJsonPrivate(method As String, otherParameters() As Tuple(Of String, String)) As String
    Dim base = "https://www.coinmex.com"
    Dim premethod = "/api/v1/spot/ccex/"
    Dim longmethod = premethod + method

    Dim timestampstring = getEstimatedTimeStamp().ToString

    Dim stringtosign = timestampstring + "GET" + longmethod + "{}" '1553784499976GET/api/v1/spot/ccex/account/assets{}

    Dim hasher = New System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(_secret1))
    Dim sighashbyte = hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringtosign))
    Dim signature = System.Convert.ToBase64String(sighashbyte) '"FIgrJFDOQctqnkOTyuv6+uTy6xw3OZiP4waC1u6P5LU="=
    Dim url = base + longmethod 'https://www.coinmex.com/api/v1/spot/ccex/account/assets

    '_apiKey1="cmx-1027e54e4723b09810576f8e7a5413**"
    '_passphrase1= 1Us6&f%*K@Qsqr**
    '
    Dim response = CookieAwareWebClient.downloadString1(url, "", {Tuple.Create("ACCESS-KEY", _apiKey1), Tuple.Create("ACCESS-SIGN", signature), Tuple.Create("ACCESS-TIMESTAMP", timestampstring), Tuple.Create("ACCESS-PASSPHRASE", _passphrase1)})

    Return response
End Function

Public Overrides Sub readbalances()
    typicalReadBalances("account/assets", "data", "currencyCode", "available", "frozen", "", {})
End Sub

我认为我做到了这里列出的内容 https://github.com/coinmex/coinmex-official-api-docs/blob/master/README_EN.md#1-access-account-information

# Request
GET /api/v1/spot/ccex/account/assets

# Response
[
    {
        "available":"0.1",
        "balance":"0.1",
        "currencyCode":"ETH",
        "frozen":"0",
        "id":1
    },
    {
        "available":"1",
        "balance":"1",
        "currencyCode":"USDT",
        "frozen":"0",
        "id":1
    }
]

对于签名

这是手册说的

  

ACCESS-SIGN标头是使用HMAC SHA256生成的输出   使用BASE64解码秘密密钥创建HMAC SHA256   prehash字符串以生成时间戳+方法+ requestPath +“?” +   queryString + body(其中“ +”代表字符串串联)和   BASE64编码的输出。时间戳记值与   ACCESS-TIMESTAMP标头。该主体是请求主体字符串或   如果没有请求正文(通常是GET请求),则省略。这个   方法应大写。

     

请记住,在将其用作HMAC的密钥之前,请先进行base64解码(   结果是64个字节)首先在64位字母数字上执行   密码字符串。此外,摘要输出是base64编码的   在发送标题之前。

     

除提交符号外,用户提交的参数必须经过签名。首先,   要签名的字符串根据参数名称排序(第一个   比较所有参数名称的首字母,按字母顺序排列,   如果遇到相同的第一个字母,则移至第二个字母   字母等等)。

     

例如,如果我们签署以下参数

curl "https://www.coinmex.com/api/v1/spot/ccex/orders?limit=100"       

Timestamp = 1590000000.281
Method = "POST"
requestPath = "/api/v1/spot/ccex/orders"
queryString= "?limit=100"
body = {
            'code': 'ct_usdt',
            'side': 'buy',
            'type': 'limit',
            'size': '1',
            'price': '1',
            'funds': '',
        }
     

生成要签名的字符串

Message = '1590000000.281GET/api/v1/spot/ccex/orders?limit=100{"code": "ct_usdt", "side": "buy", "type": "limit", "size": "1", "price": "0.1", "funds": ""}'
     

然后,使用私钥添加要签名的字符   参数以生成要签名的最终字符串。

     

例如:

hmac = hmac(secretkey, Message, SHA256)
Signature = base64.encode(hmac.digest())

我以为_secret1是base64字符串而不是utf8,所以我更改为

Dim base = "https://www.coinmex.com"
Dim premethod = "/api/v1/spot/ccex/"
Dim longmethod = premethod + method

Dim timestampstring = getEstimatedTimeStamp().ToString

'Dim stringtosign = timestampstring + "GET" + longmethod + "{}" '1553784499976GET/api/v1/spot/ccex/account/assets{} also doesn't work
Dim stringtosign = timestampstring + "GET" + longmethod  '1553784499976GET/api/v1/spot/ccex/account/assets

Dim hasher = New System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(_secret1)) 'secret looks like 43a90185f5b7ab25af045e9e64bac5dc745934f359f1806fcdd2a4af80ac2
Dim sighashbyte = hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringtosign))
Dim signature = Convert.ToBase64String(sighashbyte) '"FIgrJFDOQctqnkOTyuv6+uTy6xw3OZiP4waC1u6P5LU="=
Dim url = base + longmethod 'https://www.coinmex.com/api/v1/spot/ccex/account/assets

'_apiKey1="cmx-1027e54e4723b09810576f8e7a5413**"
'_passphrase1= 1Us6&f%*K@Qsq***
'
Dim response = CookieAwareWebClient.downloadString1(url, "", {Tuple.Create("ACCESS-KEY", _apiKey1), Tuple.Create("ACCESS-SIGN", signature), Tuple.Create("ACCESS-TIMESTAMP", timestampstring), Tuple.Create("ACCESS-PASSPHRASE", _passphrase1)})

Return response

也不起作用。

密钥(我删了几个字母)看起来像

43a90185f5b7ab25af045e9e64bac5dc745934f359f1806fcdd2a4af80ac2

这是应该解码为base 64还是utf8的东西吗?

规范说它是64。但是,它看起来不像是64编码的字符串。好像字母是从0-f

最佳答案将是: 1.告诉我代码中出了什么问题。我做了改变。尝试。跑。作品。很棒。

一个好的答案将 2.带有伪造/真实签名/随机性/密码短语和真实实际标头和签名的示例模拟。这样我就可以确切地看到错误的结果。

更新:我再次修改了代码。我将时间戳更改为秒,而不是milisecons。我删除了{}。我用两种方式。

    Dim base = "https://www.coinmex.com"
    Dim premethod = "/api/v1/spot/ccex/"
    Dim longmethod = premethod + method

    Dim timestampstring = (getEstimatedTimeStamp() / 1000).ToString

    Dim stringtosign = timestampstring + "GET" + longmethod  '1555154812.857GET/api/v1/spot/ccex/account/assets

    Dim hasher = New System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(_secret1)) '"43a90185f5b7ab25af045e9e64bac5dc745934f359f1806fcdd2a4af80ac2******
    Dim sighashbyte = hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringtosign))
    Dim signature = Convert.ToBase64String(sighashbyte) '"FIgrJFDOQctqnkOTyuv6+uTy6xw3OZiP4waC1u6P5LU="=
    Dim url = base + longmethod 'https://www.coinmex.com/api/v1/spot/ccex/account/assets

    '_apiKey1="cmx-1027e54e4723b09810576f8e7a5413**"
    '_passphrase1= 1Us6&f%*K@QsqrYZ
    '
    Dim response = CookieAwareWebClient.downloadString1(url, "", {Tuple.Create("ACCESS-KEY", _apiKey1), Tuple.Create("ACCESS-SIGN", signature), Tuple.Create("ACCESS-TIMESTAMP", timestampstring), Tuple.Create("ACCESS-PASSPHRASE", _passphrase1)})

    Return response

仍然无效。

当前错误为

消息=“远程服务器返回错误:(401)未经授权。”

我想提供一些只读的API密钥。不挂断。或创建一个空帐户,然后拥有一个只读的API密钥

2 个答案:

答案 0 :(得分:2)

文档说明

  

此正文是请求正文字符串,如果没有请求正文(通常是GET请求),则省略

注意:强调我的

但是您在GET请求中包含一个空的JSON对象

Dim stringtosign = timestampstring + "GET" + longmethod + "{}" '1553784499976GET/api/v1/spot/ccex/account/assets{}

{}不应包含在GET请求中。

'1553784499976GET/api/v1/spot/ccex/account/assets
Dim stringtosign = timestampstring + "GET" + longmethod

因此,看来您没有按照文档正确构建签名。

注意到文档

  

REST访问的根URL:https://www.coinmex.pro

在您尝试致电"https://www.coinmex.com"

  

时间戳

     

除非另有说明,否则A​​PI中的所有时间戳都以毫秒为单位返回。

     

ACCESS-TIMESTAMP标头必须是自UTC以来的秒数   Unix时代。允许使用十进制值。 您的时间戳记必须为   在API服务时间的30秒内,否则您的请求将   被认为已过期并被拒绝。如果您认为时间过长   您的服务器和API服务器之间的区别,那么我们建议   您使用时间点检查API服务器时间。

注意:强调我的

以下扩展方法用于计算时间戳

private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

/// <summary>
/// Converts the value of the current <see cref="System.DateTime"/> object to Unix Time.
/// </summary>
/// <param name="dateTime"></param>
/// <remarks>
/// </remarks>
/// This method first converts the current instance to UTC before returning its Unix time.
/// <returns> 
/// A <see cref="System.Int64"/> defined as the number of seconds that have elapsed since midnight Coordinated Universal Time (UTC), January 1, 1970, not counting leap seconds.
/// </returns>
public static long ToUnixTimeSeconds(this DateTime dateTime) {
    if (dateTime.ToUniversalTime() < Epoch) {
        return 0;
    }

    var totalSeconds = dateTime.ToUniversalTime().Subtract(Epoch).TotalSeconds;
    var timestamp = Convert.ToInt64(totalSeconds);

    return timestamp;
}

我进行了以下测试,以查看是否可以按照文档所述调用API,并且该API似乎有效。

但是我使用了c#

[TestClass]
public class CoinMaxAPITests {
    const string apiKey1 = "cmx-1027e54e4723b09810576f8e7a5413**";
    const string fakeSecret = "43a90185f5b7ab25af045e9e64bac5dc745934f359f1806fcdd2a4af80ac23==";
    const string passphrase1 = "1Us6&f%*K@QsqrYZ";

    Lazy<HttpClient> http = new Lazy<HttpClient>(() => {
        var rootUrl = "https://www.coinmex.pro";

        CookieContainer cookies = new CookieContainer();
        HttpClientHandler handler = new HttpClientHandler {
            CookieContainer = cookies,
            UseCookies = true,

        };
        var client = new HttpClient() {
            BaseAddress = new Uri(rootUrl)
        };
        client.DefaultRequestHeaders.TryAddWithoutValidation("ACCESS-KEY", apiKey1);
        client.DefaultRequestHeaders.TryAddWithoutValidation("ACCESS-PASSPHRASE", passphrase1);
        return client;
    });

    [TestMethod]
    public async Task Should_Accept_Signature() {
        //Arrange
        var requestPath = "/api/v1/spot/public/time";
        var method = "GET";
        var timeStamp = getEstimatedTimeStamp().ToString(); //"1555253371"

        var message = timeStamp + method + requestPath; //"1555253371GET/api/v1/spot/public/time"

        var secretKey = Convert.FromBase64String(fakeSecret);
        var hmac = new HMACSHA256(secretKey);
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
        var signature = Convert.ToBase64String(hash);//Jzui/eO3iyLTD6L9qVkUO0EBpZP/lFhx1HlsbuSNt/8=

        var request = new HttpRequestMessage(HttpMethod.Get, requestPath);
        request.Headers.TryAddWithoutValidation("ACCESS-TIMESTAMP", timeStamp);
        request.Headers.TryAddWithoutValidation("ACCESS-SIGN", signature);

        //Act
        var response = await http.Value.SendAsync(request);

        //Assert
        response.EnsureSuccessStatusCode();

        var json = await response.Content.ReadAsStringAsync();
        //"{\"epoch\":\"1555253501.225\",\"iso\":\"2019-04-14T14:51:41.225Z\",\"timestamp\":1555253501225}"
        var server = JsonConvert.DeserializeObject<ServerTime>(json);

        server.Should().NotBeNull();
        server.Iso.Date.Should().Be(DateTime.Today);
    }

    long getEstimatedTimeStamp() {
        return DateTime.Now.ToUnixTimeSeconds(); //custom extension method
    }
}


public partial class ServerTime {
    [JsonProperty("epoch")]
    public string Epoch { get; set; }

    [JsonProperty("iso")]
    public DateTime Iso { get; set; }

    [JsonProperty("timestamp")]
    public long Timestamp { get; set; }
}

并且能够获得一个有效的JSON响应,调用/api/v1/spot/public/time,即使使用伪造的密钥,我也可以反序列化我的声明。可能是因为这是公共API。这确实证明了所调用的URL是正确的。

当请求路径更改为

"/api/v1/spot/ccex/account/assets"

并通过API测试了更安全的私有数据,响应为400 Bad Request,响应正文中包含以下内容

{"message":"Encrypted key does not exist"}

这是预期的,因为我使用的密钥是假的。

如果您按照链接文档中的建议进行操作,则这一切向我提供了表明该API实际上可以按预期工作的指示。

答案 1 :(得分:0)

我想知道由于这种利益冲突,我应该选择什么作为答案。

发生的事情是他花了更多的精力尝试一些API。我决定创建一个新的API密钥并将其发布在此处。它是只读的,无论如何都不会出错。在我在这里发布之前,我尝试再次运行它,希望它不会像往常一样工作。

事实证明,我的代码很简单。 API密钥,机密或密码似乎有误。

这是最终可以工作的代码

Protected Overrides Function getJsonPrivate(method As String, otherParameters() As Tuple(Of String, String)) As String
    Dim base = "https://www.coinmex.pro"
    Dim premethod = "/api/v1/spot/ccex/"
    Dim longmethod = premethod + method

    Dim timestampstring = (getEstimatedTimeStamp() / 1000).ToString

    Dim stringtosign = timestampstring + "GET" + longmethod  '1555154812.857GET/api/v1/spot/ccex/account/assets

    Dim hasher = New System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(_secret1)) '"43a90185f5b7ab25af045e9e64bac5dc745934f359f1806fcdd2a4af80ac2******
    Dim sighashbyte = hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringtosign))
    Dim signature = Convert.ToBase64String(sighashbyte) '"FIgrJFDOQctqnkOTyuv6+uTy6xw3OZiP4waC1u6P5LU="=
    Dim url = base + longmethod 'https://www.coinmex.com/api/v1/spot/ccex/account/assets

    '_apiKey1="cmx-1027e54e4723b09810576f8e7a5413**"
    '_passphrase1= 1Us6&f%*K@QsqrYZ
    '
    Dim response = CookieAwareWebClient.downloadString1(url, "", {Tuple.Create("ACCESS-KEY", _apiKey1), Tuple.Create("ACCESS-SIGN", signature), Tuple.Create("ACCESS-TIMESTAMP", timestampstring), Tuple.Create("ACCESS-PASSPHRASE", _passphrase1)})

    Return response
End Function