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密钥
答案 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"
时间戳
除非另有说明,否则API中的所有时间戳都以毫秒为单位返回。
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