AWS Cognito ConfirmForgotPassword invalid request - c#

I am trying to use the ConfirmForgotPassword call with C# for AWS Cognito. When using the code below all I get is an exception "Request does not contain valid parameters" with no additional information.
Is there any way to find out what the invalid parameter is?
using (var cognito = new AmazonCognitoIdentityProviderClient(AWS_AccessKey, AWS_SecretKey, AWSRegion))
{
ConfirmForgotPasswordRequest confirmForgotPasswordRequest = new ConfirmForgotPasswordRequest();
confirmForgotPasswordRequest.Username = userName;
confirmForgotPasswordRequest.ClientId = clientId;
confirmForgotPasswordRequest.Password = password;
confirmForgotPasswordRequest.ConfirmationCode = confirmationCode;
ConfirmForgotPasswordResponse confirmForgotPasswordResponse = new ConfirmForgotPasswordResponse();
try
{
confirmForgotPasswordResponse = await cognito.ConfirmForgotPasswordAsync(confirmForgotPasswordRequest);
}
catch (Exception ex)
{
// Raises exception "Request does not contain valid parameters"
}
}
I'm not sure if the secret hash parameter is required, I have tried it with this code for the same result
confirmForgotPasswordRequest.SecretHash = HmacSha256(userName + clientId, clientSecret);
private string HmacSha256(string message, string secret)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] keyBytes = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);
byte[] bytes = cryptographer.ComputeHash(messageBytes);
return BitConverter.ToString(bytes).Replace("-", "").ToLower();
}

Related

WooCommerce webhook c# - compare hash

Can someone tell me how I can recreate the hash from WooCommerce webhook to compare with the "X-WC-Webhook-Signature" header hash from the request?
The documentation specifies the hash is generated from the 'payload', but I am unable to generate the same hash.
My API is .NET Core 3.1
First thing i tried:
var secret = "XXX";
var requestHash = Request.Headers["X-WC-Webhook-Signature"];
var generatedHash = "";
Stream byteContent = Request.Body;
byte[] keyByte = encoding.GetBytes(secret);
using(var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(byteContent);
generatedHash = Convert.ToBase64String(hashmessage);
}
if(requestHash == generatedHash)
{
// Succes
}
Second:
using(StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
String json = await reader.ReadToEndAsync();
var generatedHash = "";
byte[] messageBytes = encoding.GetBytes(json);
keyByte = encoding.GetBytes(secret);
using(var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
generatedHash = Convert.ToBase64String(hashmessage);
}
if(requestHash == generatedHash)
{
// Succes
}
}
I had the same problem and here's what I did:
using System.Security.Cryptography;
if (Request.Headers.TryGetValue("X-WC-Webhook-Signature", out var headerValues))
{
XWCWebhookSignature = headerValues.FirstOrDefault();
}
var encoding = new UTF8Encoding();
var key = "yourKeyValue";
var keyBytes = encoding.GetBytes(key);
var hash = new HMACSHA256(keyBytes);
var computedHash = hash.ComputeHash(Request.Body);
var computedHashString = System.Convert.ToBase64String(computedHash);
if (XWCWebhookSignature != computedHashString)
{
return Unauthorized();
}
Update: For this to work you will need to go to Startup.cs file and find "services.Configure" section.
Add options.AllowSynchronousIO = true;
It should look like this:
services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});

c# How to verify signature JWT?

