HMAC authentication via Postman - c#

I'm using an example for setting up HMAC authentication for a Web API project. The original example source code/project is available here:
http://bitoftech.net/2014/12/15/secure-asp-net-web-api-using-api-key-authentication-hmac-authentication/
I'm trying to get Postman to construct and send a GET request in it's pre-request script. However the request always fails with a 401 and I can't figure out why.
Postman pre-request script:
var AppId = "4d53bce03ec34c0a911182d4c228ee6c";
var APIKey = "A93reRTUJHsCuQSHR+L3GxqOJyDmQpCgps102ciuabc=";
var requestURI = "http%3a%2f%2flocalhost%3a55441%2fapi%2fv1%2fdata";
var requestMethod = "GET";
var requestTimeStamp = "{{$timestamp}}";
var nonce = "1";
var requestContentBase64String = "";
var signatureRawData = AppId + requestMethod + requestURI + requestTimeStamp + nonce + requestContentBase64String; //check
var signature = CryptoJS.enc.Utf8.parse(signatureRawData);
var secretByteArray = CryptoJS.enc.Base64.parse(APIKey);
var signatureBytes = CryptoJS.HmacSHA256(signature,secretByteArray)
var requestSignatureBase64String = CryptoJS.enc.Base64.stringify(signatureBytes);
postman.setGlobalVariable("key", "amx " + AppId + ":" + requestSignatureBase64String + ":" + nonce + ":" + requestTimeStamp);

This is the code I'm using in my Pre-Script. It works for any query GET, PUT, POST, DELETE.
You need to change the AppId & the APIKey values and on the last line adjust the name of the environment variable "hmacKey" with yours.
function interpolate (value) {
const {Property} = require('postman-collection');
return Property.replaceSubstitutions(value, pm.variables.toObject());
}
var uuid = require('uuid');
var moment = require("moment")
var hmacPrefix = "hmac";
var AppId = "4d53bce03ec34c0a911182d4c228ee6c";
var APIKey = "A93reRTUJHsCuQSHR+L3GxqOJyDmQpCgps102ciuabc=";
var requestURI = encodeURIComponent(pm.environment.values.substitute(pm.request.url, null, false).toString().toLowerCase());
var requestMethod = pm.request.method;
var requestTimeStamp = moment(new Date().toUTCString()).valueOf() / 1000;
var nonce = uuid.v4();
var requestContentBase64String = "";
var bodyString = interpolate(pm.request.body.toString());
if (bodyString) {
var md5 = CryptoJS.MD5(bodyString);
requestContentBase64String = CryptoJS.enc.Base64.stringify(md5);
}
var signatureRawData = AppId + requestMethod + requestURI + requestTimeStamp + nonce + requestContentBase64String; //check
var signature = CryptoJS.enc.Utf8.parse(signatureRawData);
var secretByteArray = CryptoJS.enc.Base64.parse(APIKey);
var signatureBytes = CryptoJS.HmacSHA256(signature,secretByteArray);
var requestSignatureBase64String = CryptoJS.enc.Base64.stringify(signatureBytes);
var hmacKey = hmacPrefix + " " + AppId + ":" + requestSignatureBase64String + ":" + nonce + ":" + requestTimeStamp;
postman.setEnvironmentVariable("hmacKey", hmacKey);

After a few days of testing I figured out the problem. It was actually to do with the variable placeholders provided by Postman of all things. In testing the placeholder {{$timestamp}} at face value was passing a valid value. When I stripped the signature back to start with just a single segment I was getting authenticated successfully. Until of course I put the timestamp placeholder back in.
When I swapped out the placeholder for the actual value passed in the header it worked fine. I can only conclude that there must be some extra character I can't see. Perhaps on the Postman side when it creates the signature. The problem extends to other placeholders such as {{$guid}}.

