SecurityManager.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Security.Cryptography;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace AipGateway.DRM
  8. {
  9. /// <summary>
  10. /// Token-based authentication for ASP .NET MVC REST web services.
  11. /// Copyright (c) 2015 Kory Becker
  12. /// http://primaryobjects.com/kory-becker
  13. /// License MIT
  14. /// </summary>
  15. public static class SecurityManager
  16. {
  17. private const string _alg = "HmacSHA256";
  18. private const string _salt = "rz8LuOtFBXphj9WQfvFh";
  19. private static int _expirationMinutes = 10;
  20. /// <summary>
  21. /// Generates a token to be used in API calls.
  22. /// The token is generated by hashing a message with a key, using HMAC SHA256.
  23. /// The message is: username:ip:userAgent:timeStamp
  24. /// The key is: password:ip:salt
  25. /// The resulting token is then concatenated with username:timeStamp and the result base64 encoded.
  26. ///
  27. /// API calls may then be validated by:
  28. /// 1. Base64 decode the string, obtaining the token, username, and timeStamp.
  29. /// 2. Ensure the timestamp is not expired.
  30. /// 2. Lookup the user's password from the db (cached).
  31. /// 3. Hash the username:ip:userAgent:timeStamp with the key of password:salt to compute a token.
  32. /// 4. Compare the computed token with the one supplied and ensure they match.
  33. /// </summary>
  34. public static string GenerateToken(string username, string password, string ip, string userAgent, long ticks)
  35. {
  36. string hash = string.Join(":", new string[] { username, ip, userAgent, ticks.ToString() });
  37. string hashLeft = "";
  38. string hashRight = "";
  39. using (HMAC hmac = HMACSHA256.Create(_alg))
  40. {
  41. hmac.Key = Encoding.UTF8.GetBytes(GetHashedPassword(password));
  42. hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
  43. hashLeft = Convert.ToBase64String(hmac.Hash);
  44. hashRight = string.Join(":", new string[] { username, ticks.ToString() });
  45. }
  46. return Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(":", hashLeft, hashRight)));
  47. }
  48. /// <summary>
  49. /// Returns a hashed password + salt, to be used in generating a token.
  50. /// </summary>
  51. /// <param name="password">string - user's password</param>
  52. /// <returns>string - hashed password</returns>
  53. public static string GetHashedPassword(string password)
  54. {
  55. string key = string.Join(":", new string[] { password, _salt });
  56. using (HMAC hmac = HMACSHA256.Create(_alg))
  57. {
  58. // Hash the key.
  59. hmac.Key = Encoding.UTF8.GetBytes(_salt);
  60. hmac.ComputeHash(Encoding.UTF8.GetBytes(key));
  61. return Convert.ToBase64String(hmac.Hash);
  62. }
  63. }
  64. /// <summary>
  65. /// Checks if a token is valid.
  66. /// </summary>
  67. /// <param name="token">string - generated either by GenerateToken() or via client with cryptojs etc.</param>
  68. /// <param name="ip">string - IP address of client, passed in by RESTAuthenticate attribute on controller.</param>
  69. /// <param name="userAgent">string - user-agent of client, passed in by RESTAuthenticate attribute on controller.</param>
  70. /// <returns>bool</returns>
  71. public static bool IsTokenValid(string token, string ip, string userAgent)
  72. {
  73. bool result = false;
  74. try
  75. {
  76. // Base64 decode the string, obtaining the token:username:timeStamp.
  77. string key = Encoding.UTF8.GetString(Convert.FromBase64String(token));
  78. // Split the parts.
  79. string[] parts = key.Split(new char[] { ':' });
  80. if (parts.Length == 3)
  81. {
  82. // Get the hash message, username, and timestamp.
  83. string hash = parts[0];
  84. string username = parts[1];
  85. long ticks = long.Parse(parts[2]);
  86. DateTime timeStamp = new DateTime(ticks);
  87. // Ensure the timestamp is valid.
  88. bool expired = Math.Abs((DateTime.UtcNow - timeStamp).TotalMinutes) > _expirationMinutes;
  89. if (!expired)
  90. {
  91. //
  92. // Lookup the user's account from the db.
  93. //
  94. if (username == "john")
  95. {
  96. string password = "password";
  97. // Hash the message with the key to generate a token.
  98. string computedToken = GenerateToken(username, password, ip, userAgent, ticks);
  99. // Compare the computed token with the one supplied and ensure they match.
  100. result = (token == computedToken);
  101. }
  102. }
  103. }
  104. }
  105. catch
  106. {
  107. }
  108. return result;
  109. }
  110. }
  111. }