Convert CachedOutputStream to a string.
After upgrading camel from 2.12 to 2.23 I have a problem on my route, same code same route.
The response after calling a endpoint is of type org.apache.camel.converter.stream.CachedOutputStream
I tried to convert the CachedOutputStream to a String.
String body = exchange.getIn().getBody(String.class);
logger.info("FJA the string after caling endpoint: " + body);
I dk.bankconnect.hub.Util efter FJA efter streng er:
-0��phjA����/h`�FhI[����G<�����,�Z���f���=��Rϝ��s#���O��~#�G�t
How to convert the CachedOutputStream to a string?
I tried many different things to avoid the response to come as a stream. However, I do not succeeded.
Here is a little snippet of my route:
from("direct:sdc:webservice")
.setProperty("webservice", constant(Boolean.TRUE))
.setProperty("duration", simple("Forwarding request to ${property.datacentral} datacentral"))
.choice().when().simple("${property.Tracked} == true").bean(Util.class, "dura-tion").end()
.bean(Util.class, "tracker")
.bean(Util.class, "foer")
.to(Util.getProperty("sdc.url"))
.bean(Util.class, "efter")
.convertBodyTo(Document.class)
.setProperty("duration", simple("Receiving response from ${proper-ty.datacentral} datacentral"))
.choice().when().simple("${property.Tracked} == true").bean(Util.class, "dura-tion").end()
.bean(Util.class, "tracker")
.setProperty(SoapError.FAULT, xpath("/soapenv:Envelope/soapenv:Body/soapenv:Fault/faultstring/text()", String.class).namespace("soapenv", Namespace.SOAP))
.end();
My convertBodyTo fails, because its suddenly a stream.
Ok, very short
The response after calling a endpoint is of type org.apache.camel.converter.stream.CachedOutputStream
I tried to convert the CachedOutputStream to a String.
String body = exchange.getIn().getBody(String.class);
This is not a String that is readable, but many special characters.
Frank :-)
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
I think I did it like what's listed here
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
}
]
And for Signature
This is the manual says
The ACCESS-SIGN header is the output generated by using HMAC SHA256 to
create the HMAC SHA256 using the BASE64 decoding secret key in the
prehash string to generate timestamp + method + requestPath + "?" +
queryString + body (where ‘+’ represents the string concatenation) and
BASE64 encoded output. The timestamp value is the same as the
ACCESS-TIMESTAMP header. This body is the request body string or
omitted if there is no request body (usually the GET request). This
method should be capitalized.
Remember that before using it as the key to HMAC, base64 decoding (the
result is 64 bytes) is first performed on the 64-bit alphanumeric
password string. In addition, the digest output is base64 encoded
before sending the header.
User submitted parameters must be signed except for sign. First, the
string to be signed is ordered according to the parameter name (first
compare the first letter of all parameter names, in alphabetic order,
if you encounter the same first letter, then you move to the second
letter, and so on).
For example, if we sign the following parameters
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': '',
}
Generate the string to be signed
Message = '1590000000.281GET/api/v1/spot/ccex/orders?limit=100{"code": "ct_usdt", "side": "buy", "type": "limit", "size": "1", "price": "0.1", "funds": ""}'
Then, the character to be signed is added with the private key
parameters to generate the final character string to be signed.
For example:
hmac = hmac(secretkey, Message, SHA256)
Signature = base64.encode(hmac.digest())
I thought may be the _secret1 is a base64 string rather than utf8 so I changed to
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
Not working either.
The secret key (I truncated a few letters) look like
43a90185f5b7ab25af045e9e64bac5dc745934f359f1806fcdd2a4af80ac2
Is this something that should be decoded as base 64 or utf8 or what?
The spec says it's 64. However, it doesn't look like a 64 encoded string. It looks like the letters are from 0-f
Best answers will:
1. Tell me what went wrong in the code. I made the change. Try. Run. Works. Awesome.
A good answer will
2. A sample simulation with a fake/real signatures/nonce/passphrase and real actual headers and signatures. So I can see where exactly I have a wrong result.
Update: I modified the code again. I change the timestamp to seconds instead of milisecons. I remove the {}. I use both way.
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
Still doesn't work.
Current Error is
Message = "The remote server returned an error: (401) Unauthorized."
I would love to give some read-only API key. Hang on. Or create an empty account and then have a read only API key
The documentation states
This body is the request body string or omitted if there is no request body (usually the GET request)
Note: emphasis mine
yet you include an empty JSON object on a GET request
Dim stringtosign = timestampstring + "GET" + longmethod + "{}" '1553784499976GET/api/v1/spot/ccex/account/assets{}
That {} should not be included in a GET request.
'1553784499976GET/api/v1/spot/ccex/account/assets
Dim stringtosign = timestampstring + "GET" + longmethod
So it appears you were not constructing the signature correctly as per documentation.
Noticed that the docs
The root URL for REST access:https://www.coinmex.pro
while you are trying to call "https://www.coinmex.com"
Timestamp
Unless otherwise specified, all timestamps in APIs are returned in microseconds.
The ACCESS-TIMESTAMP header must be the number of seconds since UTC's
time Unix Epoch. Decimal values are allowed. Your timestamp must be
within 30 seconds of the API service time, otherwise your request will
be considered expired and rejected. If you think there is a large time
difference between your server and the API server, then we recommend
that you use the time point to check the API server time.
note: emphasis mine
Following extension method was used to calculate time stamp
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;
}
I did the following Test to see if I could call the API following the documentation and it appears to have worked.
I used c# however
[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; }
}
And was able to get a valid JSON response calling /api/v1/spot/public/time that I was able to deserialize for my assertion, even with the fake keys. Probably as this is the public API. This does prove that the URL called is correct.
When the request path is changed to
"/api/v1/spot/ccex/account/assets"
And tested for more secure private data from the API, the response is 400 Bad Request with the following content in the body of the response
{"message":"Encrypted key does not exist"}
which is as expected given that the keys I used were fake.
This gives me every indication that the API does in fact work as expected provided that you follow what is suggested in the linked documentation.
I wonder what I should pick as the answer due to this conflict of interest.
What happened is he go the extra mile of trying some API. I decided to create a new API key and post it here. It's read-only and can't possibly go wrong anyway. Before I posted here I try to run it once more expecting that it won't work like usual.
It turns out my code simply works. It seems that there are error in API key, secret, or password.
Here is the code that finally work
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
I'm trying to translate a JavaScript application of TOTP to VB.Net: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/
I have encountered a problem during translation of the HMAC-part:
//Javascript:
var hmacObj = new jsSHA("Hello World!", 'HEX');
var hmac = hmacObj.getHMAC("secret", 'HEX', 'SHA-1', "HEX");
This is a codesnippet of my translation in VB.Net
'VB.Net:
Dim hmacObjTest As New HMACSHA1(System.Text.Encoding.UTF8.GetBytes("secret"))
Dim hmacTest As Byte() = hmacObjTest.ComputeHash(System.Text.Encoding.UTF8.GetBytes("Hello World!"))
Dim hmacHexTest As New StringBuilder()
For i As Integer = 0 To hmacTest.Length - 1
hmacHexTest.Append(hmacTest(i).ToString("x2"))
Next i
Dim strTest As String = "HMAC = " & hmacHexTest.ToString()
The problem is that i get different output from the two languages:
Output JS: 5efed98b0787c83f9cb0135ba283c390ca49320e //Tested from jsSha demo: http://caligatio.github.io/jsSHA/
Output VB.Net: 87b0154b8420c0b58869ca103f481e824d8876ea
The outputs are not at all the same like they are in this question: hmacsha1 output hex strings different between vb.net and python
Does anyone know where I might be doing something wrong?
Hashes don't work on strings - they work on the binary representation of the string. Now you use UTF-8 as encoding for the dotnet version, while the JavaScript version is very likely not to use UTF-8 - so you get different binary representations, resulting in different hashes.
Use either webttolkit or the hackish var utfstring = unescape(encodeURIComponent(rawstring)); to convert to UTF-8 before calcualting the hash.
I have written an ActiveX control which supports drag-drop of email attachments and disk files and uploads files to a web server.
I used the samples available at this link for Uploading files
Upload files with HTTPWebrequest (multipart/form-data)
I am sending data in chunks by setting the following properties
wr = (HttpWebRequest)WebRequest.Create(UploadUrl);
wr.ContentType = "multipart/form-data; boundary=" + boundary;
wr.Method = "POST";
wr.ContentLength = contentLength;
wr.AllowWriteStreamBuffering = false;
wr.Timeout = 600000;
wr.KeepAlive = false;
wr.ReadWriteTimeout = 600000;
wr.ProtocolVersion = HttpVersion.Version10;
wr.Credentials = System.Net.CredentialCache.DefaultCredentials;
wr.SendChunked = true;
wr.UserAgent = "Mozilla/3.0 (compatible; My Browser/1.0)";
rs = wr.GetRequestStream();
With the above settings I am getting an error (411) Length Required.
After reading the following article I realized, I dont need to set Content-Length property when I set SendChunked = true;
http://en.wikipedia.org/wiki/Chunked_transfer_encoding
But the Microsoft example code here doesn't do so
http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.sendchunked.aspx
After further digging I came to know that Chunked encoding is supported in HTTP version 1.1 only. So I changed the property as follows
wr.ProtocolVersion = HttpVersion.Version11;
Now I don't see that 411 error any more.
Now, can someone with better knowledge verify my understanding here and please let me know if I am doing right.
Thanks
Ravi.
They are both just mechanisms to let the receiver know when it has reached the end of the transfer. If you want to use Content-Length, it is pretty simple. Just take your encoded byte array of POST data, and use the Length property.
ASCIIEncoding encoding = new ASCIIEncoding ();
byte[] postDataByteArray = encoding.GetBytes (postData);
wr.ContentLength = postDataByteArray.Length;
I'm trying to send a 28 character string to a remote ip address and port. I've done this successfully in vb.net using the following code snippets:
Dim swon As String = "A55A6B0550000000FFFBDE0030C8"
Dim sendBytes As [Byte]()
sendBytes = Encoding.ASCII.GetBytes(swon)
netStream.Write(sendBytes, 0, sendBytes.Length)
I now have to convert this across to c++ and have the following so far:
char *swon = "A55A6B0550000000FFFBDE0030C8";
array<Byte>^ sendBuffer = gcnew array<Byte>(bufferSize);
sendBuffer = BitConverter::GetBytes( swon );
tcpStream->Write(sendBuffer, 0, sendBuffer->Length);
but am getting stuck at this point. I'm sure I'm missing a simple syntax error but I can't figure it out!
To clarify, I'm not getting an error, but I don't think the string is being converted to bytes correctly as when I convert back, I just get a '01'
Cheers,
Chris
I don't understand why you are not just using the exact same .Net framework classes in your ++/CLI code. eg. System::String for swon, Encoding::ASCII to produce the array of bytes.
Anything you did in VB you can map directly over to C++/CLI without using different classes - that's the easest port for you. When you are in MSDN online, just select the C++ view to get examples of stuff you want to do. Try that on this page, for example: http://msdn.microsoft.com/en-us/library/system.text.encoding.ascii.aspx
Steve is correct that the same logic can be duplicated in C++. But the C++ char* already is ASCII, no conversion is necessary. Just a copy is all that's needed.
const char swon[] = { "A55A6B0550000000FFFBDE0030C8" };
array<Byte>^ sendBuffer = gcnew array<Byte>((sizeof swon) - 1);
pin_ptr<Byte> startBuffer = &sendBuffer[0];
memcpy(startBuffer, swon, sendBuffer->Length);
tcpStream->Write(sendBuffer, 0, sendBuffer->Length);