Couldn't establish a connection with HttpClient C# and TLS 1.x - ssl

I am using Asp.net c#. I have an external API url, which does respond to specific url with TLS 1.2/1.1 enabled. I want to consume that API from my Web API. I have written following code to access the same.
public async Task<bool> Post(string oauth_signature, string oauth_consumer_key, string oauth_timestamp, string oauth_nonce, CheckKYCRequestModel payload)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;
string url = "https://recycle.icicipruamc.com/Distributorsvcs/InvestorService.svc/JSON/CheckKYC?oauth_signature=" + oauth_signature +
"&oauth_consumer_key=" + oauth_consumer_key + "&oauth_timestamp=" + oauth_timestamp + "&oauth_nonce=" + oauth_nonce + "";
var jsonData = JsonConvert.SerializeObject(payload);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
var response = await client.PostAsync(url, content);
return true;
}
But I am unable to establish a connection and it is throwing "Could not reach to the server" error.
I tried doing the same thing from Fiddler and it is working perfectly, but doesn't work with HttpClient, even though the client IP address is same and TLS is enabled in both cases.
What am I doing wrong ?

Related

Getting an OAuth2 authentication token in VB.net

I'm trying to get an OAuth token using a ClientID and SecretID.
My code so far:
Dim clientId As String = "8cd6b80dd822961f362"
Dim clientSecret As String = "5afbd4bb280f29cba5ec1f362"
Dim credentials = String.Format("{0}:{1}", clientId, clientSecret)
Dim headerValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials))
Dim content = New FormUrlEncodedContent(New Dictionary(Of String, String) From {
{"client_id", clientId},
{"client_secret", clientSecret},
{"response_type", "code"},
{"redirect_uri", "https://somesite.com/"},
{"grant_type", "authorization_code"}})
Dim requestMessage = New HttpRequestMessage(HttpMethod.Post, "https://api.site.com/oauth2/authorize")
requestMessage.Headers.Authorization = New AuthenticationHeaderValue("Basic", headerValue)
requestMessage.Content = content
Dim client As HttpClient = New HttpClient()
Dim task = client.SendAsync(requestMessage)
Dim response = task.Result
response.EnsureSuccessStatusCode()
Dim responseBody As String = response.Content.ReadAsStringAsync().Result
MsgBox(responseBody)
The above code returns the HTML for the redirect_uri site and not a token.
What am I missing or doing wrong?
Using Postman and the credentials provided I managed to get the token.
The second step of a code flow uses the token endpoint, not the authorize endpoint. Your payload looks correct though. Try posting it to this endpoint:
https://api.site.com/oauth2/token
By default HttpClient is using AllowAutoRedirect = true. The documentation says:
The Authorization header is cleared on auto-redirects and the handler automatically tries to re-authenticate to the redirected location. No other headers are cleared. In practice, this means that an application can't put custom authentication information into the Authorization header if it is possible to encounter redirection.
So depending on the setup of the server you might have to create a CookieContainer and do the redirecting on your own.
Update:
The usage of a certificate store is something I didn't get from your question. If you want to do similar handling of certificates like browsers do you have to implement this feature yourself. Here is a C# example of how you can extend WebClient class with a dedicated CookieContainer and X509 certificate handling. I used it with smart card reader. It should work similar in vb.net. Hope it helps to find the right .Net classes and how to put things together:
public class SmartCardClient : WebClient
{
public CookieContainer Cookies = new CookieContainer();
public Uri LastResponseUri = null;
public X509Certificate2 cert = null;
private string IssuerName = null;
public SmartCardClient(string issuerName)
{
IssuerName = issuerName;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
SelectCertificate();
}
protected override WebRequest GetWebRequest(Uri uri)
{
var request = base.GetWebRequest(uri) as HttpWebRequest;
LastResponseUri = null;
if (request != null)
{
request.CookieContainer = Cookies;
request.UseDefaultCredentials = true;
request.AllowAutoRedirect = true;
}
return request;
}
protected override WebResponse GetWebResponse(WebRequest request)
{
WebResponse response = base.GetWebResponse(request);
LastResponseUri = response.ResponseUri;
return response;
}
public void SelectCertificate()
{
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection certs = (X509Certificate2Collection)store.Certificates
.Find(X509FindType.FindByTimeValid, DateTime.Now, false)
.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, false)
.Find(X509FindType.FindByIssuerName, IssuerName, false);
if (certs.Count > 1)
certs = X509Certificate2UI.SelectFromCollection(
certs, "Select Certificate", "Please select a certificate:",
X509SelectionFlag.MultiSelection
);
if (certs.Count > 0)
cert = certs[0];
store.Close();
}
}

