public static bool IsSignatureValid(IEnumerable <KeyValuePair <string, StringValues> > querystring, string securitySecret = null) { Action <IDictionary <string, string>, StringBuilder> buildStringFromParams = (param, strings) => { foreach (var kvp in param) { strings.AppendFormat("{0}={1}&", WebUtility.UrlEncode(kvp.Key), WebUtility.UrlEncode(kvp.Value)); } }; // Compare the local time with the timestamp var querystringList = querystring as IList <KeyValuePair <string, StringValues> > ?? querystring.ToList(); var localTime = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; var messageTime = int.Parse(querystringList.Single(kvp => kvp.Key == "timestamp").Value); // Message cannot be more than 5 minutes old const int maxDelta = 5 * 60; var difference = Math.Abs(localTime - messageTime); if (difference > maxDelta) { return(false); } var sorted = new SortedDictionary <string, string>(); // Sort the query parameters, removing sig as we go foreach (var kvp in querystringList.Where(kv => kv.Key != "sig")) { sorted.Add(kvp.Key, kvp.Value); } // Create the signing url using the sorted parameters and your SECURITY_SECRET var sb = new StringBuilder(); buildStringFromParams(sorted, sb); var queryToSign = "&" + sb; queryToSign = queryToSign.Remove(queryToSign.Length - 1) + (securitySecret?.ToUpper() ?? Configuration.Instance.Settings["appSettings:Nexmo.security_secret"].ToUpper()); // Generate MD5 var hashgen = MD5.Create(); var hash = hashgen.ComputeHash(Encoding.UTF8.GetBytes(queryToSign)); var generatedSig = ByteArrayToHexHelper.ByteArrayToHex(hash).ToLower(); // A timing attack safe string comparison to validate hash return(SlowEquals(Encoding.UTF8.GetBytes(generatedSig), Encoding.UTF8.GetBytes(querystringList.Single(kvp => kvp.Key == "sig").Value))); }
private static StringBuilder BuildQueryString(IDictionary <string, string> parameters, Credentials creds = null) { var apiKey = (creds?.ApiKey ?? Configuration.Instance.Settings["appSettings:Nexmo.api_key"]).ToUpper(); var apiSecret = creds?.ApiSecret ?? Configuration.Instance.Settings["appSettings:Nexmo.api_secret"]; var securitySecret = creds?.SecuritySecret ?? Configuration.Instance.Settings["appSettings:Nexmo.security_secret"]; var sb = new StringBuilder(); Action <IDictionary <string, string>, StringBuilder> buildStringFromParams = (param, strings) => { foreach (var kvp in param) { strings.AppendFormat("{0}={1}&", WebUtility.UrlEncode(kvp.Key), WebUtility.UrlEncode(kvp.Value)); } }; parameters.Add("api_key", apiKey); if (string.IsNullOrEmpty(securitySecret)) { // security secret not provided, do not sign parameters.Add("api_secret", apiSecret); buildStringFromParams(parameters, sb); return(sb); } // security secret provided, sort and sign request parameters.Add("timestamp", ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds).ToString(CultureInfo.InvariantCulture)); var sortedParams = new SortedDictionary <string, string>(parameters); buildStringFromParams(sortedParams, sb); var queryToSign = "&" + sb; queryToSign = queryToSign.Remove(queryToSign.Length - 1) + securitySecret.ToUpper(); var hashgen = MD5.Create(); var hash = hashgen.ComputeHash(Encoding.UTF8.GetBytes(queryToSign)); sb.AppendFormat("sig={0}", ByteArrayToHexHelper.ByteArrayToHex(hash).ToLower()); return(sb); }