I'm following this site to have a token based authentication from an ionic+angularJs mobile client to C# Web API REST Client.
The JS code to generate token is as following:
generate: function (username, domain, password) {
if (username && domain && password) {
// If the user is providing credentials, then create a new key.
this.logout();
}
//Set the username
SecurityManager.username = SecurityManager.username || username;
//Set the domain
SecurityManager.domain = SecurityManager.domain || domain;
// Set the key to a hash of the user's password + salt.
SecurityManager.key = SecurityManager.key || CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256([password, SecurityManager.salt].join(':'), SecurityManager.salt));
$log.log("security key: " + SecurityManager.key);
// Set the client IP address.
SecurityManager.ip = SecurityManager.ip || this.getIp();
// Persist key pieces.
if (SecurityManager.username) {
localStorage['SecurityManager.username'] = SecurityManager.username;
localStorage['SecurityManager.domain'] = SecurityManager.domain;
localStorage['SecurityManager.key'] = SecurityManager.key;
}
// Get the (C# compatible) ticks to use as a timestamp. http://stackoverflow.com/a/7968483/2596404
var ticks = ((new Date().getTime() * 10000) + 621355968000000000);
// Construct the hash body by concatenating the username, domnain, ip, and userAgent.
var message = [SecurityManager.username, SecurityManager.domain, SecurityManager.ip, navigator.userAgent.replace(/ \.NET.+;/, ''), ticks].join(':');
$log.log("values are:" + message.split(':'));
// Hash the body, using the key.
var hash = CryptoJS.HmacSHA256(message, SecurityManager.key);
$log.log("security key hash: " + hash);
// Base64-encode the hash to get the resulting token.
var token = CryptoJS.enc.Base64.stringify(hash);
$log.log("base 64 encoded hash: " + token);
// Include the username, domain and timestamp on the end of the token, so the server can validate.
var tokenId = [SecurityManager.username, SecurityManager.domain, ticks].join(':');
// Base64-encode the final resulting token.
var tokenStr = CryptoJS.enc.Base64.parse([token, tokenId].join(':'));
var finalToken = CryptoJS.enc.Base64.stringify(tokenStr);
$log.log("Token is " + finalToken);
return finalToken;
}
This gets the output:
1 414448 log security key: LGlemne7vnKZMI35qNw2pgv7YsLerXTaegcycy6x5n0=
2 414562 log values are:joe,cs,127.0.0.1,Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586,635887772145620000
3 414564 log security key hash: d27a0346ea0ad6b50ae158cdc4433a129f957a1c965e126d9b2060f9fb11c0b7
4 414565 log base 64 encoded hash: 0noDRuoK1rUK4VjNxEM6Ep+VehyWXhJtmyBg+fsRwLc=
5 414566 log Token is 0noDRuoK1rUK4VjNxEM6Ep+VehyWXhJtmyBg+fsRwLc=
The C# Code to Generate Token is:
public static string GenerateToken(string username, string domain, string password, string ip, string userAgent, long ticks)
{
string hash = string.Join(":", new string[] { username, domain, userAgent, ticks.ToString() });
string hashLeft = string.Empty;
string hashRight = string.Empty;
using (HMAC hmac = HMACSHA512.Create(_alg))
{
hmac.Key = Encoding.UTF8.GetBytes(GetHashedPassword(password));
**var a = System.Text.Encoding.UTF8.GetString(hmac.Key); //correct**
hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
hashLeft = Convert.ToBase64String(hmac.Hash);
hashRight = string.Join(":", new string[] { username, domain, ticks.ToString() });
}
var c = Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(":", hashLeft, hashRight)));
return c;
}
private static string GetHashedPassword(string password)
{
string key = string.Join(":", new string[] { password, _salt });
using (HMAC hmac = HMACSHA512.Create(_alg))
{
// Hash the key.
hmac.Key = Encoding.UTF8.GetBytes(_salt);
hmac.ComputeHash(Encoding.UTF8.GetBytes(key));
return Convert.ToBase64String(hmac.Hash);
}
}
When I run the test in C# passing in the same long ticks, string username, string password, string domain, string ip and string user agent I'm getting a different token.
[TestMethod]
public void GenerateTokenTest2()
{
string username = "joe";
string domain = "cs";
string password = "password";
string ip = "127.0.0.1";
string userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586";
long ticks = 635887772145620000;
var token = SecurityManager.GenerateToken(username, domain, password, ip, userAgent, ticks);
Assert.IsNotNull(token);
}
The C# and JS security key match. LGlemne7vnKZMI35qNw2pgv7YsLerXTaegcycy6x5n0=
But the final tokens do not.
The post has more explanation on what they're trying to achieve. I added the domain bit to it, not that it works without it.
The salt is also the same:9eiPO7M99Xphj9WQf76t
I've also referenced the following libraries in the web page.
<!--Crypto-JS-->
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha256.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>