Webhook Subscription for Teams Conversation not working with WCF Relay

Can someone please help me to fix this Issue. I am not able to debug from where it is going wrong. Basically I have created a WCF Rest API WebService using WCF Relay in Azure to have hybrid connection between on-premise and Azure. Also if I am ignoring any certificate validation, that endpoint is for on-premise as it is self-signed certificate but when making API call, I am using the base64 encoded public key provided by WCF Relay when publishing it in Azure.
WCF Contract And Implementation:
Contract Interface
Implementation
I am successfully able to get the "validationToken" in the WCF service and also returning the same validationToken immediately below 5 seconds. After returning, it always error out showing this message.
Postman Client For Sending HTTP Request
Error Response and no subscription created
EDIT
WCF Contract
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "webhookForConservation?validationToken={validationToken}",
BodyStyle = WebMessageBodyStyle.Bare)]
string webhookForConservation(WebhookPayload data, string validationToken);
WCF Implementation:
1st approach to return 200 OK status code:
public string webhookForConservation(WebhookPayload data, string validationToken = "")
{
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "text/plain");
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
HttpResponseMessage response = null;
WebOperationContext ctx = WebOperationContext.Current;
if (validationToken != null && validationToken != "")
{
response = client.PostAsync("http://localhost:8080/conversationWebHook/conversationSubscription?validationToken=" + validationToken, null).Result;
var apiContent = response.Content.ReadAsStringAsync().Result;
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
ctx.OutgoingResponse.ContentType = "text/plain";
return apiContent;
}
else
{
StringContent strContent = new StringContent(DataContractJsonSerializerHelper.SerializeJson(data));
strContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
response = client.PostAsync("http://localhost:8080/conversationWebHook/conversationSubscription", strContent).Result;
}
var result = (response != null) ? response.Content.ReadAsStringAsync().Result : "";
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
return result;
}
2nd Approach to return 200 status code:
public WebFaultException<string> webhookForConservation(WebhookPayload data, string validationToken="")
{
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "text/plain");
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
HttpResponseMessage response = null;
if (validationToken != null && validationToken != "")
{
response = client.PostAsync("http://localhost:8080/conversationWebHook/conversationSubscription?validationToken=" + validationToken, null).Result;
var apiContent = response.Content.ReadAsStringAsync().Result;
return new WebFaultException<string>(apiContent, HttpStatusCode.OK);
}
else
{
StringContent strContent = new StringContent(DataContractJsonSerializerHelper.SerializeJson(data));
strContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
response = client.PostAsync("http://localhost:8080/conversationWebHook/conversationSubscription", strContent).Result;
}
var result = (response != null) ? response.Content.ReadAsStringAsync().Result : "";
return new WebFaultException<string>(result, HttpStatusCode.OK); ;
}
Same error seen after returning 200 OK response code from WCF Service
Calling Relay WCF API directly with Postman:
Headers:
Thank you in advance for all the help.
Two problems were preventing the WCF relay to work properly:
The relay wasn't setting the response content type to text/plain, this was fixed with ctx.OutgoingResponse.ContentType = "text/plain"
The relay was adding an XML wrapper to the required response body, this was addressed by changing the return value to Stream

Do I have to use SSL with Paypal? How do I set Paypal on Windows Azure?