I have a token, a file containing public key and I want to verify the signature.
I tried to verify signature based on this.
However, decodedCrypto and decodedSignature don't match.
Here is my code:
public static string Decode(string token, string key, bool verify)
{
var parts = token.Split('.');
var header = parts[0];
var payload = parts[1];
byte[] crypto = Base64UrlDecode(parts[2]);
var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
var headerData = JObject.Parse(headerJson);
var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
var payloadData = JObject.Parse(payloadJson);
if (verify)
{
var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
var keyBytes = Encoding.UTF8.GetBytes(key);
var algorithm = (string)headerData["alg"];
var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign);
var decodedCrypto = Convert.ToBase64String(crypto);
var decodedSignature = Convert.ToBase64String(signature);
if (decodedCrypto != decodedSignature)
{
throw new ApplicationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
}
}
return payloadData.ToString();
}
I'm sure that the signature of token is valid. I try to verify on https://jwt.io/ and it showed that Signature verified.
So the problem is the algorithm to encode, decode.
Is there anyone can solve this problem? The algorithm is RS256
How about using JwtSecurityTokenHandler?
it could look something like this:
public bool ValidateToken(string token, byte[] secret)
{
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningToken = new BinarySecretSecurityToken(secret)
};
SecurityToken validatedToken;
try
{
tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
}
catch (Exception)
{
return false;
}
return validatedToken != null;
}
Be aware I haven't tested it but we used a similar implementation in one of the projects
I finally got a solution from my colleague.
For those who have the same problem, try my code:
public static string Decode(string token, string key, bool verify = true)
{
string[] parts = token.Split('.');
string header = parts[0];
string payload = parts[1];
byte[] crypto = Base64UrlDecode(parts[2]);
string headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
JObject headerData = JObject.Parse(headerJson);
string payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
JObject payloadData = JObject.Parse(payloadJson);
if (verify)
{
var keyBytes = Convert.FromBase64String(key); // your key here
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
SHA256 sha256 = SHA256.Create();
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(parts[0] + '.' + parts[1]));
RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
rsaDeformatter.SetHashAlgorithm("SHA256");
if (!rsaDeformatter.VerifySignature(hash, FromBase64Url(parts[2])))
throw new ApplicationException(string.Format("Invalid signature"));
}
return payloadData.ToString();
}
It works for me. The algorithm is RS256.
I know this is an old thread but I could have recommended you to use this library instead of writing on your own. It has got some good documentation to get started. Am using it without any issues.
byte[] crypto = Base64UrlDecode(parts[2]);
In this line you are base64 decoding signature part of JWT token, but as I know that part isn't base64 encoded. Please try this code. ( I have commented out unnecessary lines )
public static string Decode(string token, string key, bool verify)
{
var parts = token.Split('.');
var header = parts[0];
var payload = parts[1];
// byte[] crypto = Base64UrlDecode(parts[2]);
var jwtSignature = parts[2];
var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
var headerData = JObject.Parse(headerJson);
var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
var payloadData = JObject.Parse(payloadJson);
if (verify)
{
var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
var keyBytes = Encoding.UTF8.GetBytes(key);
var algorithm = (string)headerData["alg"];
var computedJwtSignature = Encoding.UTF8.GetString(HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign));
// var decodedCrypto = Convert.ToBase64String(crypto);
// var decodedSignature = Convert.ToBase64String(signature);
if (jwtSignature != computedJwtSignature)
{
throw new ApplicationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
}
}
return payloadData.ToString();
}

HMAC validation Failure with code 403

