using Ninject; using Ninject.Modules; using Ninject.Parameters; using PartSource.Entities.Models; using PartSource.Services; using System; using System.Configuration; using System.Net; using System.Net.Http; using System.Security.Cryptography; using System.Security.Principal; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Http; using System.Web.Http.Filters; namespace PartSource.Filters { public class HmacAuthenticationAttribute : Attribute, IAuthenticationFilter, IFilter { private readonly SecurityService _securityService; public bool AllowMultiple { get { return false; } } public HmacAuthenticationAttribute() { _securityService = new StandardKernel().Get(); } public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) { if (context.Request.Headers.Date.HasValue) { DateTimeOffset? date = context.Request.Headers.Date; DateTimeOffset timestamp = date.Value; if (ValidateTimestamp(timestamp)) { if (context.Request.Headers.Authorization == null) { context.ErrorResult = new HmacErrorResult(HttpStatusCode.Unauthorized); return; } string[] authParams = context.Request.Headers.Authorization.Parameter.Split(':'); ApiClient client = await _securityService.GetApiClientByKeyAsync(authParams[0]); if (client == null || !client.Active) { context.ErrorResult = (IHttpActionResult)new HmacErrorResult(HttpStatusCode.Unauthorized); return; } byte[] secret = Encoding.UTF8.GetBytes(client.Secret); StringBuilder builder = new StringBuilder(); builder.Append((object)secret); builder.Append((object)context.Request.Method); StringBuilder stringBuilder = builder; date = context.Request.Headers.Date; string str = date.Value.ToString("r"); stringBuilder.Append(str); if (context.Request.Method != HttpMethod.Get) { using (MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider()) builder.Append((object)md5Provider.ComputeHash(Encoding.UTF8.GetBytes(await context.Request.Content.ReadAsStringAsync()))); } byte[] numArray; using (HMACSHA1 hmacshA1 = new HMACSHA1(secret)) { numArray = Encoding.UTF8.GetBytes(builder.ToString()); numArray = hmacshA1.ComputeHash(numArray); } if (Convert.ToBase64String(numArray) == authParams[1]) { string[] roles = new string[1] { "ValidUser" }; context.Principal = (IPrincipal)new GenericPrincipal((IIdentity)new GenericIdentity(client.AppName), roles); return; } context.ErrorResult = (IHttpActionResult)new HmacErrorResult(HttpStatusCode.Unauthorized); return; } } context.ErrorResult = (IHttpActionResult)new HmacErrorResult(HttpStatusCode.Unauthorized); } public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken) { return new Task(null); } private bool ValidateTimestamp(DateTimeOffset timestamp) { if (int.TryParse(ConfigurationManager.AppSettings["ClockDriftThresholdMinutes"], out int result) && DateTimeOffset.UtcNow.AddMinutes((double)(result * -1)) < timestamp) { return timestamp < DateTimeOffset.UtcNow.AddMinutes((double)result); } return false; } } }