I am putting online an old web application I had running like 3 years ago.
Back then everything worked and Paypal's ExpressCheckout was set perfectly.
I really cannot remember what I was doing back then but now I put my app on Windows Azure. My app is written in ASP.NET MVC5.
The following piece of code might look familiar for those of you who implemented Paypal inside your apps and its probably taken from Paypal's documentation and used for posing to Paypal's server:
/// <summary>
/// HttpCall: The main method that is used for all API calls
/// </summary>
/// <param name="NvpRequest"></param>
/// <returns></returns>
public string HttpCall(string NvpRequest) //CallNvpServer
{
string url = pendpointurl;
//To Add the credentials from the profile
string strPost = NvpRequest + "&" + buildCredentialsNVPString();
strPost = strPost + "&BUTTONSOURCE=" + HttpUtility.UrlEncode(BNCode);
HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(url);
objRequest.Timeout = Timeout;
objRequest.Method = "POST";
objRequest.ContentLength = strPost.Length;
objRequest.ContentType = "application/x-www-form-urlencoded";
try
{
using (StreamWriter myWriter = new StreamWriter(objRequest.GetRequestStream()))
{
myWriter.Write(strPost, 0, strPost.Length);
}
}
catch (Exception e)
{
CommonFuncs.Log(MyGlobals.LOG_FILE_DO_EXPRESS_CHECKOUT, e.Message);
return null;
/*
if (log.IsFatalEnabled)
{
log.Fatal(e.Message, this);
}*/
}
//Retrieve the Response returned from the NVP API call to PayPal
HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse();
string result;
using (StreamReader sr = new StreamReader(objResponse.GetResponseStream()))
{
result = sr.ReadToEnd();
}
//Logging the response of the transaction
/* if (log.IsInfoEnabled)
{
log.Info("Result :" +
" Elapsed Time : " + (DateTime.Now - startDate).Milliseconds + " ms" +
result);
}
*/
return result;
}
Now, when I'm trying to POST (here)
using (StreamWriter myWriter = new StreamWriter(objRequest.GetRequestStream()))
{
myWriter.Write(strPost, 0, strPost.Length);
}
I am getting the following error message
The request was aborted: Could not create SSL/TLS secure channel.
Does it mean that I have to purchase an SSL certificate? or is there something I just need to tweek on Azure so it will work?
No need to purchase an SSL. But you can upgrade your certificate into SHA 256 and TLS 1.2. Refer to the link below.
https://www.paypal-knowledge.com/infocenter/index?page=content&id=FAQ1913
and
https://github.com/paypal/TLS-update
Thank you #PP_MTS_Steven.
I can't remember the source on SO which gave me the solution. However, all I did was to put this two lines of code:
ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
// allows for validation of SSL conversations
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Right here:
public string HttpCall(string NvpRequest) //CallNvpServer
{
string url = pendpointurl;
//To Add the credentials from the profile
string strPost = NvpRequest + "&" + buildCredentialsNVPString();
strPost = strPost + "&BUTTONSOURCE=" + HttpUtility.UrlEncode(BNCode);
ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
// allows for validation of SSL conversations
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(url);
objRequest.Timeout = Timeout;
objRequest.Method = "POST";
objRequest.ContentLength = strPost.Length;
objRequest.ContentType = "application/x-www-form-urlencoded";
...
And things started working.

How to consume REST service from a MVC 4 web application?

Can someone give me pointers on how to How to consume an external REST service from a MVC 4 web application? The services rely on an initial call with credentials base 64 encoded, then returns a token which is used for further web service queries.
I cannot find an easy primer on how to do this kind of thing, could someone help please?
I have all this working in classic ASP & JQuery but need to move over to an MVC 4 web application.
You could use the HttpClient class. Here's an example of how you could send a GET request and use Basic Authentication:
var client = new HttpClient();
client.BaseAddress = new Uri("http://foo.com");
var buffer = Encoding.ASCII.GetBytes("john:secret");
var authHeader = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(buffer));
client.DefaultRequestHeaders.Authorization = authHeader;
var response = client.GetAsync("/api/authenticate").Result;
if (response.IsSuccessStatusCode)
{
string responseBody = response.Content.ReadAsStringAsync().Result;
}
Once you have retrieved the access token you could make authenticated calls:
var client = new HttpClient();
client.BaseAddress = new Uri("http://foo.com");
string accessToken = ...
var authHeader = new AuthenticationHeaderValue("Bearar", accessToken);
client.DefaultRequestHeaders.Authorization = authHeader;
var response = client.GetAsync("/api/bar").Result;
if (response.IsSuccessStatusCode)
{
string responseBody = response.Content.ReadAsStringAsync().Result;
}

Eventbrite OAUTH2 login on windows store apps keep me get BAD REQUEST

I got problem on oauth2 handshake, as the eventbrite documentation is not very clear.
http://developer.eventbrite.com/doc/authentication/oauth2/ -> number 4
what i currently do is like this
WebRequest webRequest = WebRequest.Create("https://www.eventbrite.com/oauth/token");
string URLEncoded = "code=" + token + "&client_secret=" + APISecret + "&client_id=" + APIKey + "&grant_type=authorization_code";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Method = "POST";
byte[] bytes = Encoding.UTF8.GetBytes(URLEncoded);
Stream os = null;
try
{
os = await webRequest.GetRequestStreamAsync();
os.Write(bytes, 0, bytes.Length);
WebResponse response = await webRequest.GetResponseAsync();
os = response.GetResponseStream();
StreamReader reader = new StreamReader(os);
string responseFromServer = reader.ReadToEnd();
}
catch (WebException ex)
{
string err = ex.ToString();
}
finally
{
if (os != null)
{
os.Dispose();
}
}
Can someone give tips on this? i keep getting BAD REQUEST as a result. Thank you
edited : the response say : code is invalid or expired
I know its clear, but i already implement and get the token to exchange, use Secret API key and API Key to get it, but how come its invalid/expired?