I have been exploring payeezy api from last three days. I am just making a simple http web request from a C# application. I have followed all the steps mentions and correctly verified each and everything. Below is the detail per item.
API Key :- I have verified my api key its correct.
API Secret :- It is also correct.
merchant token :- It is also verified.
Nonce :- I have created cryptographically strong random number as following.
RandomNumberGenerator rng = new RNGCryptoServiceProvider();
byte[] nonceData = new byte[18];
rng.GetBytes(nonceData);
string nonce = BitConverter.ToUInt64(nonceData,0).ToString();
Timestamp :-
string timestamp = Convert.ToInt64(ts.TotalMilliseconds).ToString();
Payload :-
{"merchant_ref":"Astonishing-Sale","transaction_type":"authorize","method":"credit_card","amount":"1299","currency_code":"USD","credit_card":{"type":"visa","cardholder_name":"John Smith","card_number":"4788250000028291","exp_date":"1020","cvv":"123"}}
Then I have created HMAC as following.
private string CreateAuthorization(string data, string secret)
{
// data is in following format.
// data = apiKey + nonce + timestamp + token + payload;
secret = secret ?? "";
using (var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
byte[] hashdata = hmacsha256.ComputeHash(Encoding.UTF32.GetBytes(data));
return Convert.ToBase64String(hashdata);
}
}
Now I am getting hmac validation error. My generated hmac string is 64 bit while on your website under docs and sandbox its 86 bit.
Can you please assist me in this as I am stuck on this issue from last three days.
Thanks
These are the common causes for “HMAC validation Failure”:
API key and/or API secret are incorrect.
Leading or trailing spaces in the API key, API secret, merchant token.
Timestamp in the HTTP header is not in milliseconds.
Timestamp in the HTTP header does not represent EPOCH time.
Timestamp in the HTTP header is not within 5 minutes of our server time.
System time is not accurate.
Here is a sample c# code to generate HMAC:
public byte[] CalculateHMAC(string data, string secret)
{
HMAC hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
byte[] hmac2Hex = hmacSha256.ComputeHash(Encoding.UTF8.GetBytes(data));
string hex = BitConverter.ToString(hmac2Hex);
hex = hex.Replace("-","").ToLower();
byte[] hexArray = Encoding.UTF8.GetBytes(hex);
return hexArray;
}
protected void Button1_Click(object sender, EventArgs e)
{
string jsonString = "{ \"merchant_ref\": \"MVC Test\", \"transaction_type\": \"authorize\", \"method\": \"credit_card\", \"amount\": \"1299\", \"currency_code\": \"USD\", \"credit_card\": { \"type\": \"visa\", \"cardholder_name\": \"Test Name\", \"card_number\": \"4005519200000004\", \"exp_date\": \"1020\", \"cvv\": \"123\" } }";
Random random = new Random();
string nonce = (random.Next(0, 1000000)).ToString();
DateTime date = DateTime.UtcNow;
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
TimeSpan span = (date - epoch);
string time = span.TotalSeconds.ToString();
string token = Request.Form["token"];//Merchant token
string apiKey = Request.Form["apikey"];//apikey
string apiSecret = Request.Form["apisecret"];//API secret
string hashData = apiKey+nonce+time+token+jsonString;
string base64Hash = Convert.ToBase64String(CalculateHMAC(hashData, apiSecret));
string url = "https://api-cert.payeezy.com/v1/transactions";
//begin HttpWebRequest
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.Accept = "*/*";
webRequest.Headers.Add("timestamp", time);
webRequest.Headers.Add("nonce", nonce);
webRequest.Headers.Add("token", token);
webRequest.Headers.Add("apikey", apiKey);
webRequest.Headers.Add("Authorization", base64Hash );
webRequest.ContentLength = jsonString.Length;
webRequest.ContentType = "application/json";
StreamWriter writer = null;
writer = new StreamWriter(webRequest.GetRequestStream());
writer.Write(jsonString);
writer.Close();
string responseString;
try
{
using(HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
{
using (StreamReader responseStream = new StreamReader(webResponse.GetResponseStream()))
{
responseString = responseStream.ReadToEnd();
request_label.Text = "<h3>Request</h3><br />" + webRequest.Headers.ToString() + System.Web.HttpUtility.HtmlEncode(jsonString);
response_label.Text = "<h3>Response</h3><br />" + webResponse.Headers.ToString() + System.Web.HttpUtility.HtmlEncode(responseString);
}
}
}
catch (WebException ex)
{
if (ex.Response != null)
{
using (HttpWebResponse errorResponse = (HttpWebResponse)ex.Response)
{
using (StreamReader reader = new StreamReader(errorResponse.GetResponseStream()))
{
string remoteEx = reader.ReadToEnd();
error.Text = remoteEx;
}
}
}
}
}
I've been working on an integration and it's working great; maybe you can take a peek
https://github.com/clifton-io/Clifton.Payment
Specifically you'll want to look here:
https://github.com/clifton-io/Clifton.Payment/blob/cc4053b0bfe05f2453dc508e96a649fc138b973c/Clifton.Payment/Gateway/Payeezy/PayeezyGateway.cs#L66
Best of luck :)

oauth_problem=token_rejected when refreshing Yahoo's OAuth Access Token

I've done Oauth with Hammock, I succeed to get access token, access token secret and session handle but now I must get the refresh access token when the token expired.
I've followed the instruction and I tried to pass the access token with urldecode and without urldecode but I can't get the token, I obtain
oauth_problem=token_rejected
UPDATE:
that's my code:
##the call##
var AccessTokenQuery = OAuthUtil.GetAccessTokenQueryRenewal(accessToken, session_handle, accessTokenSecret);
AccessTokenQuery.RequestAsync(AppSettings.AccessTokenUri, null);
AccessTokenQuery.QueryResponse += new EventHandler<WebQueryResponseEventArgs>(AccessTokenQuery_QueryResponse);
internal static OAuthWebQuery GetAccessTokenQueryRenewal(string oauth_token,string session_handle, string oauth_token_secret)
{
var oauth = new OAuthWorkflow
{
AccessTokenUrl = AppSettings.AccessTokenUri,
ConsumerKey = AppSettings.consumerKey,
ConsumerSecret = AppSettings.consumerKeySecret,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
TokenSecret = oauth_token_secret,
Token = oauth_token,
SessionHandle = session_handle,
Version = AppSettings.oAuthVersion
};
var info = oauth.BuildAccessTokenInfo(WebMethod.Post);
var objOAuthWebQuery = new OAuthWebQuery(info, false);
objOAuthWebQuery.HasElevatedPermissions = true;
objOAuthWebQuery.SilverlightUserAgentHeader = "Hammock";
return objOAuthWebQuery;
}
void AccessTokenQuery_QueryResponse(object sender, WebQueryResponseEventArgs e)
{
try
{
StreamReader reader = new StreamReader(e.Response);
string strResponse = reader.ReadToEnd();
var parameters = MainUtil.GetQueryParameters(strResponse);
accessToken = parameters["oauth_token"];
accessTokenSecret = parameters["oauth_token_secret"];
session_handle = parameters["oauth_session_handle"];
MainUtil.SetKeyValue<string>("AccessToken", accessToken);
MainUtil.SetKeyValue<string>("AccessTokenSecret", accessTokenSecret);
MainUtil.SetKeyValue<string>("SessionHandle", session_handle);
userLoggedIn();
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(ex.Message);
});
}
}
Quick possibility you could try: you said you 'tried to pass the access token with urldecode'. Have you tried using urlencode? Urldecode is used for decoding urls returned from a web call, encoding is what is done before passing to a web call.
Also, note that the encoding scheme for Oauth is slightly different than the one used in .NET 's default encoding. You can easily write your own encoding routine though, check out the oauth spec for details.
Ex:
private string UrlEncode(string value)
{
string unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
StringBuilder result = new StringBuilder();
foreach (char symbol in value)
{
if (unreserved.IndexOf(symbol) != -1)
result.Append(symbol);
else
result.Append('%' + String.Format("{0:X2}", (int)symbol));
}
return result.ToString();
}

