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'));
Related
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);
}
}
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'm trying to port a C# application into Node.
The app has this C# function to generate a Sha256
public static string CreateSHA256Signature(string targetText)
{
string _secureSecret = "E49756B4C8FAB4E48222A3E7F3B97CC3";
byte[] convertedHash = new byte[_secureSecret.Length / 2];
for (int i = 0; i < _secureSecret.Length / 2; i++)
{
convertedHash[i] = (byte)Int32.Parse(_secureSecret.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
}
string hexHash = "";
using (HMACSHA256 hasher = new HMACSHA256(convertedHash))
{
byte[] hashValue = hasher.ComputeHash(Encoding.UTF8.GetBytes(targetText));
foreach (byte b in hashValue)
{
hexHash += b.ToString("X2");
}
}
return hexHash;
}
Response.Write(CreateSHA256Signature("TEST STRING"));
// returns 55A891E416F480D5BE52B7985557B24A1028E4DAB79B64D0C5088F948EB3F52E
I tried to use node crypto as following:
console.log(crypto.createHmac('sha256', 'E49756B4C8FAB4E48222A3E7F3B97CC3').update('TEST STRING', 'utf-8').digest('hex'))
// returns bc0a28c3f60d323404bca7dfc4261d1280ce46e887dc991beb2c5bf5e7ec6100
How can I get the same C# result in node?
Your key is different from the C# version. Try to convert the hex string to raw bytes. This way crypto knows to take the bytes instead of the actual string.
For example:
var crypto = require('crypto');
var key = Buffer.from('E49756B4C8FAB4E48222A3E7F3B97CC3', 'hex');
console.log(crypto.createHmac('sha256', key).update('TEST STRING').digest('hex'))
For Python ninjas
import hmac
import hashlib
import binascii
def create_sha256_signature(key, message):
byte_key = binascii.unhexlify(key)
message = message.encode()
return hmac.new(byte_key, message, hashlib.sha256).hexdigest().upper()
http://www.gauravvjn.com/generate-hmac-sha256-signature-in-python/
I need to sign the string with private Key using RSA phpseclib and then verify it in C# . I have seen many examples of how to encrypt in C# and decrypt in php, but none of how to sign string in php and verify in .NET.
here is php code:
include('Crypt/RSA.php');
$info = "Something";
$PrivateKey= "<RSAKeyValue><Modulus>3C5QWo4H+............"; //long string
$unsignedString = base64_encode($info);
$signedString = HashAndSignBytes($info, $PrivateKey);
file_put_contents('file.txt', $unsignedString."\n".$signedString);
function HashAndSignBytes($stringToSign, $Key) {
$rsa = new Crypt_RSA();
$rsa->loadKey($Key); // private key
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($stringToSign);
return base64_encode($signature);
}
and here is my attempt to read the file and verify it in C#:
const string publicKey = #"<RSAKeyValue><Modulus>3C5QWo4H.....";
TextReader reader = new StreamReader(path, Encoding.ASCII);
var unsignedString = reader.ReadLine();
var signedString = reader.ReadLine();
reader.Close();
if (VerifySignedHash(unsignedString,signedString, publicKey)) {
//some code
}
private bool VerifySignedHash(string stringToVerify, string signedString, string publicKey)
{
var byteConverter = new ASCIIEncoding();
var dataToVerify = Convert.FromBase64String(stringToVerify);
var signedData = Convert.FromBase64String(signedString);
try
{
// Create a new instance of RSACryptoServiceProvider using the
// key from RSAParameters.
var rsaAlg = new RSACryptoServiceProvider();
rsaAlg.FromXmlString(publicKey);
// Verify the data using the signature. Pass a new instance of SHA1CryptoServiceProvider
// to specify the use of SHA1 for hashing.
return rsaAlg.VerifyData(dataToVerify, new SHA1CryptoServiceProvider(), signedData);
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return false;
}
}
verfication fails...
In your "signing" code, you base64encode the original string, then write that string to the output file. However, on the C# side, you read that value into unsignedString, but never reverse the base64 encoding.
The end result is that you're trying to verify the bytes of the base64Encoded string, not the data itself, so the VerifyData step fails.
Think that's your problem.
Modifying the following line might solve the problem:
var dataToVerify = Convert.FromBase64String(stringToVerify);
I am having trouble matching a C# hashing algorithm with node, the problem seems to be the Unicode encoding. Is there a way to convert a string to Unicode then hash it and output it as hexadecimal? Unfortunately I cannot change the c# code, it is out of my control.
node algorithm
function createMd5(message) {
var crypto = require("crypto");
var md5 = crypto.createHash("md5");
return md5.update(message).digest('hex');
}
c# hashing algorithm
private static string GetMD5(string text)
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(text);
using (MD5 hasher = new MD5CryptoServiceProvider())
{
string hex = "";
hashValue = hasher.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex.ToLower();
}
}
Your suspicion of this being an encoding problem is correct. You can fix your node code with the following alteration, which will convert your message string into utf-16 (which is what .NET's default encoding is):
function createMd5(message) {
var crypto = require("crypto");
var md5 = crypto.createHash("md5");
return md5.update(new Buffer(message, 'ucs-2')).digest('hex');
}