I have registered a webhook and provided a secret as documented on https://www.weavy.com/docs/backend/webhooks.
When the payload is delivered to my url I want to verify the signature, but I can't seem to get the calculation correct. What am I doing wrong?
Here is the code I'm using:
public static bool Verify(string signature, string body, string secret)
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var hash = Encoding.UTF8.GetString(hashBytes);
return signature.Equals(hash);
}
}
The documentation says the signature is a HMAC hex digest so instead of converting hashBytes to an UTF8 string you should convert it to a hexadecimal string.
public static bool Verify(string signature, string body, string secret)
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(Secret)))
{
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var hash = Convert.ToHexString(hashBytes).ToLowerInvariant();
return signature.Equals(hash);
}
}
Related
I'm trying to verify a webhook from bamboohr. the documentation is here https://documentation.bamboohr.com/docs/webhooks
and after unpacking the headers I see this:
X-BambooHR-Signature: 362cb0eff0193af8d3f939349f84014e5c70bba4cfc105682b45ecd305db01ff
X-BambooHR-Timestamp: 1652747163
Here is my code, from an azure function triggered by the webhook. The testOutput is not whats in the ‘X-BambooHR-Signature’ header:
string data;
using (var reader = new StreamReader(req.Body))
{
data = await reader.ReadToEndAsync();
}
string privateKey = "<gotten from bamboohr webhookconfig>";
if (req.Headers.Keys.Contains("X-BambooHR-Signature") && req.Headers.Keys.Contains("X-BambooHR-Timestamp"))
{
string timestamp = req.Headers["X-BambooHR-Timestamp"];
string signature = req.Headers["X-BambooHR-Signature"];
byte[] privateKeyBytes = Encoding.UTF8.GetBytes(privateKey);
byte[] combinedBytes = Encoding.UTF8.GetBytes(data + timestamp);
HMACSHA256 hmac = new HMACSHA256(privateKeyBytes);
byte[] testOutputBytes = hmac.ComputeHash(combinedBytes);
string testOutput = Convert.ToBase64String(testOutputBytes);
log.LogInformation("testOutput is: " + testOutput); //----NOT EQUAL TO signature.
}
Any idea what I might be doing wrong? the testUutput is something like 'llBdZd2IfVdrJBlkGFFNG2dszDxpgJlJ4vQqTATJsYU=' which as you can see isnt even close.
Instead of using ToBase64String, try converting it to hexadecimal format. (There are different ways to convert it.) I had similar issue verifying bambooHR signature key in python, and fixed it by converting expected signature to hexadecimal format (not bytes or bytes string).
c#
using System;
using System.Text;
using System.Security.Cryptography;
namespace Test
{
public class VerifySignature
{
public static void Main(string[] args)
{
string data = "request data";
string privateKey = "your private key";
string timestamp = "1652747163";
string signature = "362cb0eff0193af8d3f939349f84014e5c70bba4cfc105682b45ecd305db01ff";
byte[] privateKeyBytes = Encoding.UTF8.GetBytes(privateKey);
byte[] combinedBytes = Encoding.UTF8.GetBytes(data + timestamp);
HMACSHA256 hmac = new HMACSHA256(privateKeyBytes);
byte[] testOutputBytes = hmac.ComputeHash(combinedBytes);
// edited from here
// string testOutput = Convert.ToBase64String(testOutputBytes);
// log.LogInformation("testOutput is: " + testOutput);
string hexOutput = BitConverter.ToString(testOutputBytes).Replace("-",""); // convert to hexadecimal format
Console.WriteLine(hexOutput);
}
}
}
python
import hmac
from hashlib import sha256
def verify_signature(request):
received_signature = 'get sig from request headers'
timestamp = 'get timestamp from request headers'
private_key = 'your private key'
body = request.body
calculated_signature = hmac.new(
private_key, body + timestamp, sha256
).hexdigest()
return received_signature == calculated_signature
I hope this helps although it's a bit late!
I need c# equivalent of the below nodejs code. I have some c# code, but the results are not the same.
//working node code below
Ice3x.prototype._signMessage = function (message) {
var hmac = crypto.createHmac('sha512',new Buffer(this.secret, 'base64'));
hmac.update(message);
var signature = hmac.digest('base64');
return signature;
}
//c# code below
public class HmacSignatureCalculator : ICalculteSignature
{
public string Signature(string secret, string value)
{
var secretBytes = Encoding.UTF8.GetBytes(secret);
var valueBytes = Encoding.UTF8.GetBytes(value);
string signature;
using (var hmac = new HMACSHA512(secretBytes))
{
var hash = hmac.ComputeHash(valueBytes);
signature = Convert.ToBase64String(hash);
}
return signature;
}
}
It looks like the difference comes from the way the secret is encoded. In the node version it assumes that it represents a base64 encoded byte array, whereas in your C# version you treat it as a normal string.
So in your C# version read the byte array from the base 64 encoded secret:
var secretBytes = Convert.FromBase64String(secret);
Now you are consistent with the node version:
var hmac = crypto.createHmac('sha512', new Buffer(this.secret, 'base64'));
I have salt and encrypted password in my db. and want to create a login function. I created a login function but it always returns false.
My function for conversion is here, is it right or not?
public static String HashPassword(String password, String salt)
{
var combinedPassword = String.Concat(password, salt);
var sha256 = new SHA256Managed();
var bytes = UTF8Encoding.UTF8.GetBytes(combinedPassword);
var hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
public static Boolean ValidatePassword(String enteredPassword, String storedHash, String storedSalt)
{
// Consider this function as an internal function where parameters like
// storedHash and storedSalt are read from the database and then passed.
var hash = HashPassword(enteredPassword, storedSalt);
return String.Equals(storedHash, hash);
}
The new authentication of bitstamp says the following:
Signature is a HMAC-SHA256 encoded message containing: nonce, client ID and API key. The HMAC-SHA256 code must be generated using a secret key that was generated with your API key. This code must be converted to it's hexadecimal representation (64 uppercase characters).Example (Python):
message = nonce + client_id + api_key
signature = hmac.new(API_SECRET, msg=message, digestmod=hashlib.sha256).hexdigest().upper()
Source: link
I've got the following code to add the new signature (and other parameters):
public void AddApiAuthentication(RestRequest restRequest)
{
var nonce = DateTime.Now.Ticks;
var signature = GetSignature(nonce, apiKey, apiSecret, clientId);
restRequest.AddParameter("key", apiKey);
restRequest.AddParameter("signature", signature);
restRequest.AddParameter("nonce", nonce);
}
private string GetSignature(long nonce, string key, string secret, string clientId)
{
string msg = string.Format("{0}{1}{2}", nonce,
clientId,
key);
return ByteArrayToString(SignHMACSHA256(secret, StrinToByteArray(msg))).ToUpper();
}
public static byte[] SignHMACSHA256(String key, byte[] data)
{
HMACSHA256 hashMaker = new HMACSHA256(Encoding.ASCII.GetBytes(key));
return hashMaker.ComputeHash(data);
}
public static byte[] StrinToByteArray(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public static string ByteArrayToString(byte[] hash)
{
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
And then I get this error:
{"error": "Invalid signature"}
Anyone got an idea what the problem could be? I checked my parameters a 100 times and those aren't wrong. Maybe somebody got a working piece of code (in C#) for the new authentication?
UPDATE
Abhinav was right, the StringToByteArray method was wrong (not only the typo :P) the working code is:
public static byte[] StrinToByteArray(string str)
{
return System.Text.Encoding.ASCII.GetBytes(str);
}
You are using str.ToCharArray() in StrinToByteArray which is incorrect (correct ONLY when used on the same system). You need to use ASCII encoding or something.
I must convert a JAVA function that Hashing a string.
this is a function:
private static String hmacSha256(String value, String key) throws NoSuchAlgorithmException, InvalidKeyException {
byte[] keyBytes = key.getBytes();
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(value.getBytes());
return String.format("%0" + (rawHmac.length << 1) + "x", new BigInteger(1, rawHmac));
}
My doubt is: this function take 2 parameters:
String value: It is the string to crypt
String Key: It is another key
I already used the Sha256, but I always use it with only one parameter (one string to encrypt)
please, how can I wrote this function in c# or is there anyone who can explain to me the logical?
thank you
You can use HMACSHA256 class to make it work:
private static string ComputeHash(string key, string value)
{
var byteKey = Encoding.UTF8.GetBytes(key);
string hashString;
using (var hmac = new HMACSHA256(byteKey))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(value));
hashString = Convert.ToBase64String(hash);
}
return hashString;
}
This is not plain SHA256, this is HMACSHA256 and there is allready a class in .Net.
HMACSHA256