API exception while calling 'DataWarehouseGetReportData' in C#

I am using Omniture api to download a report. The report is completed when I checked the status with DataWarehouseCheckRequest method. Now when I try to fetch the report using DataWarehouseGetReportData method, I get
CommunicationException Error in deserializing body of reply message for operation 'DataWarehouseGetReportData
Inner exception says
The specified type was not recognized: name='data_warehouse_report_row', namespace='http://www.omniture.com/', at <rows xmlns=''>
I am new with C# and the API both. Got no idea how to resolve this. Please help.
Thanks
When you want to download a DW report the best option is to do it over http. This the standard way and is much more efficient.
The response to CheckRequest contains a DataURL. Use that to download the data.
Here is some c# sample code I am using for an almost identical API (Partner vs you Enterprise API) (note I'm no c# expert either, so you will need to do a code review on this).
HttpWebResponse statusResponse = null;
string response = "";
StringBuilder sbUrl = new StringBuilder(dwrq.data_url); // hardcode to variable "rest_url" for testing.
HttpWebRequest omniRequest = (HttpWebRequest)WebRequest.Create(sbUrl.ToString());
string timecreated = generateTimestamp();
string nonce = generateNonce();
string digest = getBase64Digest(nonce + timecreated + secret);
nonce = base64Encode(nonce);
omniRequest.Headers.Add("X-WSSE: UsernameToken Username=\"" + username + "\", PasswordDigest=\"" + digest + "\", Nonce=\"" + nonce + "\", Created=\"" + timecreated + "\"");
omniRequest.Method = "GET"; // Switched from POST as GET is the right HTTP verb in this case
try
{
statusResponse = (HttpWebResponse)omniRequest.GetResponse();
using (Stream receiveStream = statusResponse.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
response = readStream.ReadToEnd();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
Console.WriteLine("Response is a TAB delimeted CSV structure. Printing to screen.");
Console.WriteLine(response);
Console.WriteLine("Ending REST...");
Console.WriteLine("Ending ExportRequestSegmentedData...");
and the supporting methods
/*** Here are the private functions ***/
// Encrypting passwords with SHA1 in .NET and Java
// http://authors.aspalliance.com/thycotic/articles/view.aspx?id=2
private static string getBase64Digest(string input)
{
SHA1 sha = new SHA1Managed();
ASCIIEncoding ae = new ASCIIEncoding();
byte[] data = ae.GetBytes(input);
byte[] digest = sha.ComputeHash(data);
return Convert.ToBase64String(digest);
}
// generate random nonce
private static string generateNonce()
{
Random random = new Random();
int len = 24;
string chars = "0123456789abcdef";
string nonce = "";
for (int i = 0; i < len; i++)
{
nonce += chars.Substring(Convert.ToInt32(Math.Floor(random.NextDouble() * chars.Length)), 1);
}
return nonce;
}
// Time stamp in UTC string
private static string generateTimestamp()
{
return DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
}
// C#-Base64 Encoding
// http://www.vbforums.com/showthread.php?t=287324
public static string base64Encode(string data)
{
byte[] encData_byte = new byte[data.Length];
encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
string encodedData = Convert.ToBase64String(encData_byte);
return encodedData;
}
Best of Luck! C.

Categories

Resources