-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
126 additions
and
130 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
namespace Nexmo.Api.Request | ||
{ | ||
internal static class ByteArrayToHexHelper | ||
{ | ||
///// There is no built-in byte[] => hex string, so here's an implementation | ||
/// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa/24343727#24343727 | ||
/// We're not going to going with the unchecked version. Seems overkill for now. | ||
internal static readonly uint[] _lookup32 = CreateLookup32(); | ||
|
||
internal static uint[] CreateLookup32() | ||
{ | ||
var result = new uint[256]; | ||
for (var i = 0; i < 256; i++) | ||
{ | ||
var s = i.ToString("X2"); | ||
result[i] = s[0] + ((uint)s[1] << 16); | ||
} | ||
return result; | ||
} | ||
|
||
internal static string ByteArrayToHex(byte[] bytes) | ||
{ | ||
var lookup32 = _lookup32; | ||
var result = new char[bytes.Length * 2]; | ||
for (var i = 0; i < bytes.Length; i++) | ||
{ | ||
var val = lookup32[bytes[i]]; | ||
result[2 * i] = (char)val; | ||
result[2 * i + 1] = (char)(val >> 16); | ||
} | ||
return new string(result); | ||
} | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Security.Cryptography; | ||
using System.Text; | ||
using System.Net; | ||
using Microsoft.Extensions.Primitives; | ||
|
||
namespace Nexmo.Api.Request | ||
{ | ||
public static class SignatureHelper | ||
{ | ||
private static bool SlowEquals(byte[] a, byte[] b) | ||
{ | ||
var diff = (uint)a.Length ^ (uint)b.Length; | ||
for (var i = 0; i < a.Length && i < b.Length; i++) | ||
diff |= (uint)(a[i] ^ b[i]); | ||
return diff == 0; | ||
} | ||
|
||
public static bool IsSignatureValid(IEnumerable<KeyValuePair<string, StringValues>> querystring) | ||
{ | ||
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) + 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)); | ||
} | ||
} | ||
} |
This file contains 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