Last active
September 29, 2015 08:12
-
-
Save JohnnyJosep/218d83698f5ed67a228d to your computer and use it in GitHub Desktop.
Hcma-Sha256
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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