1

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>
Sherantha
  • 462
  • 3
  • 19

1 Answers1

0

I found my mistake. I wasn't using ip as part of the hash in C#.

Also,

 // Base64-encode the final resulting token.
            var tokenStr = CryptoJS.enc.Utf8.parse([token, tokenId].join(':'));

should be

 // Base64-encode the final resulting token.
            var tokenStr = CryptoJS.enc.Base64.parse([token, tokenId].join(':'));

But this above was only because I was trying different things.