C# version of the javascript function Cryptojs.HmacSHA384 - c#

I am trying to connect to an API where I need to authenticate by passing in the header a hashed string in my C# application. In the documentation from the provider though they only have an example of how to hash the string using JS functions in a react application, bellow is the screenshot of the code snippet found in said documentation.
I have already found an article here on stackoverflow on an alternative for the btoa function but I am completely stuck on the CryptoJS.HmacSHA384 function alternative. I have tried using the System.Security.Cryptography.HMACSHA384 class but can't figure out how to encode the strings I am passing to the method in order to obtain the same result, causing an Authorization denied when I try to connect to the API endpoint. Bellow is the code from the method I have written so far (that is not working):
public void myMethodToConnet(string user, string pass, string custId, string partId, string partKey, string ver, string commId)
{
int resetPasswordErrorCode;
_user = user;
_password = pass;
_rooftopId = custId;
_partnerId = partId;
_partnerKey = Encoding.UTF8.GetBytes(partKey);
_version = ver;
_communityId = commId;
_url = "url";
_token = GetToken().Result;
using (HMACSHA384 hmac = new HMACSHA384(_partnerKey))
{
_hmac = hmac.ComputeHash(Encoding.UTF8.GetBytes(_token));
_hash = BTOA($"{_user}:{Encoding.UTF8.GetString(_hmac)}:{_password}");
}
ActivateToken().Result;
}
private static string BTOA(string toEncode)
{
byte[] bytes = Encoding.GetEncoding(28591).GetBytes(toEncode);
string result = System.Convert.ToBase64String(bytes);
return result;
}
private async Task<int> ActivateToken()
{
CheckPasswordRequest request = new CheckPasswordRequest();
CheckPasswordResponse response = new CheckPasswordResponse();
StringContent content;
string jsonString;
string apiResponse;
int errorCode = 0;
using (HttpClient httpClient = new HttpClient())
{
request = new CheckPasswordRequest() { RooftopId = _rooftopId };
jsonString = JsonConvert.SerializeObject(request);
content = new StringContent(jsonString, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Add("Authorization", $"DataHub-Hash {_hash}");
using (var resp = await httpClient.PostAsync($"{_url}/CheckPassword", content))
{
apiResponse = await resp.Content.ReadAsStringAsync();
response = JsonConvert.DeserializeObject<CheckPasswordResponse>(apiResponse);
errorCode = response.ErrorCode;
}
}
return errorCode;
}
Thanks!

I finally figured it out by creating a simple HTML page where I ran the JS commands and printed out the output to than compare it to what the output in my C# application was. At the end the solution was that of converting the byte array output of the encryption algorithm to hex, to do so I created this helper method:
public static string ByteToString(byte[] input)
{
string output= "";
for (int i = 0; i < input.Length; i++)
{
output+= input[i].ToString("X2");
}
return (output);
}
by inserting the output string of this method between the user and password and running my BTOA helper method returned the string the API was expecting and managed to authenticate.

Related

How to make an OAuth 1 Twitter API call with C# (dotnet core 3.1)

I want to be able to search for twitter handles from a dotnet core API service. I've looked at the twitter documentation for users/search.json and begged, borrowed and stolen what code examples I can from the stackoverflow, etc. (see below), but all I get back is:
{"errors":[{"code":215,"message":"Bad Authentication data."}]}
when I execute the resulting curl command.
I'm sorry the code is a bit of a mess, but can anyone see what I'm doing wrong? Or better still, if there's a library which will do this for me, I've been unable to find one, that would be even better.
using Xunit;
using System;
using System.Linq;
using System.Collections.Generic;
using OAuth; // OAuth.DotNetCore, 3.0.1
using System.IO;
using System.Net;
namespace TwitterLibTest
{
public class BuildHeaderTest
{
private static readonly string consumerKey = "...";
private static readonly string consumerSecret = "...";
private static readonly string method = "GET";
private static readonly OAuthSignatureMethod oauthSignatureMethod = OAuthSignatureMethod.HmacSha1;
private static readonly string oauthVersion = "1.0a";
[Fact]
public void Header()
{
var url = "https://api.twitter.com/1.1/users/search.json";
var generatedNonce = RandomString(32);
var generatedTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
var oauthToken = BuildAuthToken(consumerKey, consumerSecret);
var generatedSignature = GetSignatureBaseString(method, url, generatedTimestamp, generatedNonce, consumerKey, oauthToken, oauthSignatureMethod.ToString(), oauthVersion, new SortedDictionary<string, string>());
Console.WriteLine($"curl --request GET --url '{url}?q=soccer' --header 'authorization: OAuth oauth_consumer_key=\"{consumerKey}\", oauth_nonce=\"{generatedNonce}\", oauth_signature=\"{generatedSignature}\", oauth_signature_method=\"{oauthSignatureMethod.ToString()}\", oauth_timestamp=\"{generatedTimestamp}\", oauth_token=\"{oauthToken}\", oauth_version=\"{oauthVersion}\"'");
}
private static Random random = new Random();
private static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
// https://stackoverflow.com/questions/35677711/generate-oauth1-signature-in-c-sharp
private static string GetSignatureBaseString(string method, string strUrl, string timeStamp,
string nonce, string strConsumer, string strOauthToken, string oauthSignatureMethod,
string oauthVersion, SortedDictionary<string, string> data)
{
//1.Convert the HTTP Method to uppercase and set the output string equal to this value.
string Signature_Base_String = method.ToUpper();
Signature_Base_String = Signature_Base_String.ToUpper();
//2.Append the ‘&’ character to the output string.
Signature_Base_String = Signature_Base_String + "&";
//3.Percent encode the URL and append it to the output string.
string PercentEncodedURL = Uri.EscapeDataString(strUrl);
Signature_Base_String = Signature_Base_String + PercentEncodedURL;
//4.Append the ‘&’ character to the output string.
Signature_Base_String = Signature_Base_String + "&";
//5.append OAuth parameter string to the output string.
var parameters = new SortedDictionary<string, string>
{
{"oauth_consumer_key", strConsumer},
{"oauth_token", strOauthToken },
{"oauth_signature_method", oauthSignatureMethod},
{"oauth_timestamp", timeStamp},
{"oauth_nonce", nonce},
{"oauth_version", oauthVersion}
};
//6.append parameter string to the output string.
foreach (KeyValuePair<string, string> elt in data)
{
parameters.Add(elt.Key, elt.Value);
}
bool first = true;
foreach (KeyValuePair<string, string> elt in parameters)
{
if (first)
{
Signature_Base_String = Signature_Base_String + Uri.EscapeDataString(elt.Key + "=" + elt.Value);
first = false;
}
else
{
Signature_Base_String = Signature_Base_String + Uri.EscapeDataString("&" + elt.Key + "=" + elt.Value);
}
}
return Signature_Base_String;
}
private string BuildAuthToken(string consumerKey, string consumerSecret)
{
var client = Client(consumerKey, consumerSecret);
var response = Get(client);
var tokenMap = Parse(response);
return tokenMap["oauth_token"];
}
private static OAuthRequest Client(string consumerKey, string consumerSecret)
{
return new OAuthRequest
{
Method = method,
Type = OAuthRequestType.RequestToken,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
ConsumerKey = consumerKey,
ConsumerSecret = consumerSecret,
RequestUrl = "https://api.twitter.com/oauth/request_token",
Version = oauthVersion,
};
}
private static HttpWebResponse Get(OAuthRequest client)
{
string auth = client.GetAuthorizationHeader();
var request = (HttpWebRequest) WebRequest.Create(client.RequestUrl);
request.Headers.Add("Authorization", auth);
return (HttpWebResponse) request.GetResponse();
}
private static Dictionary<string, string> Parse(HttpWebResponse response)
{
using var stream = response.GetResponseStream() ;
using var reader = new StreamReader( stream );
var responseAsText = reader.ReadToEnd();
var map = new Dictionary<string, string>();
foreach( var token in responseAsText.Split("&"))
{
var tokens = token.Split("=");
map.Add(tokens[0], tokens[1]);
}
return map;
}
}
}
I don't think you need to do all of the signing and signature stuff separately like that - here's an example project that also uses OAuth.DotNetCore, which does "do it for you". In this case, I've used HttpWebRequest directly, instead of shelling out to use a curl command.
using System;
using OAuth;
using System.Net;
using System.IO;
namespace TwitterDotNetCore
{
class Program
{
static void Main(string[] args)
{
// convenient to load keys and tokens from a config file for testing
// edit .env to add your keys and tokens (no quotation marks)
DotNetEnv.Env.Load();
string CONSUMER_KEY = System.Environment.GetEnvironmentVariable("CONSUMER_KEY");
string CONSUMER_TOKEN = System.Environment.GetEnvironmentVariable("CONSUMER_TOKEN");
string ACCESS_TOKEN = System.Environment.GetEnvironmentVariable("ACCESS_TOKEN");
string ACCESS_TOKEN_SECRET = System.Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET");
// this is the endpoint we will be calling
string REQUEST_URL = "https://api.twitter.com/1.1/users/search.json?q=soccer";
// Create a new connection to the OAuth server, with a helper method
OAuthRequest client = OAuthRequest.ForProtectedResource("GET", CONSUMER_KEY, CONSUMER_TOKEN, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
client.RequestUrl = REQUEST_URL;
// add HTTP header authorization
string auth = client.GetAuthorizationHeader();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(client.RequestUrl);
request.Headers.Add("Authorization", auth);
Console.WriteLine("Calling " + REQUEST_URL);
// make the call and print the string value of the response JSON
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string strResponse = reader.ReadToEnd();
Console.WriteLine(strResponse); // we have a string (JSON)
}
}
}
Turns out all I needed was https://github.com/linvi/tweetinvi and:
Auth.SetUserCredentials(APIkey, APISecretKey, AccessToken, AccessTokenSecret);
Search.SearchUsers("...").Select(u => ...);

Getting 404 error only when using .NET framework

I'm trying to create a version in JIRA for a specific project.
I'm able to do the process via Postman by building my requests manually, but it fails with a 404 when creating the version record via .NET.
I'm assuming .NET adds pesky parameters to the request that Postman doesn't do.
The weird thing is that the authentication call works, but the the version creation fails.
Here's the helper I wrote:
public class JIRA
{
private string AuthToken { get; set; }
private const string c_JIRAUrl = "https://org.atlassian.net";
private const string c_LoginUrl = c_JIRAUrl + "/rest/auth/1/session";
private const string c_CreateVersionUrl = c_JIRAUrl + "/rest/api/2/version";
public JIRA()
{
//this works...
var authResponse = ExecuteRequest(c_LoginUrl, "POST", new
{
username = "login",
password = "password"
});
AuthToken = authResponse["session"]["value"].ToString();
}
public void CreateVersion(string name, string projectKey, ProjectEnvironment environment)
{
//lets hardcode the same data I use in Postman for testing purposes...
var createVersionResponse = ExecuteRequest(c_CreateVersionUrl, "POST", new
{
description = "An excellent version",
name = "1.1.2",
archived = false,
released = false,
project = "TEST"
});
}
private JObject ExecuteRequest(string url, string method, object data)
{
HttpWebResponse response;
var jsonDataString = JsonConvert.SerializeObject(data);
byte[] dataBytes = Encoding.Default.GetBytes(jsonDataString);
var responseText = string.Empty;
var wr = (HttpWebRequest)WebRequest.Create(url);
wr.ContentType = "application/json";
if (!string.IsNullOrEmpty(AuthToken))
wr.Headers.Add(HttpRequestHeader.Authorization, $"Bearer {AuthToken}");
wr.Method = method;
wr.ContentLength = dataBytes.Length;
wr.Accept = "application/json";
using (var webStream = wr.GetRequestStream())
{
webStream.Write(dataBytes, 0, dataBytes.Length);
response = (HttpWebResponse)wr.GetResponse();
}
using (var sr = new StreamReader(response.GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return JObject.Parse(responseText);
}
}
The CreateVersion method always fails with a 404.
As I've said, doing the same (retrieving the token, creating the version) all works in Postman.
Any ideas what's going on ?
Thanks.
Apparently, when retrieving the token (/rest/auth/1/session) the response contains cookies that POSTMAN was sending back in the 2nd request (creating the version). I had to fire up Fiddler to find out it was doing so because its UI was not saying so.
My .NET client was not doing so. When making it do so, it works.
I'm a little miffed that a REST service expects cookies...

Authentication error in Cloud JIRA REST API using asp.net (C#)?

I am working on a requirement where I want to upload the multiple test cases into JIRA. We are using the cloud JIRA hence we are using the JIRA rest API for cloud. I am new into API integration hence not sure how to accomplish my task. However I write few lines of code login connection:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello and welcome to a Jira Example application!");
Console.Write("Username: ");
string username = Console.ReadLine();
Console.Write("Password: ");
string password = Console.ReadLine();
JiraManager manager = new JiraManager(username, password);
manager.RunQuery(JiraResource.project, null, null, "GET");
Console.Read();
}
public enum JiraResource
{
project
}
public class JiraManager
{
private const string m_BaseUrl = "https://domain.atlassian.net/rest/auth/1/session";
private string m_Username;
private string m_Password;
public JiraManager(string username, string password)
{
m_Username = username;
m_Password = password;
}
public string RunQuery(string argument, string data, string method)
{
argument = null;
data = null;
method = "GET";
return RunQuery(argument, data, method);
}
public void RunQuery(JiraResource resource, string argument, string data, string method)
{
string url = string.Format("{0}{1}/", m_BaseUrl, resource.ToString());
if (argument != null)
{
url = string.Format("{0}{1}/", url, argument);
}
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.ContentType = "application/json";
request.Method = method;
if (data != null)
{
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(data);
}
}
string base64Credentials = GetEncodedCredentials();
request.Headers.Add("Authorization", "Basic " + base64Credentials);
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
string result = string.Empty;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
Console.WriteLine(result);
}
private string GetEncodedCredentials()
{
string mergedCredentials = string.Format("{0}:{1}", m_Username, m_Password);
byte[] byteCredentials = UTF8Encoding.UTF8.GetBytes(mergedCredentials);
return Convert.ToBase64String(byteCredentials);
}
}
However When I am running the above code, I got the following error:
the remote server returned an error (404)
But few time back I was getting the different error by executing the same code:
the remote server returned an error (401) unauthorized.
I tried different work arounds but all went in vain. May I know why this error is coming? What could be the resolution of this error?

Is there any way I can get the results from php and use it on c#? Like for Login?

This is my PHP file.
require "conn.php";
$user_name = $_POST["user_name"];
$user_pass = $_POST["password"];
$mysql_qry = "select * from companies where company_username like '$user_name' and company_password like '$user_pass'";
if(mysqli_num_rows($result) > 0)
{
echo "Company Login Successful";
}
else{
echo "Login Error! Please try again";
}
Should I use WebClient?
Sorry I'm just a newbie,thanks :)
There are quite some possibilities. I will present one, but this is far from the single one.
First of all I would not use the standard content type (most likely text/html, but I have not checked it). You could use JSON or XML as a content type, I will use JSON here
header("Content-Type: application/json");
This is not necessary, we could parse a JSON disregarded of the Content-Type header, but it's cleaner that way.
Next we will have to generate the output
$arr = array('Status' => 1, 'Message' => 'Company Login Successful'); //or otherwise, depending on if the login *was* successful
echo json_encode($arr);
On the C# side you can use HttpClient class to fetch the result.
HttpClient httpClient = new HttpClient();
var httpResponseMessage = await httpClient.PostAsync($"http://www.yourserver.net/login.php", loginHttpContent);
var stream = await httpResponseMessage.ReadAsStreamAsync();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(LoginResult));
var loginResult = serializer.ReadObject(stream) as LoginResult;
LoginResult class:
[DataContract]
class LoginResult
{
[DataMember]
public int Status { get; set; }
[DataMember]
public string Message { get; set; }
}
Please note: The code provided is just a sketch and not at all production-ready. For example you should not create the HttpClient or the DataContractJsonSerializer at this point, dispose resources correctly, handle exceptions, etc.
I searched for solutions and I saw a simple way to get the results from PHP...
private void SubmitData()
{
try
{
string user = tbUsername.Text;
string pass = tbPassword.Text;
string url = "--my_php_url--";
WebClient webclient = new WebClient();
NameValueCollection formData = new NameValueCollection();
formData["user_name"] = user;
formData["password"] = pass;
byte[] responseBytes = webclient.UploadValues(url, "POST", formData);
string responsefromserver = Encoding.UTF8.GetString(responseBytes);
MessageBox.Show(responsefromserver);
webclient.Dispose();
}
catch (Exception e )
{
MessageBox.Show("Error :" + e.Message);
}
Thanks for the help! :)

Azure Storage Blob Rest Api Headers

I can't upload image to my azure storage by REST API.
This is my code:
using (var client = new HttpClient())
{
using (var fileStream = await _eventPhoto.OpenStreamForReadAsync())
{
var content = new StreamContent(fileStream);
content.Headers.Add("Content-Type", _eventPhoto.ContentType);
content.Headers.Add("x-ms-blob-type", "BlockBlob");
var uploadResponse = await client.PutAsync(new Uri("https://myservice.blob.core.windows.net/photos/newblob"),content);
int x = 2;
}
}
I get error about missing paramerer. Probably Authorization.
1) How to add missing headers?
2) Someone have working sample?
3) How to add Authorization?
Do you have a reason not to be using the client library that does this for you already? If you have a reason and you really want to use HttpClient, then you need to either a.) implement signing per spec, or b.) use a Shared Access Signature in your URI.
using System;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography;
namespace AzureSharedKey
{
class Program
{
private const string blobStorageAccount = "your_account";
private const string blobStorageAccessKey = "your_access_key";
private const string blobPath = "blob_path";
private const string blobContainer = "your_container";
static void Main(string[] args)
{
Console.WriteLine("Program Initiated..");
CreateContainer();
Console.ReadLine();
}
private async static void CreateContainer()
{
string requestMethod = "GET";
string msVersion = "2016-05-31";
string date = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
string clientRequestId = Guid.NewGuid().ToString();
string canHeaders = string.Format("x-ms-client-request-id:{0}\nx-ms-date:{1}\nx-ms-version:{2}", clientRequestId, date, msVersion);
string canResource = string.Format("/{0}/{1}/{2}", blobStorageAccount, blobContainer, blobPath);
string SignStr = string.Format("{0}\n\n\n\n\n\n\n\n\n\n\n\n{1}\n{2}", requestMethod, canHeaders, canResource);
string auth = CreateAuthString(SignStr);
string urlPath = string.Format("https://{0}.blob.core.windows.net/{1}/{2}", blobStorageAccount, blobContainer, blobPath);
Uri uri = new Uri(urlPath);
Console.WriteLine("urlPath "+ urlPath);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("x-ms-date", date);
client.DefaultRequestHeaders.Add("x-ms-version", "2016-05-31");
client.DefaultRequestHeaders.Add("x-ms-client-request-id", clientRequestId);
client.DefaultRequestHeaders.Add("Authorization", auth);
HttpResponseMessage response = client.GetAsync(uri).Result;
if (response.IsSuccessStatusCode)
{
string actualFilename= Path.GetFileName(blobPath);
string physicaPath = "D:/Others/AZ/";//change path to where you want to write the file
HttpContent content = response.Content;
string pathName = Path.GetFullPath(physicaPath + actualFilename);
FileStream fileStream = null;
try
{
fileStream = new FileStream(pathName, FileMode.Create, FileAccess.Write, FileShare.None);
await content.CopyToAsync(fileStream).ContinueWith(
(copyTask) =>
{
fileStream.Close();
});
Console.WriteLine("FileName: "+actualFilename);
Console.WriteLine("File Saved Successfully..");
}
catch
{
if (fileStream != null) fileStream.Close();
throw;
}
}
else
{
throw new FileNotFoundException();
}
}
private static string CreateAuthString(string SignStr)
{
string signature = string.Empty;
byte[] unicodeKey = Convert.FromBase64String(blobStorageAccessKey);
using (HMACSHA256 hmacSha256 = new HMACSHA256(unicodeKey))
{
byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(SignStr);
signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}
string authorizationHeader = string.Format(
CultureInfo.InvariantCulture,
"{0} {1}:{2}",
"SharedKey",
blobStorageAccount,
signature);
return authorizationHeader;
}
}
}
this code worked for me
Have a look at this example.
http://www.codeninjango.com/programming/azure/azure-blob-storage-rest-api/
It gives a step by step guide to using the Azure Blob Storage REST API to upload chunked files with SAS url's.

Categories