I am trying follow the example given at AWS documentation Signing AWS requests and make a ListUsers call in C#. I have arrived till the last stage of generating the signature (i.e ready to submit the signed request given at signature-4 request examples). But the code I pasted below is throwing 'bad request' exception when submitted.
static void submitSignedRequest(string my_access_key, string my_secret_key, string signature, string curr_utc_date_string, string curr_utc_datetime_string)
{
string url = "https://iam.amazonaws.com/?Action=ListUsers&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-SHA256&" +
"X-Amz-Credential=" + my_access_key + "/" + curr_utc_date_string + "/us-east-1/iam/aws4_request&X-Amz-Date=" + curr_utc_datetime_string +
"&X-Amz-SignedHeaders=content-type;host;x-amz-date&X-Amz-Signature=" + signature;
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "POST";
request.Host = "iam.amazonaws.com";
request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
WebHeaderCollection headers = (request as HttpWebRequest).Headers;
headers.Add("Authorization", "AWS4-HMAC-SHA256 Credential=" + my_access_key +"/" + curr_utc_date_string + "/us-east-1/iam/aws4_request, " +
"SignedHeaders=content-type;host;x-amz-date, Signature=" + signature);
headers.Add("x-amz-date", curr_utc_datetime_string);
HttpWebResponse response;
try
{
response = request.GetResponse() as HttpWebResponse;
}
catch (Exception ex)
{
Console.WriteLine("Error Message: " + ex.Message);
}
}
Output i get is:
Error Message: The remote server returned an error: (400) Bad Request.
Can some one help me what wrong I am doing here?
Edit:
I solved this finally by myself. I had to transform it to below. (answer is for RDS though I believe it's visible what the differences are).
string url = "https://rds.us-west-1.amazonaws.com";
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
// ASCIIEncoding encoding = new ASCIIEncoding();
string postData = "Action=DescribeDBInstances&Version=2013-09-09";
byte[] data = Encoding.UTF8.GetBytes(postData);
request.Method = "POST";
request.Host = "rds.us-west-1.amazonaws.com";
request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
// request.ContentLength = data.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
WebHeaderCollection headers = (request as HttpWebRequest).Headers;
request.Headers.Add("Authorization", "AWS4-HMAC-SHA256 Credential=" + my_access_key + "/" + curr_utc_date_string + "/us-west-1/rds/aws4_request, " +
"SignedHeaders=content-type;host;x-amz-date, Signature=" + signature);
request.Headers.Add("x-amz-date", curr_utc_datetime_string);
Can some one help me what wrong I am doing here?
Maybe, but I strongly suggest to skip this endeavor all together and just use the excellent AWS SDK for .NET for all your AWS API interactions instead, because it indeed helps take the complexity out of coding by providing .NET APIs for many AWS services including Amazon S3, Amazon EC2, DynamoDB and more.
I'm working with AWS for years in various environments and with all sorts of languages, and I have never even bothered to look into using their API without the assistance of one of the multitude of SDKs they are offering, most of which offer high level tooling in addition to making working with the API from your respective language much easier in the first place.
But if you really need or want to do it yourself, I suggest to simply take a look at the source code of these very SDKs, which are all available at GitHub, including the one for the AWS SDK for .NET - regarding the issue at hand, you might want to start looking into AWS4Signer.cs for example.
Please note that AWS has just released the significantly overhauled version 2, see GA Release of AWS SDK for .NET Version 2 for details.
Related
We have code written in Xamarin Cross Platform that works for Android with Clients tunneling software:
string body = "<rest_access/>";
byte[] dataByte = Encoding.ASCII.GetBytes(body);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(endpoint + #"/rest_access");
request.Method = "POST";
request.ContentType = "application/xml";
request.Accept = "application/json";
var credentials = System.Text.Encoding.ASCII.GetBytes(username + ":" + password);
string encodedCredentials = System.Convert.ToBase64String(credentials);
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + encodedCredentials);
request.ContentLength = dataByte.Length;
Stream stream = request.GetRequestStream();
stream.Write(dataByte, 0, dataByte.Length);
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
string responseString = reader.ReadToEnd().ToString();
dynamic authResponse = JsonConvert.DeserializeObject(responseString);
//Console.WriteLine("Auth response:\n" + authResponse);
accessKey = authResponse.rest_access.access_key;
//Console.WriteLine("kljuc " + accessKey);
return accessKey;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return "Unexpected Error";
}
But when using this same code for iOS application tunnel does not seem to be present.
In their documentation we found:
Apps built with the Xamarin development platform can access network servers in various ways. AppTunnel with
HTTP/S tunneling is supported only as follows:
• The app uses the NSURLConnection or NSURLSession APIs exposed to C# through the Xamarin.iOS
binding.
• The app uses the ModernHttpClient library with NSURLSession. The ModernHttpClient library with
CFNetwork will not work.
For example, the app initializes the instance of the ModernHttpClient as follows:
var httpClient = new HttpClient (new NativeMessageHandler ());
Does this mean that we have to rewrite all methods that used HttpWebRequest in them to now use one of these libs?
If so could I get some link to How to rewrite these so they are acceptable?
Have you tried to set the HttpClient Implementation for Xamarin.iOS in
HttpClient Stack
change it to NSUrlSession or Managed
I've been working on the Walmart API but I keep getting either the 401 error or the 500 error when I run the code
public void post()
{
byte[] data = Encoding.ASCII.GetBytes(
$"username={user}&password={password}");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://marketplace.walmartapis.com/v2/feeds?feedType=item");
request.Method = "POST";
request.Accept = "application/xml;";
request.ContentLength = data.Length;
request.Headers.Add("WM_SVC.NAME", "Walmart Marketplace");
request.Headers.Add(authId);
request.Headers.Add("WM_CONSUMER.ID", user);
request.Headers.Add( time);
request.Headers.Add(CorId);
using (Stream stream = request.GetRequestStream ())
{
stream.Write(data , 0, data.Length);
}
string responseContent = null;
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
using (StreamReader sr99 = new StreamReader(stream))
{
responseContent = sr99.ReadToEnd();
}
}
}
MessageBox.Show(responseContent);
}
where authID is a signature generated from a jar file provided by walmart
time is also generated from the jar file
CorID is a randomly generated number
and user is the user id.
here is the link that describes the header parameters. Did I miss something in my header?
https://developer.walmartapis.com/#getting-started
There are multiple problems with your request. First, you are submitting a feed, but sending it as an application/xml when it should be a multipart/form-data request. Beyond this, your headers aren't set up properly and there is currently a major problem with submitting multipart/form-data requests to Walmart using C#. I have not seen a post from anyone successfully sending a feed to Walmart via C#. I am currently using C# to execute a batch file that then fires a modified version of the Walmart Java SDK which is capable of sending the multipart/form-data requests.
The reponse below is for any request other than feeds. I would start with the example listed below to get familiar with how you need to set your headers up. This is going to work for the majority of Walmart interfacing, but if the request is a feed style request, you will either need to come up with a better solution to the multipart/form-data issue, use the Java SDK, or wait for the C# SDK. If someone reads this and has a better answer as to how to submit feeds via C# exclusively I would love to hear about it!
Here is an example of an application/xml request that works.
string timestamp = CurrentTimeMillis().ToString().Trim();
string query = #"orders/"+poID+"/acknowledge";
string request = v3BaseUrl + query; //Constructed URI
string stringToSign = consumerId + "\n" +
request.Trim() + "\n" +
"POST" + "\n" +
timestamp + "\n";
string signedString = signData(stringToSign); //Your signed string
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(request);
webRequest.Accept = "application/xml";
webRequest.ContentType = "application/xml";
webRequest.Method = "POST";
webRequest.Headers.Add("WM_SVC.NAME", "Walmart Marketplace");
webRequest.Headers.Add("WM_SEC.AUTH_SIGNATURE", signedString);
webRequest.Headers.Add("WM_CONSUMER.ID", consumerId);
webRequest.Headers.Add("WM_SEC.TIMESTAMP", timestamp.ToString().Trim());
webRequest.Headers.Add("WM_QOS.CORRELATION_ID", Guid.NewGuid().ToString());
webRequest.Headers.Add("WM_CONSUMER.CHANNEL.TYPE", channelType);
webRequest.ContentLength = 0;
webRequest.Timeout = Timeout.Infinite;
webRequest.KeepAlive = true;
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
success = true;
}
}
I realise there have been a number of similar posts to this but I haven't found a solution yet. Am trying to post some xml to an MPI gateway but keep getting the following error:
Unable to read data from the transport connection: An existing
connection was forcibly closed by the remote host.
Below is the code I'm currently using but have tried just about every different approach I can think of and they all return the same error:
string result = "";
string xml = "<TNSAuthRequest><CardNumber>0123456789</CardNumber><ExpiryDate>1801</ExpiryDate><PurchaseAmt>750</PurchaseAmt><CurrencyCode>826</CurrencyCode><CurrencyExponent>2</CurrencyExponent><CountryCode>826</CountryCode><MerchantName>Mayflower</MerchantName><MerchantId>0123456789</MerchantId><MerchantData>abcdefghijklmnopqrstuvwxyz0123456789</MerchantData><MerchantUrl>example.com</MerchantUrl><NotificationURL>example.com/basket</NotificationURL></TNSAuthRequest>";
var url = "https://mpi.securecxl.com";
byte[] bytes = System.Text.Encoding.ASCII.GetBytes("xmldata=" + xml.ToString());
ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(ValidateRemoteCertificate);
var req = (HttpWebRequest)WebRequest.Create(url);
req.AllowWriteStreamBuffering = true;
req.ContentType = "text/xml";
req.Method = "POST";
//req.ContentLength = bytes.Length;
req.KeepAlive = false;
req.ProtocolVersion = HttpVersion.Version10;
req.ServicePoint.ConnectionLimit = 1;
//req.Timeout = -1;
try
{
using (var writer = new StreamWriter(req.GetRequestStream(), Encoding.ASCII))
{
writer.WriteLine(bytes);
}
using (WebResponse resp = req.GetResponse())
{
using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
{
result = sr.ReadToEnd().Trim();
}
}
}
catch (Exception ex)
{
result = ex.Message + "<br />" + ex.InnerException.Message + "<br /><br />" + xml.Replace("<", "<");
}
ViewBag.result = result;
Am basically wandering if anyone can see anything that might be wrong with the code that could be causing this error or if it's most likely I problem on the their end? Have tried running on my localhost, our live server and my own private server (with a completely different IP) and still get same result.
Any ideas?
I think its because you are connecting to "https" url. In this case you have to add following line to your code.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
It will accept "ssl" protocol for your request. "ServicePointManager.ServerCertificateValidationCallback" handler just controls certificate validity.
Slightly better perhaps:
System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol | System.Net.SecurityProtocolType.Tls12;
#AlisettarHuseynli is right, this sometimes has to do with https. Most likely occurs when the infrastructure gets updates which may mean TLS gets updated for example from TLS1.0 to TLS1.2 Usually happens with some APIs, etcetera.
If the service you are trying to access can be accessed over http, do that. Change the scheme from https to http. Worked in my case. Otherwise you'll have to add code to support higher versions of TLS. Popular software usually have an opt-in option to use TLS1.2 instead of the old TLS1.0.
I am working with eloqua 10.
I need to create Email in eloqua by using their rest api from an aspx web page.But am get 404 Bad Request.
Given below is the code sample for the POST request that I tried.
string authenticateStr = eloquainstance + #"\" + username + ':' + password;
byte[] bytesToEncode = Encoding.UTF8.GetBytes(authenticateStr);
string encodedText = Convert.ToBase64String(bytesToEncode);
string requrl = "/assets/email";
string requestBody = "<subject>Test subject</subject>" +
"<senderName>Ajai Test</senderName>" +
"<senderEmail>amani#suyati.com</senderEmail>" +
"<emailGroupId>9</emailGroupId>" +
"<htmlContent>This is a test email templete created trough rest api.This is for testing purpose only</htmlContent>"+
"<type>Email</type>" +
"<name>Email By api</name>";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(baseurl + requrl);
request.Headers.Add("Authorization", "Basic " + encodedText);
request.Accept = "application/xml";//"application/x-www-form-urlencoded";
request.Method = "POST";
request.ContentType = "application/xml";
request.ContentLength = requestBody.Length;
//write body to text
byte[] body = System.Text.Encoding.UTF8.GetBytes(requestBody);
Stream dataStream = request.GetRequestStream();
dataStream.Write(body, 0, requestBody.Length);
dataStream.Close();
HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
Please correct me if anything wrong in my code.
Can anybody tried a post using eloqua rest api, if so can you share a sample code to make a POST request to eloqua from c#.Any help be appreciable.
Not sure whether you've got this working yet as some time has passed since you asked this, but you can find a whole set of samples for using eloqua's topliners site:
http://topliners.eloqua.com/community/code_it/blog/2012/10/08/eloqua-rest-api--open-source-c-samples
Looking at your code I think you may need to escape the html body
I would also consider writing a class to represent the email object and then use restsharp to simplify the process of serialising the request and handling the response.
Also experiment with test requests using fiddler.
I'm trying to connect to a API (In this case freeagent) to grab some data. I've used Googles OAuth Playground to generate me a token.
var req = (HttpWebRequest)WebRequest.Create("https://api.freeagent.com/v2/recurring_invoices");
req.Headers["Authorization"] = "Bearer " + Convert.ToBase64String(Encoding.ASCII.GetBytes(_accessToken));
req.ContentType = "application/xml";
req.Accept = "application/xml";
req.Method = "GET";
// and get the response
var resp = req.GetResponse();
var streamIn = new StreamReader(resp.GetResponseStream());
returnData = streamIn.ReadToEnd();
streamIn.Close();
return resp;
Now I'm loosly trying to conver the following : https://dev.freeagent.com/docs/using_curl
I constantly get a Bad Request HTTP 400 - Anyone have any suggestions on what could be causing this problem?
I have most of the api working using RestSharp:
https://github.com/nicwise/FreeAgent
I wrote that for my iPhone app, mobileAgent, but it's fairly basic
.NET, so it should work on anything.
My authentication injection code looks very much like yours:
protected void SetAuthentication(RestRequest request)
{
request.AddHeader("Authorization", "Bearer " +
Client.CurrentAccessToken.access_token);
}
Have you run fiddler or something similar to see exactly what is going
over the wire? that often helps.
The solution to this, is to pass the UserAgent as well.
Example...
req.UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17";
You shouldn't need to base64 encode the token, just set the Authorization header to "Bearer " + _accessToken. If this still doesn't work, try using http://httpbin.org/get as the URL to look at your request to see if there are any other possible problems.