Im trying to integrate using SOAP to a web service but I'm getting stucked in the authorization part, and more specifically when creating sha1 encrypted password.
Documentation for this specific authorization can be found at https://www.beautyfort.com/api/docs/AuthHeader-t1
I have been searching the net trying to find different ways to create the password but none seems to generate the same password as the example in the documentation page.
string testDateString = "2015-07-08T11:31:53+01:00";
string testNonce = "186269";
string testSecret = "Ok4IWYLBHbKn8juM1gFPvQxadieZmS2";
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(testNonce + testDateString + testSecret));
string Sha1Password = Convert.ToBase64String(hashedDataBytes);
The input variables is from documentation page. According to the documentation the password should get the value "ZDg3MTZiZTgwYTMwYWY4Nzc4OGFjMmZhYjA5YzM3MTdlYmQ1M2ZkMw=="
but the code I am using is generating "2HFr6Aowr4d4isL6sJw3F+vVP9M=".
Anyone have a bright idea of what I am doing wrong?
I took a wild guess that maybe the output of "sha1()" in the documention psuedo-code was a hex-string (like sha1() in PHP, etc), and that seems to output the expected password.
Updated code:
string testDateString = "2015-07-08T11:31:53+01:00";
string testNonce = "186269";
string testSecret = "Ok4IWYLBHbKn8juM1gFPvQxadieZmS2";
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(testNonce + testDateString + testSecret));
var hexString = BitConverter.ToString(hashedDataBytes).Replace("-", string.Empty).ToLower();
string Sha1Password = Convert.ToBase64String(Encoding.UTF8.GetBytes(hexString));
Related
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'm trying to figure out how to pass a parameter in my .NET application. The URL request looks like:
http://webservices.amazon.com/onca/xml?
Service=AWSECommerceService
&Operation=ItemLookup
&ResponseGroup=Large
&SearchIndex=All
&IdType=UPC
&ItemId=635753490879
&AWSAccessKeyId=[Your_AWSAccessKeyID]
&AssociateTag=[Your_AssociateTag]
&Timestamp=[YYYY-MM-DDThh:mm:ssZ]
&Signature=[Request_Signature]
The part that I'm confused about are these:
&Timestamp=[YYYY-MM-DDThh:mm:ssZ]
&Signature=[Request_Signature]
I'm not sure whether I can Just simply do it something like this for timestamp part:
var TimeStamp = DateTime.Now; // without any special datetime formating?
So my question is how do I actually generate this signature URL in the request URL ?
I have all of these parameters above but I'm not sure how to generate this last one ?
Can someone help me out ?
AWS utilizes HMAC request-signing. Generally speaking, the way this works is that you create a "message", which is composed of things like your access key(s), request headers, request body and a timestamp. You then HMAC this "message" and that becomes your "signature" for the request. This prevents replay-attacks as each request must have a unique signature.
It looks like the timestamp simply needs to be in ISO format (YYYY-MM-DDThh:mm:ssZ), so, no you can't just use DateTime.Now. The default format utilized by ToString will not be ISO. Instead, you'd need to use something like:
DateTime.Now.ToString("yyyy-MM-ddThh:mm:sszzz");
Or it would actually probably be better to use UTC time and simply append a Z:
DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ssZ");
As for creating the signature, see the AWS documentation, where they provide some sample code:
static byte[] HmacSHA256(String data, byte[] key)
{
String algorithm = "HmacSHA256";
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
}
static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName)
{
byte[] kSecret = Encoding.UTF8.GetBytes(("AWS4" + key).ToCharArray());
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
/*
DOCUMENTATION: https://docs.aws.amazon.com/AWSECommerceService/latest/DG/rest-signature.html#rest_detailedexample
*/
var itemID = "0679722769";
var accessKeyID = "AKIAIOSFODNN7EXAMPLE";
var timeStamp = DateTime.UtcNow.ToString("o");
var req = $"Service=AWSECommerceService&AWSAccessKeyId={accessKeyID}&Operation=ItemLookup&IdType=UPC&ItemId={itemID}&Version=2013-08-01&Timestamp={timeStamp}";
req = req.Replace(":", "%3A").Replace(",", "%2C"); //UrlDecode certain characters
var reqlist = req.Split('&').ToArray(); //we need to sort our key/value pairs
Array.Sort(reqlist);
req = String.Join("&", reqlist); //join everything back
var reqToSign = $#"GET
webservices.amazon.com
/onca/xml
{req}".Replace("\r", ""); //create the request for signing. We need to replace microsofts's crlf with just a lf; Make sure there are no leading spaces after the linefeeds.
var signage = getSignatureKey("1234567890",reqToSign);
req = $"http://webservices.amazon.com/onca/xml?{req}&Signature={signage}"; //create our request with the signature appended.
return req;
}
private static byte[] HmacSHA256(String data, byte[] key)
{
String algorithm = "HmacSHA256";
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
}
private static string getSignatureKey(string key, string stringToSign)
{
byte[] kSecret = Encoding.UTF8.GetBytes(key.ToCharArray());
byte[] kSigning = HmacSHA256(stringToSign, kSecret);
return WebUtility.UrlEncode(Convert.ToBase64String(kSigning));
}
Contrary to most of the answers found here and elsewhere, this is the only way that works. The entire request has to be hashed, not just particular parameters. I can't speak to other Amazon services, but the Commerce Service has to be done like this.
Quite a few answers here and elsewhere referenced this: https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
This is most certainly not correct. If you're not passing a region parameter, how can Amazon create the same signature since it doesn't have all the information.
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
For various reasons I need to be able to make Chef API requests in C#. I've followed the guides here (Header specification) and here (Bash example) but have reached a dead end. Every request I send comes back 401 Unauthorized with the content
{"error":["Invalid signature for user or client 'myuser'"]}
I have also configured Knife locally to use Fiddler as a HTTP proxy so I can inspect Knife HTTP requests and have copied the visible headers as accurately as possible, but naturally I cannot see the canonical header it generated nor the one the server expects.
However, I have confirmed in this way that the hashes I'm generating for the content (empty) and path are the same as Knife is generating.
Here's my code. Loading the RSA private key from PEM format is using an extension method taken from Christian Etter's blog - run out of links sorry.
const string path = "/cookbooks"
const string basePath = "https://chefserver.internal:443";
var timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
var method = "GET";
var clientName = "myuser";
var hashedPath = ToBase64EncodedSha1String(path);
var hashedBody = ToBase64EncodedSha1String(String.Empty);
var canonicalHeader = String.Format("Method:{0}\nHashed Path:{1}\nX-Ops-Content-Hash:{2}\nX-Ops-Timestamp:{3}\nX-Ops-UserId:{4}",
method, hashedPath, hashedBody, timestamp, clientName);
var privateKey = File.ReadAllText("C:\\chef\\myuser.private.pem");
string signature;
byte[] rawData;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.PersistKeyInCsp = false;
rsa.LoadPrivateKeyPEM(privateKey);
using (var sha1 = new SHA1CryptoServiceProvider())
{
rawData = rsa.SignData(Encoding.UTF8.GetBytes(canonicalHeader), sha1);
signature = Convert.ToBase64String(rawData);
}
}
var client = new HttpClient();
var message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.RequestUri = new Uri(basePath + path);
message.Headers.Add("Accept", "application/json");
message.Headers.Add("Host", "chefserver.internal:443");
message.Headers.Add("X-Chef-Version", "11.12.4");
message.Headers.Add("X-Ops-Timestamp", timestamp);
message.Headers.Add("X-Ops-Sign", "version=1.0;");
message.Headers.Add("X-Ops-Userid", clientName);
message.Headers.Add("X-Ops-Content-Hash", hashedBody);
message.Headers.Add("User-Agent", "Chef Knife/11.4.0 (ruby-1.9.2-p320; ohai-6.16.0; x86_64-darwin11.3.0; +http://opscode.com)");
var currentItem = new StringBuilder();
var i = 1;
foreach (var l in signature)
{
currentItem.Append(l);
if (currentItem.Length == 60)
{
message.Headers.Add("X-Ops-Authorization-" + i, currentItem.ToString());
i++;
currentItem = new StringBuilder();
}
}
message.Headers.Add("X-Ops-Authorization-" + i, currentItem.ToString());
var response = await client.SendAsync(message);
var content = await response.Content.ReadAsStringAsync();
And the helper
private string ToBase64EncodedSha1String(string input)
{
return
Convert.ToBase64String(new SHA1CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(input)));
}
I had a similar problem and found that the .NET crypto libraries did not support what was needed to make this work. I ended up using the BouncyCastle crypto libraries to accomplish this.
I made a simple C# class library that wraps this up. Take a look at that here: https://github.com/mattberther/dotnet-chef-api
I'm not super familiar with C# crypto, but the docs on SHA1CryptoServiceProvider seem to show that is is signing a SHA1 hash. Chef doesn't use signed hashes, you actually need to do an RSA signature on canonicalHeader itself.
SignData(Byte[], Object): Computes the hash value of the specified byte array using the specified hash algorithm, and signs the resulting hash value.
Is there a null or pass-through hash you can use?
I'm trying to create a client for the new tent.io protocol that's being developed and they are using the HTTP MAC Oauth2 scheme described by https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-http-mac-01.
I've written a simple method in C# that creates the Authorization header, but when I submit my request I get a simple "Invalid MAC signature" error.
Since I don't have a reference implementation, I'm struggling to figure out what's wrong with my code. I'm posting it here in the hope that somebody can spot my mistake.
public string GetAuthorizationHeader(string macKeyIdentifier, string macKey, string macAlgorithm, string method, Uri uri)
{
TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
string timestamp = ((int)t.TotalSeconds).ToString();
string nonce = new Random().Next().ToString();
string normalizedString = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n\n",
timestamp,
nonce,
method,
uri.PathAndQuery,
uri.Host,
uri.Port);
HashAlgorithm hashGenerator = null;
if (macAlgorithm == "hmac-sha-256")
{
hashGenerator = new HMACSHA256(Encoding.ASCII.GetBytes(macKey));
}
else if (macAlgorithm == "hmac-sha-1")
{
hashGenerator = new HMACSHA1(Encoding.ASCII.GetBytes(macKey));
}
else
{
throw new InvalidOperationException("Unsupported MAC algorithm");
}
string hash = System.Convert.ToBase64String(hashGenerator.ComputeHash(Encoding.ASCII.GetBytes(normalizedString)));
StringBuilder authorizationHeader = new StringBuilder();
authorizationHeader.AppendFormat(#"id=""{0}"",ts=""{1}"",nonce=""{2}"",mac=""{3}""",
macKeyIdentifier, timestamp, nonce, hash);
return authorizationHeader.ToString();
}
I create the full header using the returned value and it looks something lke this
Authorization: MAC id="a:dfsdfa2",ts="1349277638",nonce="1469030797",mac="ibZ/HXaoz2VgBer3CK7K9vu0po3K+E36K+TQ9Sgcw6o="
I'm sure I'm missing something small, but I cannot see it.
Any help would be very much appreciated!
It turns out the code above is perfect, but I was passing the wrong HTTP method value into it!
Where I was getting the error, I was POST'ing JSON, but I had actually put "GET" into the GetAuthorizationMethod!
Once I'd corrected that, I got an access_token value from Tent.is.
Nicely executed tool at http://buchananweb.co.uk/security01.aspx showing HMAC using MD5 and SHA1, SHA256, SHA384, SHA512