Skip to content

Instantly share code, notes, and snippets.

@JohnnyJosep
Last active September 29, 2015 08:12
Show Gist options
  • Select an option

  • Save JohnnyJosep/218d83698f5ed67a228d to your computer and use it in GitHub Desktop.

Select an option

Save JohnnyJosep/218d83698f5ed67a228d to your computer and use it in GitHub Desktop.
Hcma-Sha256
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using System.Threading;
using System.Web.Http;
using System.Net.Http;
using System.Net;
using System.Net.Http.Headers;
using System.Web;
using System.Security.Cryptography;
using System.Web.Http.Results;
using System.Security.Principal;
namespace GamesFlow_Api.Filters
{
/// <summary>
///
/// http://bitoftech.net/2014/12/15/secure-asp-net-web-api-using-api-key-authentication-hmac-authentication/
/// </summary>
public class HMACAuthenticationAttribute : Attribute, IAuthenticationFilter
{
private static readonly string TableName = "KeysTable";
private readonly UInt64 requestMaxAgeInSeconds = 300; // 5 mins
private readonly string authenticationScheme = "amx";
private CloudTable keysTable;
public bool AllowMultiple { get { return false; } }
public HMACAuthenticationAttribute()
{
var storageAccount = CloudStorageAccount.Parse(
ConfigurationManager.AppSettings["StorageConnectionString"]);
var tableClient = storageAccount.CreateCloudTableClient();
keysTable = tableClient.GetTableReference(TableName);
keysTable.CreateIfNotExists();
}
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var request = context.Request;
if (request.Headers.Authorization != null &&
authenticationScheme.Equals(request.Headers.Authorization.Scheme, StringComparison.OrdinalIgnoreCase))
{
var rawAuthHeader = request.Headers.Authorization.Parameter;
var authHeaderArray = GetAutherizationHeaderValues(rawAuthHeader);
if (authHeaderArray != null)
{
var APPId = authHeaderArray[0];
var incomingBase64Signature = authHeaderArray[1];
var nonce = authHeaderArray[2];
var requestTimeStamp = authHeaderArray[3];
var isValid = isValidReq(request, APPId, incomingBase64Signature, nonce, requestTimeStamp);
if (isValid.Result)
{
var currentPrincipal = new GenericPrincipal(new GenericIdentity(APPId), null);
context.Principal = currentPrincipal;
}
else
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}
}
else
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}
}
else
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}
return Task.FromResult(0);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
context.Result = new ResultWithChallenge(context.Result);
return Task.FromResult(0);
}
private string[] GetAutherizationHeaderValues(string rawAuthHeader)
{
var credArray = rawAuthHeader.Split(':');
if (credArray.Length == 4)
{
return credArray;
}
return null;
}
private async Task<bool> isValidReq(HttpRequestMessage request, string id, string inSignature, string nonce, string timeStamp)
{
if (isReplayRequest(nonce, timeStamp)) return false;
var requestContent = "";
var requestUri = HttpUtility.UrlEncode(request.RequestUri.AbsolutePath.ToLower());
var requestMethod = request.Method.Method;
var query = new TableQuery().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, id));
var entities = keysTable.ExecuteQuery(query);
if (entities.Count() != 1)
{
return false;
}
string key = entities.ElementAt(0).RowKey;
var data = string.Format("{0}{1}{2}{3}{4}{5}", id, requestMethod, requestUri, timeStamp, nonce, requestContent);
var keyBytes = Convert.FromBase64String(key);
byte[] sig = Encoding.UTF8.GetBytes(data);
using (HMACSHA256 hmac = new HMACSHA256(keyBytes))
{
byte[] sigBytes = hmac.ComputeHash(sig);
var signatureBase64 = Convert.ToBase64String(sigBytes);
return inSignature.Equals(signatureBase64, StringComparison.Ordinal);
}
}
private bool isReplayRequest(string nonce, string requestTimeStamp)
{
if (System.Runtime.Caching.MemoryCache.Default.Contains(nonce))
{
return true;
}
DateTime epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
TimeSpan currentTs = DateTime.UtcNow - epochStart;
var serverTotalSeconds = Convert.ToUInt64(currentTs.TotalSeconds);
var requestTotalSeconds = Convert.ToUInt64(requestTimeStamp);
if ((serverTotalSeconds - requestTotalSeconds) > requestMaxAgeInSeconds)
{
return true;
}
System.Runtime.Caching.MemoryCache.Default.Add(nonce, requestTimeStamp, DateTimeOffset.UtcNow.AddSeconds(requestMaxAgeInSeconds));
return false;
}
private static async Task<byte[]> ComputeHash(HttpContent httpContent)
{
using (MD5 md5 = MD5.Create())
{
byte[] hash = null;
var content = await httpContent.ReadAsByteArrayAsync();
if (content.Length != 0)
{
hash = md5.ComputeHash(content);
}
return hash;
}
}
}
public class ResultWithChallenge : IHttpActionResult
{
private readonly string authenticationScheme = "amx";
private readonly IHttpActionResult result;
public ResultWithChallenge(IHttpActionResult result)
{
this.result = result;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = await result.ExecuteAsync(cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(authenticationScheme));
}
return response;
}
}
}
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
namespace AppTest.ApiClient
{
public class HttpClientHcmaSha256Handler : HttpClientHandler
{
private readonly string _key, _id;
public HttpClientHcmaSha256Handler(string id, string key)
{
_key = key;
_id = id;
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var requestContentBase64String = "";
var requestUri = WebUtility.UrlEncode(request.RequestUri.AbsolutePath).ToLower();
var requestMethod = request.Method.Method;
var epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
var timeSpan = DateTime.UtcNow - epochStart;
var requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();
string nonce = Guid.NewGuid().ToString("N");
if (request.Content != null)
{
var content = await request.Content.ReadAsStringAsync();
var hash = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
var buff = CryptographicBuffer.ConvertStringToBinary(content, BinaryStringEncoding.Utf8);
var hashed = hash.HashData(buff);
requestContentBase64String = CryptographicBuffer.EncodeToHexString(hashed);
}
var data = string.Format("{0}{1}{2}{3}{4}{5}",
_id, requestMethod, requestUri, requestTimeStamp, nonce, requestContentBase64String);
System.Diagnostics.Debug.WriteLine("Data: " + data);
var alg = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
var dataBuf = CryptographicBuffer.ConvertStringToBinary(data, BinaryStringEncoding.Utf8);
var keyBuf = CryptographicBuffer.DecodeFromBase64String(_key);
var signatureKey = alg.CreateKey(keyBuf);
var signedBuf = CryptographicEngine.Sign(signatureKey, dataBuf);
var signedStg = CryptographicBuffer.EncodeToBase64String(signedBuf);
request.Headers.Authorization = new AuthenticationHeaderValue(
"amx", string.Format("{0}:{1}:{2}:{3}", _id, signedStg, nonce, requestTimeStamp));
return await base.SendAsync(request, cancellationToken);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment