I`m trying to authenticate the DocuSign request in my WebHook using HMAC but not matter what I do the generated does not match any of the incoming values.
I`m using the same key and the same code as the one in the DocuSign documentation so I guess the only thing that is different is the extraction of the request body. Did anyone manage to get this work in C# ? How did extract the request body from the request ?
My code:
GenerateHash(key, GetRequestBodyByteArray())
private static byte[] GetRequestBodyByteArray()
{
using (var buffer = new MemoryStream())
{
// Copy the request stream to the memory stream.
var stream = HttpContext.Current.Request.InputStream;
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(buffer);
// Rewind the memory stream.
buffer.Position = 0L;
return buffer.ToArray();
}
}
private string GenerateHash(string connectKey, byte[] requestBody)
{
var keyAsBytes = Encoding.UTF8.GetBytes(connectKey);
var hmac = new HMACSHA256(keyAsBytes);
var hashAsBytes = hmac.ComputeHash(requestBody);
return Convert.ToBase64String(hashAsBytes);
}
See this guide Step2 C# example. Also you can simplify your StreamReader. Very simplified version will look something like this, signature is your X-DocuSign-Signature-1 from the header
Task<string> body;
var signature = Request.Headers["x-docusign-signature-1"];
string secret = "your secret";
using (var reader = new StreamReader(Request.Body))
{
body = reader.ReadToEndAsync();
}
var result = Hmac.ComputeHash(secret,body.Result.ToString()).Equals(signature);
Related
I want to regenerate a token to verify the request using hmacHash
The resource documentation is in javascript and I have to implement it in C#.
here what I found in their documentation on generating the token.
routes.post("/webhook", (request) => {
const body = request.rawBody;
const signatureTime = "2022-05-17T09:47:51.935Z";
const signingKey = "localyStoredKey";
const hmac = createHmac("sha256", signingKey);
hmac.update(`${signatureTime}${body}`);
const token = hmac.digest("hex");
}
here is what I have done in C#
public async Task<string> GetWebhookAuthenticationTokenAsync(Stream requestBody) // requestBody is from HttpContext.Request.Body
{
var signingKey = "localyStoredKey"; // should be from db
var signatureTime = "2022-05-17T09:47:51.935Z"; // a timestamp
var reader = new StreamReader(requestBody, Encoding.UTF8);
var body = await reader.ReadToEndAsync();
var byteArray1 = Encoding.UTF8.GetBytes($"{signatureTime}");
var byteArray2 = Encoding.UTF8.GetBytes(body);
var concatenatedByteArray = byteArray1.Concat(byteArray2).ToArray();
var stringInMemoryStream = new MemoryStream(concatenatedByteArray);
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(signingKey));
var hashBytes = hmac.ComputeHash(stringInMemoryStream);
var token = BitConverter.ToString(hashBytes).Replace("-", String.Empty).ToLower();
return token;
}
This works fine when I send the request body(json) without newline characters(\n).
when I send the formatted json body with the request, the token is different from javascript generated token. What am I missing?
simply if json is {"pro": "duct"} the tokens are matched but when json {\n "pro": "duct"\n} its different.
PS: I found the issue is due to Carriage Return \r in json body. Any help to handle this independently from the platform?
I'm trying to use the Telegram API via http (documentation on their site says this is possible) to authorize, following these instructions:
https://core.telegram.org/mtproto/auth_key#dh-exchange-initiation
https://core.telegram.org/mtproto/description#unencrypted-message
However, I cannot get any response from the server except a 404 page. Here is the code I'm using:
async Task<String> SendAuthorizeRequestTEST()
{
HttpClient client = new HttpClient();
String message = "req_pq#60469778 3761821:int128 = ResPQ";
HttpContent content = new ByteArrayContent(Packetify(message));
HttpResponseMessage msg = await client.PostAsync(new Uri("http://149.154.167.40:443"), content);
byte[] bytes = await msg.Content.ReadAsByteArrayAsync();
return Encoding.UTF8.GetString(bytes);
}
public byte[] Packetify(String message)
{
var memoryStream = new MemoryStream();
var binaryWriter = new BinaryWriter(memoryStream);
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
binaryWriter.Write(0); //auth_key_id
binaryWriter.Write(1234567); //message_id
binaryWriter.Write(messageBytes.Length); //message_data_length
binaryWriter.Write(messageBytes); //message_data
byte[] packet = memoryStream.ToArray();
memoryStream.Dispose();
binaryWriter.Dispose();
return packet;
}
What am I doing wrong?
You could study what webogram does. It uses the HTTP protocol to speak to telegram.
Further more here are some steps you can use to move along quickly
https://stackoverflow.com/a/34929980/44080
cheers.
I have searched for a good example or explanation but couldn´t find anything helpful.
I have an ApiController TestController Where I have a Post function
public void Post([FromBody] string value)
{
//something
}
What would I have to do to send a simple string like "test" from a WinForms App to this function?
Note: My Problem is not to hit the function that works fine in many different ways. The Problem is that the value is always null.
I use the following which works fine for me.
// Create a WebRequest to the remote site
WebRequest myWebClient = WebRequest.Create(uri);
myWebClient.ContentType = "application/xml";
myWebClient.Method = method;
// Apply ASCII Encoding to obtain the string as a byte array.
string postData = //Data you want to post;
byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(postData);
Stream dataStream = myWebClient.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
// Call the remote site, and parse the data in a response object
System.Net.WebResponse response = myWebClient.GetResponse();
// Check if the response is OK (status code 200)
//if (response.StatusCode == System.Net.HttpStatusCode.OK)
//{
// Parse the contents from the response to a stream object
System.IO.Stream stream = response.GetResponseStream();
// Create a reader for the stream object
System.IO.StreamReader reader = new System.IO.StreamReader(stream);
// Read from the stream object using the reader, put the contents in a string
string contents = reader.ReadToEnd();
This will serialize object into xml if you want to post an object.
public string ToXml(object Obj, System.Type ObjType)
{
XmlSerializer ser = default(XmlSerializer);
ser = new XmlSerializer(ObjType);
MemoryStream memStream = default(MemoryStream);
memStream = new MemoryStream();
XmlTextWriter xmlWriter = default(XmlTextWriter);
xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8);
xmlWriter.Namespaces = true;
ser.Serialize(xmlWriter, Obj);
xmlWriter.Close();
memStream.Close();
string xml = null;
xml = Encoding.UTF8.GetString(memStream.GetBuffer());
xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)));
xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1));
return xml;
}
Try to use HttpClient from System.Net.Http.dll:
using (var loClient = new HttpClient() { BaseAddress = new Uri("http://localhost:55743/") })
{
var loResult = loClient.PostAsJsonAsync<string>("api/Test/Post/", "Hello World");
loResult.Result.EnsureSuccessStatusCode();
}
Most basic tasks can be accomplished quite easily with WebClient:
using (var wc = new WebClient())
{
wc.Headers.Add("Content-Type", "application/json");
var response = wc.UploadString("http://provide-url", "\"test\"");
}
EDIT: Turns out you'll need to specify the Content-Type in the client as application/json AND wrap the string in additional quotes.
According to this post, it has to do with the way Web API handles parameter binding.
My task is to create data that are digitally signed in format of PKCS#7 version 1.5 (RFC 2315) DER (ITU-T Recommendation X.690) - basically ANSI.1 with X.509 signature?
the message must satisfy following:
must be type signedData
must contain signed data
must contain signer's certificate
must contain one digital signature
My code is following
static void Main(string[] args)
{
string pfx = #"C:\Users\marek\Downloads\mfcr\marek-pfx.pfx";
string xml = #"C:\Users\marek\Downloads\mfcr\souhr20141.xml";
X509Certificate2 cert = new X509Certificate2(pfx, "thepass");
byte[] publicBytes = cert.RawData;
//var f = new FileStream(xml, System.IO.FileMode.Open);
var fileContent = System.IO.File.ReadAllBytes(xml);
char[] cArray = System.Text.Encoding.ASCII.GetString(fileContent).ToCharArray();
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
byte[] signedData = rsa.SignData(new System.Text.UTF8Encoding().GetBytes(cArray), new SHA1CryptoServiceProvider());
RSACryptoServiceProvider rsa2 = (RSACryptoServiceProvider)new X509Certificate2(publicBytes).PublicKey.Key;
var dataGenerator = new CmsEnvelopedDataStreamGenerator();
bool verified = rsa2.VerifyData(new System.Text.UTF8Encoding().GetBytes(cArray), new SHA1CryptoServiceProvider(), signedData);
File.WriteAllBytes(#"C:\Users\marek\Downloads\mfcr\Foo.p7b", signedData);
}
The WebService that Iam sending the Foo.p7b responds with: File is not in expected format of PKCS7(DER).
This code for sending the HttpWebRequest :
static void Main(string[] args)
{
try
{
string fileName = (#"C:\Users\marek\Downloads\mfcr\Foo.p7b");
WebResponse rsp = null;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://adisepo.mfcr.cz/adistc/epo_podani");
request.ClientCertificates.Add(new X509Certificate(pfx,"thepass"));
request.Method = "POST";
request.ContentType = "application/pkcs7-signature";
request.Credentials = CredentialCache.DefaultNetworkCredentials;
var encoder = new UTF8Encoding();
var reqStream = request.GetRequestStream();
StreamWriter writer = new StreamWriter(request.GetRequestStream());
// Write the XML text into the stream
writer.WriteLine(GetTextFromXMLFile(fileName));
writer.Close();
reqStream.Close();
rsp = request.GetResponse();
StreamReader sr = new StreamReader(rsp.GetResponseStream());
string result = sr.ReadToEnd();
sr.Close();
Console.Write("\n příkaz odeslán \n");
Console.Write(result);
Console.ReadLine();
Console.Read();
}
catch (Exception ex)
{ Console.WriteLine(ex.ToString());
Console.ReadLine();
}
}
private static string GetTextFromXMLFile(string file)
{
StreamReader reader = new StreamReader(file);
string ret = reader.ReadToEnd();
reader.Close();
return ret;
}
}
I'm struggling with this issue for almost 5 days - I'm surely not expert on digital signature or certificates.
From what I learned so far - to create message like that I should do:
Sign the xml with my private key
Envelope that blob with my public key
But how could the recipient check whether I am the real sender? Should I add to HttpWebRequest parameter with my certificate? Or that step 2 - Enveloping the message is enough for him to check that?
Thank you everyone for your time and replies.
Your code tries digitally sign byte representation of XML, but signing XML requires more processing before signing. For example XML requires to be canonicalized (or signed message can be injected with unsigned data), and there is a special format for enveloped signatures. I don't know what is actual method Danovy Portal uses, but if it uses standard way, you can follow the links below and sign your data.
MSDN: How to: Sign XML Documents with Digital Signatures
How enveloped signatures look
And just for information (don't think you really need to read this)
W3C Xml Signature specification
EDIT: to send pkcs#7 message change the code. When generating
ContentInfo contentInfo = new ContentInfo(new System.Text.UTF8Encoding().GetBytes(cArray));
SignedCms cms = new SignedCms (contentInfo);
CmsSigner signer = new CmsSigner (cert);
cms.ComputeSignature (signer);
byte[] pkcs7=cms.Encode ();
File.WriteAllBytes(#"../../Foo.p7b", pkcs7);
When send:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://adisepo.mfcr.cz/adistc/epo_podani");
//we don't need to add certificate to POST
// request.ClientCertificates.Add(new X509Certificate(pfx,"test"));
request.Method = "POST";
request.ContentType = "application/pkcs7-signature";
request.Credentials = CredentialCache.DefaultNetworkCredentials;
var encoder = new UTF8Encoding();
using (var reqStream = request.GetRequestStream())
{
// Write pkcs#7 into the stream
byte[] pkcs = File.ReadAllBytes(#"../../Foo.p7b");
reqStream.Write(pkcs, 0, pkcs.Length);
}
rsp = request.GetResponse();
Take a look at BouncyCastle - it has all you need for that:
http://www.bouncycastle.org/csharp/
I decided that my question here isn't really what I want to do - the XML I need to send is a lot longer, potentially, than I really want to send in a URI.
It didn't "feel" right doing that, and this unsealed the deal.
I need to send both a couple of args AND a file to my Web API app from a client (handheld/CF) app.
I may have found the code for receiving that, from here [
http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2]
Specifically, Wasson's Controller code here looks like it very well might work:
public async Task<HttpResponseMessage> PostFile()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
StringBuilder sb = new StringBuilder(); // Holds the response body
// Read the form data and return an async task.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the form data.
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
sb.Append(string.Format("{0}: {1}\n", key, val));
}
}
// This illustrates how to get the file names for uploaded files.
foreach (var file in provider.FileData)
{
FileInfo fileInfo = new FileInfo(file.LocalFileName);
sb.Append(string.Format("Uploaded file: {0} ({1} bytes)\n", fileInfo.Name, fileInfo.Length));
}
return new HttpResponseMessage()
{
Content = new StringContent(sb.ToString())
};
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
...but now I need to know how to send it; other calls from the client are of the form:
http://<IPAddress>:<portNum>/api/<ControllerName>?arg1=Bla&arg2=Blee
but how does the file I need to send/attach get passed along? It is a XML file, but I don't want to append the whole thing to the URI, as it can be quite large, and doing so would be horrendously weird.
Does anybody know how to accomplish this?
UPDATE
Following the crumbs tvanfosson dropped below, I found code here which I think I can adapt to work on the client:
var message = new HttpRequestMessage();
var content = new MultipartFormDataContent();
foreach (var file in files)
{
var filestream = new FileStream(file, FileMode.Open);
var fileName = System.IO.Path.GetFileName(file);
content.Add(new StreamContent(filestream), "file", fileName);
}
message.Method = HttpMethod.Post;
message.Content = content;
message.RequestUri = new Uri("http://localhost:3128/api/uploading/");
var client = new HttpClient();
client.SendAsync(message).ContinueWith(task =>
{
if (task.Result.IsSuccessStatusCode)
{
//do something with response
}
});
...but that depends on whether the Compact Framework supports MultipartFormDataContent
UPDATE 2
Which it doesn't, according to How can i determine which .Net features the compact framework has?
UPDATE 3
Using the Bing Search Code for C# extension, I mashed "h", chose "How do I", entered "send file via http" and got this:
WebRequest request = WebRequest.Create("http://www.contoso.com/PostAccepter.aspx ");
request.Method = "POST";
string postData = "This is a test that posts this string to a Web server.";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
WebResponse response = request.GetResponse();
Console.WriteLine(((HttpWebResponse)response).StatusDescription);
dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);
reader.Close();
dataStream.Close();
response.Close();
As I need to add a couple of string args in addition to the file (which I assume I can add via the postData byte array), can I do that by adding more calls to dataStream.Write()? IOW, is this sensible (first and third lines differ):
WebRequest request = WebRequest.Create("http://MachineName:NNNN/api/Bla?str1=Blee&str2=Bloo");
request.Method = "POST";
string postData = //open the HTML file and assign its contents to this, or make it File postData instead of string postData?
// the rest is the same
?
UPDATE 4
Progress: This, such as it is, is working:
Server code:
public string PostArgsAndFile([FromBody] string value, string serialNum, string siteNum)
{
string s = string.Format("{0}-{1}-{2}", value, serialNum, siteNum);
return s;
}
Client code (from Darin Dimitrov in this post):
private void ProcessRESTPostFileData(string uri)
{
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
var data = "=Short test...";
var result = client.UploadString(uri, "POST", data);
//try this: var result = client.UploadFile(uri, "bla.txt");
//var result = client.UploadData()
MessageBox.Show(result);
}
}
Now I need to get it sending a file instead of a string in the [FromBody] arg.
You should look into using multipart/form-data with a custom media type formatter that will extract both the string properties and the uploaded XML file.
http://lonetechie.com/2012/09/23/web-api-generic-mediatypeformatter-for-file-upload/