I need to hash a string according to the sha1 algorithm, with the user's secret key, something like: hash = hash (string, secret_key).
I use this code:
byte[] data = sha1.ComputeHash(Encoding.UTF8.GetBytes(input));
But I can not find how to use the secret key in this algorithm.
The hash is the result of applying a hash function to data. It maps arbitrary data to fixed size value. This does not require any kind of secret.
The cryptographic process which uses hashes and secret keys is signing data. You may want to create a hash of your data and a MAC (message authentication code) based on public/private key cryptography using the private key.
A hash tells you only that data was modified or not.
A MAC uses any kind of secret, thus it protects also against manipulation. Generally, MACs only work when the sender and receiver know each other.
You could do something like:
public string SignData(string message, string secret)
{
var encoding = new System.Text.UTF8Encoding();
var keyBytes = encoding.GetBytes(secret);
var messageBytes = encoding.GetBytes(message);
using (var hmacsha1 = new HMACSHA1(keyBytes))
{
var hashMessage = hmacsha1.ComputeHash(messageBytes);
return Convert.ToBase64String(hashMessage);
}
}
Related
I am creating an open Source Project which uses the youtube pubsub api to get notifications on video uploads. I want to verify that the request does come from youtube and not from a 3rd Party by checking the HMAC SHA1 Signature as described.
So, every time 1 Run my Program, I will generate a Secret later, to debug the problem, i use "test" as my secret string.
I use the following method to check if the provided signature is valid
public static bool Check(string body, string signature)
{
using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(Secret)))
{
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var hash = Encoding.UTF8.GetString(hashBytes);
Console.WriteLine("Computed Hash " + hash);
return signature.Equals(hash);
}
}
Where body is the request body and signature is a value provided in the request header.
According to https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.7
If the subscriber supplied a value for hub.secret in their subscription request, the hub MUST generate an HMAC signature of the payload and include that signature in the request headers of the content distribution request. The X-Hub-Signature header's value MUST be in the form sha1=signature where signature is a 40-byte, hexadecimal representation of a SHA1 signature [RFC3174]. The signature MUST be computed using the HMAC algorithm [RFC2104] with the request body as the data and the hub.secret as the key.
I supply my Secret as hub.secret in my subscription request.
So if I understand it correctly, the hub SHOULD use that secret to generate a HMACSHA1 of the payload -> the body.
I want to regenerate the HMAC and should get the same value, right?
It does not work. Also the computed hash value logged by console.WriteLine is something completely different, not alphabetic characters at all, so I guess it might be a problem with the encoding, but I can't figure it out. Thanks for all the help!
The documentation says "where signature is a 40-byte, hexadecimal representation" so instead of converting hashBytes to an UTF8 string you should convert it to a hexadecimal string.
public static bool Check(string body, string signature)
{
using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(Secret)))
{
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var hash = Convert.ToHexString(hashBytes).ToLowerInvariant();
Console.WriteLine("Computed Hash " + hash);
return signature.Equals(hash);
}
}
I am attempting to use this process to hash data in custom policy:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/general-transformations#hash
I need to hash data the same way in c# that will be sent as claims to the policy, and then will use custom policy to hash the same data, and then will compare in custom policy that the hashes match. However, when I hash the data using c# using the following code snipet and then try to hash the data in the policy, the hashes are not matching. Im looking for the method I can use in C# that the custom policy is using to hash the data?
var saltedClaim = string.Concat(text, salt);
byte[] saltedClaimAsBytes = Encoding.UTF8.GetBytes(saltedClaim);
byte[] keyAsBytes = Encoding.UTF8.GetBytes(key);
using (HMACSHA256 hmac = new HMACSHA256(keyAsBytes))
{
// Compute the hash
byte[] hashValue = hmac.ComputeHash(saltedClaimAsBytes);
return Convert.ToBase64String(hashValue);
}
Ran into the same issue and answer doesn't quite give you all the answers:
$saltedClaim = "<AzureSecret/key><plaintext><salt>"
$saltedClaimAsBytes = [System.Text.Encoding]::UTF8.GetBytes($saltedClaim);
$hasher = [System.Security.Cryptography.HashAlgorithm]::Create('sha256')
$hashValue = $hasher.ComputeHash($saltedClaimAsBytes);
[Convert]::ToBase64String($hashValue);
Please use SHA256 instead of HMACSHA256 and it will solve your issue.
Reference:
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha256?view=net-5.0
Algorithms are different in SHA256 Vs HMACSHA256.
Microsoft documentation lacks some essential information for this claim transformation. Doing a SHA456 of the concatenation of "Secret key", "plain text" and "salt" in that order before converting to a base64 worked for me. Here is a working C# example:
using System.Security.Cryptography;
string textToBeHashed = "myText";
string salt = "mySalt";
string secretKey = "mySecret";
string assembledClaim = string.Concat(secretKey, textToBeHashed, salt);
using (SHA256 sha256Hash = SHA256.Create())
{
byte[] bytes = sha256Hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(assembledClaim));
string hashResult = System.Convert.ToBase64String(bytes);
}
I need to validate a user by using an external authorize API. This API requires me to send a SHA-256 hash of something unique on my client side. I should use this to validate the signature later on.
I'm generating the Hash like this. As text I'm sending a unique Session ID:
private static string CalculateSHA256Hash(string text)
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(text);
SHA256Managed hashString = new SHA256Managed();
string hex = "";
hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
From the API I received a public signing key as a string:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA185vTa2xheB
<..>
SLhDuNk0SkiL7f8KbOG3tP9IDmAcAfoZWw/o2XjUxVLp1uw9V/hj4oJoapyS2sW4
cwIDAQAB
-----END PUBLIC KEY-----
I should use this key somehow to validate the response. The response I receive is this 512 character long string:
"57ec50faf72340f9618cf690160ab6b251a2d76fcce61583779804a07f8ac1f520bf09a4f5918a3
<...>
245f8d00b2cce3fa1ecc849f8771be0c8"
Now I should be able to Validate the signature I've received with the Public-key-string and my own hashed value.
How do I do this?
Edit
The hasing & padding scheme used on the API is "SHA256_RSA_PKCS_PSS"
I am building login workflow using Google for user. Once user is authenticated, I call GetAuthResponse to get idToken.
https://developers.google.com/identity/sign-in/web/backend-auth
Now, I need to verify certificate against Google certificate. I am using JwtSecurityToken(C#) for the same.
I am referencing for verification - http://blogs.msdn.com/b/alejacma/archive/2008/06/25/how-to-sign-and-verify-the-signature-with-net-and-a-certificate-c.aspx
Issue is - I always gets false from VerifyHash. As, VerifyHash returns just false without any reason, I am not able to find way to verify whether idToken is
valid or not. My code is given below
String strID = ""; // idToken received from Google AuthResponse
JwtSecurityToken token = new JwtSecurityToken(strID);
byte[] text = GetHash(token.RawData);
SHA256Cng sha1 = new SHA256Cng();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
byte[] signature = Encoding.Unicode.GetBytes(token.RawSignature);
// Modulus and exponent value from https://www.googleapis.com/oauth2/v2/certs - second set of keys
String modulus = "uHzGq7cMlx21nydbz9VsW1PItetb9mqvnpLp_8E3Knyk-mjv9DlaPhKGHYlJfHYGzKa2190C5vfsLLb1MIeGfdAv7ftpFsanIWawl8Zo0g-l0m7T2yG_7XerqcVK91lFifeJtgxKI86cPdZkgRy6DaYxMuAwAlhvpi3_UhPvsIwi7M6mxE8nUNpUWodh_YjJNu3wOxKDwbBZuRV2itjY6Z7RjFgJt1CsKF-QjqSVvWjAl0LaCaeMS_8yae0ln5YNeS8rAb6xkmcOuYeyhYsiBzwLRvgpXzEVLjLr631Z99oUHTpP9vWJDpGhfkrClkbmdtZ-ZCwX-eFW6ndd54BJEQ==";
String exponent = "AQAB";
modulus = modulus.Replace('-', '+').Replace('_', '/'); // Else it gives Base64 error
StringBuilder sb = new StringBuilder();
sb.Append("<RSAKeyValue>");
sb.Append("<Modulus>");
sb.Append(modulus);
sb.Append("</Modulus>");
sb.Append("<Exponent>");
sb.Append(exponent);
sb.Append("</Exponent>");
sb.Append("</RSAKeyValue>");
RSACryptoServiceProvider RSAVerifier = new RSACryptoServiceProvider();
RSAVerifier.FromXmlString(sb.ToString());
// Verify the signature with the hash
return RSAVerifier.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA256"), signature);
You might want to try as done in the Google+ Token Verification project - this fork includes a few minor updates that are still in review.
An alternative approach is to just verify the tokens using Google's token verification endpoints:
curl https://www.googleapis.com/oauth2/v2/tokeninfo?id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjkyNGE0NjA2NDgxM2I5YTA5ZmFjZGJiNzYwZGI5OTMwMWU0ZjBkZjAifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTEwNTcwOTc3MjI2ODMwNTc3MjMwIiwiYXpwIjoiMzY0MzgxNDQxMzEwLXRuOGw2ZnY2OWdnOGY3a3VjanJhYTFyZWpmaXRxbGpuLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXRfaGFzaCI6IlAzLU1HZTdocWZhUkZ5Si1qcWRidHciLCJhdWQiOiIzNjQzODE0NDEzMTAtdG44bDZmdjY5Z2c4ZjdrdWNqcmFhMXJlamZpdHFsam4uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJjX2hhc2giOiJjd3hsdXBUSkc4N2FnbU1pb0tSYUV3IiwiaWF0IjoxNDM0NDcyODc2LCJleHAiOjE0MzQ0NzY0NzZ9.Gz_WljZOV9NphDdClakLstutEKk65PNpEof7mxM2j-AOfVwh-SS0L5uxIaknFOk4-nDGmip42vrPYgNvbQWKZY63XuCs94YQgVVmTNCTJnao1IavtrhYvpDqGuGKdEB3Wemg5sS81pEthdvHwyxfwLPYukIhT8-u4ESfbFacsRtR77QRIOk-iLJAVYWTROJ05Gpa-EkTunEBVmZyYetbMfSoYkbwFKxYOlHLY-ENz_XfHTGhYhb-GyGrrw0r4FyHb81IWJ6Jf-7w6y3RiUJik7kYRkvnFouXUFSm8GBwxsioi9AAkavUWUk27s15Kcv-_hkPXzVrW5SvR1zoTI_IMw
This code fails every time I pass it an APK file that has a certification chain (intermediate and Root CA). If the file is self-signed it works correctly.
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)Cert.PublicKey.Key;
bool verified = csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), vData)
"hash" is the sha1 digest of the signature file (SF) and vData the encrypted hash of a signature (CMSG_ENCRYPTED_DIGEST,), both are byte arrays.
I will answer my own question after finding the answer. The problem resides when specifying the digest method.
A X509 certificate uses two algorithms that may not coincide (SignatureAlgorithm.FriendlyName and PublicKey.Key.SignatureAlgorithm). When verifying the hash you must make sure that it matches the SignatureAlgorithm of the certificate and not the algorithm used in the public key (which was my fault).
If in doubt, check both:
if (cert.SignatureAlgorithm.FriendlyName.ToString().Contains("sha256"))
{
SHA256Managed sha256 = new SHA256Managed();
byte[] hash256 = sha256.ComputeHash(sig);
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key;
verify_all.Add(csp.VerifyHash(hash256, CryptoConfig.MapNameToOID("SHA256"), vData));
}
else if (cert.SignatureAlgorithm.FriendlyName.ToString().Contains("sha1"))
{
SHA1Managed Sha1 = new SHA1Managed();
byte[] hash1 = sha1.ComputeHash(sig);
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key;
verify_all.Add(csp.VerifyHash(hash1, CryptoConfig.MapNameToOID("SHA256"), vData));
}