public string Compose(EscherConfig config, string key, DateTime dateTime, string[] headersToSign, string stringToSign, string secret) { Array.Sort(headersToSign, StringComparer.Ordinal); return(String.Join(" ", new[] { config.AlgorithmPrefix + "-HMAC-" + config.HashAlgorithm.ToUpper(), string.Format("Credential={0}/{1}/{2},", key, dateTime.ToEscherShortDate(), config.CredentialScope), string.Format("SignedHeaders={0},", String.Join(";", headersToSign.Select(h => h.ToLower()))), "Signature=" + SignatureCalculator.Sign(stringToSign, secret, dateTime, config) })); }
public string Authenticate(IEscherRequest request, IKeyDb keyDb, DateTime currentTime) { currentTime = currentTime.ToUniversalTime(); var authHeader = request.Headers.FirstOrDefault(header => header.Name == Config.AuthHeaderName); if (authHeader == null) { throw new EscherAuthenticationException("The authorization header is missing"); } var dateHeader = request.Headers.FirstOrDefault(header => header.Name == Config.DateHeaderName); if (dateHeader == null) { throw new EscherAuthenticationException("The date header is missing"); } var hostHeader = request.Headers.FirstOrDefault(header => header.Name.ToLower() == "host"); if (hostHeader == null) { throw new EscherAuthenticationException("The host header is missing"); } var match = Regex.Match(authHeader.Value, "^([^\\-]+)-HMAC-(SHA[\\d]+) Credential=([^/]+)/([\\d]{8})/([^,]+), SignedHeaders=([^,]+), Signature=([a-z0-9]+)$"); if (!match.Success) { throw new EscherAuthenticationException("Could not parse auth header"); } var algorythmPrefix = match.Groups[1].Value; var hashAlgorythm = match.Groups[2].Value; var apiKey = match.Groups[3].Value; var shortDate = match.Groups[4].Value; var credentialScope = match.Groups[5].Value; var signedHeaders = match.Groups[6].Value.Split(';'); var signature = match.Groups[7].Value; DateTime requestTime; try { requestTime = DateTime.Parse(dateHeader.Value).ToUniversalTime(); } catch (FormatException) { requestTime = DateTimeParser.FromEscherLongDate(dateHeader.Value); } if (requestTime.ToUniversalTime().Date != DateTimeParser.FromEscherShortDate(shortDate).Date) { throw new EscherAuthenticationException("The Authorization header's shortDate does not match with the request date"); } if (Math.Abs((currentTime - requestTime).TotalSeconds) > Config.ClockSkew) { throw new EscherAuthenticationException("The request date is not within the accepted time range"); } if (credentialScope != Config.CredentialScope) { throw new EscherAuthenticationException("The credential scope is invalid"); } if (!signedHeaders.Contains(Config.DateHeaderName.ToLower())) { throw new EscherAuthenticationException("The date header is not signed"); } if (!signedHeaders.Contains("host")) { throw new EscherAuthenticationException("The host header is not signed"); } if (!new[] { "SHA256", "SHA512" }.Contains(hashAlgorythm.ToUpper())) { throw new EscherAuthenticationException("Only SHA256 and SHA512 hash algorithms are allowed"); } var secret = keyDb[apiKey]; if (secret == null) { throw new EscherAuthenticationException("Invalid Escher key"); } var canonicalizedRequest = new RequestCanonicalizer().Canonicalize(request, signedHeaders, Config); var stringToSign = new StringToSignComposer().Compose(canonicalizedRequest, requestTime, Config); var calculatedSignature = SignatureCalculator.Sign(stringToSign, secret, requestTime, Config); if (calculatedSignature != signature) { throw new EscherAuthenticationException("The signatures do not match"); } return(apiKey); }