If you are having trouble with the script provided by Florian SANTI and you are using Postman v8.0 or higher. You'll need to make sure that the empty string is set by the variable requestContentBase64String is set properly by correctly inspecting the request body.
This is how I solved the issue.
var uuid = require('uuid');
var moment = require("moment")
var AppId = "4d53bce03ec34c0a911182d4c228ee6c";
var APIKey = "A93reRTUJHsCuQSHR+L3GxqOJyDmQpCgps102ciuabc=";
var requestURI = encodeURIComponent(pm.environment.values.substitute(pm.request.url, null, false).toString()).toLowerCase();
var requestMethod = pm.request.method;
var requestTimeStamp = moment(new Date().toUTCString()).valueOf() / 1000;
var nonce = uuid.v4();
var hasBody = (pm.request.body !== null);
if (hasBody) {
hasBody = (!pm.request.body.isEmpty);
}
var requestContentBase64String = "";
if (hasBody) {
var md5 = CryptoJS.MD5(JSON.stringify(postBody));
requestContentBase64String = CryptoJS.enc.Base64.stringify(md5);
}
var signatureRawData = AppId + requestMethod + requestURI + requestTimeStamp + nonce + requestContentBase64String; //check
var signature = CryptoJS.enc.Utf8.parse(signatureRawData);
var secretByteArray = CryptoJS.enc.Base64.parse(APIKey);
var signatureBytes = CryptoJS.HmacSHA256(signature,secretByteArray);
var requestSignatureBase64String = CryptoJS.enc.Base64.stringify(signatureBytes);
var hmacKey = "amx " + AppId + ":" + requestSignatureBase64String + ":" + nonce + ":" + requestTimeStamp;
postman.setEnvironmentVariable("hmacKey", hmacKey );

Related

Incorrect OAuth 1.0a signature generation in C#

After reviewing the OAuth 1.0 documentation for signing requests, I'm struggling getting my signature to match what Postman generates. I've checked things are capitalized / lowercased when necessary, parameters are sorted, encoding is done where appropriate, but I'm missing something.
public string SignRequest(string method, string url, string tokenSecret, Dictionary<string, string> parameters)
{
//, string consumerKey, string token, string timestamp, string nonce, string consumerSecret, string tokenSecret, string identityStatement
string baseString = method.ToUpper() + "&" + Uri.EscapeDataString(url) + "&";
string paramString = "";
var list = parameters.Keys.ToList<string>();
list.Sort();
foreach (string key in list)
{
paramString += key + "=" + Uri.EscapeDataString(parameters[key]) + "&";
}
paramString = paramString.Remove(paramString.Length - 1, 1);
baseString += Uri.EscapeDataString(paramString);
string signingKey = Uri.EscapeDataString(consumerSecret) + "&" + Uri.EscapeDataString(tokenSecret);
var signatureEncoding = new ASCIIEncoding();
var keyBytes = signatureEncoding.GetBytes(signingKey);
var signatureBaseBytes = signatureEncoding.GetBytes(baseString);
var hmacsha1 = new HMACSHA1(keyBytes);
var hashBytes = hmacsha1.ComputeHash(signatureBaseBytes);
var signatureString = Convert.ToBase64String(hashBytes);
return signatureString;
}
I've tried to simplify it down by all the parameters being "1", both secrets "1", the consumer key "1", and a dummy URL for both my implementation and Postman - still getting different signatures. An example of calling it with "1"s and a bogus URL:
postKeys.Add("oauth_consumer_key", "1");
postKeys.Add("oauth_token", "1");
postKeys.Add("oauth_signature_method", "HMAC-SHA1");
postKeys.Add("oauth_timestamp", "1");
postKeys.Add("oauth_nonce", "1");
postKeys.Add("oauth_version", "1");
string signature = SignRequest("GET", "http://hi.com", "1", postKeys);
When I use the same method for the initial retrieval of a token (no token secret yet), my signatures do match a Postman request.
I just can't figure out what I'm missing in here. This seems to match other implementations in other languages, but I can't figure out what I have wrong.

How to download a private bitbucket.org repository using pure REST API calls, version 1 or 2

I'm trying to clone/download my private bitbucket.org repository using C#, but I want to do it using pure HTTPS REST calls, and not a third party lib, i want to learn how it works.
So far I could only find example code for version 1 of the api.
This is what i've got working so far in C#:
static void AUthenticate()
{
var time = GenerateTimeStamp();
var url = "https://bitbucket.org/api/1.0/oauth/request_token";
var secret = "SECRET";
var key = "KEY";
var sigBaseStringParams = "";
sigBaseStringParams += "oauth_callback=http%3A%2F%2Flocal%3Fdump";
sigBaseStringParams += "&" + "oauth_consumer_key=" + key;
sigBaseStringParams += "&" + "oauth_nonce=" + GetNonce();
sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + time;
sigBaseStringParams += "&" + "oauth_version=1.0";
var sigBaseString = "POST&";
sigBaseString += Uri.EscapeDataString(url) + "&" + Uri.EscapeDataString(sigBaseStringParams);
var signature = GetSignature(sigBaseString, secret);
var res = PostData(url, sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature));
var items = GetParameters(res);
var tokenSecret = items["oauth_token_secret"];
var token = items["oauth_token"];
var callbackConfirmed = items["oauth_callback_confirmed"];
url = "https://bitbucket.org/api/1.0/oauth/authenticate?oauth_token=" + token;
}
This authenticates and I get 3 values back. The last URL i paste into a browser, where i grant my application access and i end up with an oauth_verifier
Note: I don't really want to do this as I am writing a server program which won't really be able to send a user to a browser link (but one thing at a time)
I then run the following code:
var url = "https://bitbucket.org/api/1.0/oauth/access_token";
var token = "TOKEN FROM PREV CALL";
var time = GenerateTimeStamp();
var sigBaseStringParams = "";
//sigBaseStringParams += "oauth_callback=http%3A%2F%2Flocal%3Fdump";
var secret = "SECRET";
var key = "KEY";
sigBaseStringParams += "oauth_consumer_key=" + key;
sigBaseStringParams += "&" + "oauth_token=" + token;
sigBaseStringParams += "&" + "oauth_nonce=" + GetNonce();
sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + time;
sigBaseStringParams += "&" + "oauth_verifier=AUTH VERIFIER FROM PREV CALL";
var sigBaseString = "POST&";
sigBaseString += Uri.EscapeDataString(url) + "&" + Uri.EscapeDataString(sigBaseStringParams);
var tokenSecret = "TOKEN SECRET FROM PREVIOUS CALL";
var signature = GetSignature(sigBaseString, secret, tokenSecret);
var res = PostData(url, sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature));
This gives me a 400 bad request. I can't see much else.
I'm following the steps on this page: https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html#OAuthonBitbucketCloud-Refreshtokens
Why I'm doing this is because i want to eventually make a POST request (or GET) to this URL:
var url2 = "https://bitbucket.org/ACCOUNT/REPOSITORY/get/tip.zip?access_token="+token;
Because I think this will give me the entire repository in a zip file. If i put this in the browser it works because i'm logged into bitbucket.
If there is a newer/easier/better way i'm open to suggestions.
Thanks in advance
I found a very simple solution.
I just had to provide credentials to a GET request:
public static void Downloadfiles(string username, string password, string account, string repository, string pathToSave)
{
var creds = Base64Encode(String.Format("{0}:{1}", username, password));
var url = String.Format("https://bitbucket.org/{0}/{1}/get/tip.zip", account, repository);
using (var client = new WebClient())
{
client.Headers.Add("Authorization", "Basic " + creds);
client.Headers.Add("Content-Type", "application/octet-stream");
client.DownloadFile(url, pathToSave);
}
}

how to get json result from payfort Apis in C#

The problem I am facing is that, payfort API should return me some json. But instead i get some html that has some hidden fields. and i see no error written inside there.
Here is my code
string access_code = string.Empty,
amount = string.Empty,
currency = string.Empty,
customer_email = string.Empty,
merchant_reference = string.Empty,
order_description = string.Empty,
language = string.Empty,
merchant_identifier = string.Empty,
signature = string.Empty,
command = string.Empty;
access_code = "X78979879h8h8h8";
amount = "1000";
command = "AUTHORIZATION";
currency = "AED";
customer_email = "zetawars#hotmail.com";
language = "en";
merchant_identifier = "RaskdQuCc";
merchant_reference = "ASASASASASADSS";
order_description = "Some order details";
signature = "";
string signature_string = "PASSaccess_code="+access_code+"amount="+amount+"command="+command+"currency="+currency+"customer_email"+customer_email+"language"+language+"merchant_identifier"+merchant_identifier+"merchant_reference"+merchant_reference+"order_description"+order_description+"PASS";
signature = getHashSha256(signature_string);
string url = "https://sbcheckout.payfort.com/FortAPI/paymentPage";
string param = "access_code" + access_code + "&amount=" + amount + "&currency=" + currency +
"&customer_email=" + customer_email + "&merchant_reference=" + merchant_reference +
"&order_description=" + order_description + "&language=" + language + "merchant_identifier="
+ merchant_identifier + "&signature=" + signature + "&command=" + command;
using (WebClient wc = new WebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = "application/json";
//wc.Headers.Add("Content-Type", "application/json");
string HtmlResult = wc.UploadString(url, param);
}
Try adding values for below header
wc.Headers[HttpRequestHeader.Authorization]
wc.Headers[HttpRequestHeader.TenantId]
wc.Headers[HttpRequestHeader.Client-Type]
wc.Headers[HttpRequestHeader.Protocol]
Works for me!!
First Encode the response
string responseString = Encoding.UTF8.GetString(response);
Use HtmlDocument (HtmlAgilityPack)
var html = new HtmlDocument();
var tokenValue = "";
html.LoadHtml(responseString);
After Loading This into html you can query fields and values.
var tokenFormIdElement =
html.DocumentNode.SelectSingleNode("//input[#name='token']");
tokenValue = tokenFormIdElement.GetAttributeValue("value", "");

Creating a hash in Realex payments

I need to update the Expiry Date and update the Cardholder Name on an existing card in Realex payments.
The hash value syntax should be in the following format:
Timestamp.merchantID.payerref.ref.expirydate.cardnumber
And here is an example of how it should look
20030516175919.yourmerchantid.mypayer.card01.1015.
When I run the following method I get the error:
"sha1hash incorrect - check your code and the Developers Documentation"
private string ReturnHash(string timeStamp, string merchantId, string payerRef, string reference, string expDate, string cardNum )
{
SHA1 hash = new SHA1Managed();
StringBuilder builder = new StringBuilder();
builder.Append(timeStamp).Append(".");
builder.Append(merchantId).Append(".");
builder.Append(payerRef).Append(".");
builder.Append(reference).Append(".");
builder.Append(expDate).Append(".");
builder.Append(cardNum );
string resultingHash = BitConverter.ToString(hash.ComputeHash(Encoding.UTF8.GetBytes(builder.ToString())));
resultingHash = BitConverter.ToString(hash.ComputeHash(Encoding.UTF8.GetBytes(resultingHash)));
return resultingHash;
}
What am I doing wrong?
Thank you for your message.
Could you try before running this line of code:
string resultingHash = BitConverter.ToString(hash.ComputeHash(Encoding.UTF8.GetBytes(builder.ToString())));
To make "resultingHash" all lowercase?
Also before running:
resultingHash = BitConverter.ToString(hash.ComputeHash(Encoding.UTF8.GetBytes(resultingHash)));
make "resultingHash" to lowercase as well.
Thanks,
Borja
var timeStamp = RealexDateFormatter.DateFormatForRealex();
var orderid = model.ORDER_ID;
var secret = ConfigurationManager.AppSettings["Appsecretkey"];
var merchantId = ConfigurationManager.AppSettings["AppMerchantId"];
var temp1 = FormsAuthentication.HashPasswordForStoringInConfigFile(
timeStamp + "." +
merchantId + "." +
orderid + "." +
model.AMOUNT + "." + "EUR", "sha1");
temp1 = temp1.ToLower();
var temp2 = temp1 + "." + secret;
var sha1hash = FormsAuthentication.HashPasswordForStoringInConfigFile(temp2, "sha1");
sha1hash = sha1hash.ToLower();`enter code here`
var url = "https://hpp.sandbox.realexpayments.com/pay?MERCHANT_ID="
+ ConfigurationManager.AppSettings["AppMerchantId"] +
"&ORDER_ID=" + orderid + "&CURRENCY=EUR" + "&AMOUNT=" + model.AMOUNT + "&TIMESTAMP=" + timeStamp + "&SHA1HASH=" + sha1hash + "&MERCHANT_RESPONSE_URL=http://deposit.projectstatus.in/Payment/Response";

Error message when I use xx.Contains in C#

I am trying the following code inside an ASP MVC Razor file:
var topic = ViewData["TopicID"];
var mustBeReplaced = string.Empty;
var topicValue = Model.Topic;
var replaceResult = string.Empty;
if (topic.Contains(topicValue)) {
mustBeReplaced = "value=\"" + topicValue + "\"";
replaceResult = mustBeReplaced + " selected=\"selected\"";
topic = topic.Replace(mustBeReplaced, replaceResult);
}
But I get an error message:
object' does not contain a definition for 'Contains' and the best extension method overload
var topic = ViewData["TopicID"];
Returns object. You need to cast to string.
Try this
var topic = (string)ViewData["TopicID"];
var mustBeReplaced = string.Empty;
var topicValue = "11111";
var replaceResult = string.Empty;
if (topic.Contains(topicValue))
{
mustBeReplaced = "value=\"" + topicValue + "\"";
replaceResult = mustBeReplaced + " selected=\"selected\"";
topic = topic.Replace(mustBeReplaced, replaceResult);
}

Categories