Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication with hashing

I need to make a connection to an API using a complicated authentication process that I don't understand. I know it involves multiple steps and I have tried to mimic it, but I find the documentation to be very confusing...

The idea is that I make a request to an endpoint which will return a token to me that I need to use to make a websocket connection.

I did get a code sample which is in Python that I don't know the syntax of, but I can use it as a guide to convert it to C#-syntax.

This is the Python code sample:

import time, base64, hashlib, hmac, urllib.request, json

api_nonce = bytes(str(int(time.time()*1000)), "utf-8")
api_request = urllib.request.Request("https://www.website.com/getToken", b"nonce=%s" % api_nonce)
api_request.add_header("API-Key", "API_PUBLIC_KEY")
api_request.add_header("API-Sign", base64.b64encode(hmac.new(base64.b64decode("API_PRIVATE_KEY"), b"/getToken" + hashlib.sha256(api_nonce + b"nonce=%s" % api_nonce).digest(), hashlib.sha512).digest()))

print(json.loads(urllib.request.urlopen(api_request).read())['result']['token'])

So I have tried to convert this into C# and this is the code I got so far:

    static string apiPublicKey = "API_PUBLIC_KEY";
    static string apiPrivateKey = "API_PRIVATE_KEY";
    static string endPoint = "https://www.website.com/getToken";

    private void authenticate()
    {
        using (var client = new HttpClient())
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;


            // CREATE THE URI
            string uri = "/getToken";


            // CREATE THE NONCE
            /// NONCE = unique identifier which must increase in value with each API call
            /// in this case we will be using the epoch time
            DateTime baseTime = new DateTime(1970, 1, 1, 0, 0, 0);
            TimeSpan epoch = CurrentTime - baseTime;
            Int64 nonce = Convert.ToInt64(epoch.TotalMilliseconds);


            // CREATE THE DATA
            string data = string.Format("nonce={0}", nonce);

            // CALCULATE THE SHA256 OF THE NONCE
            string sha256 = SHA256_Hash(data);


            // DECODE THE PRIVATE KEY
            byte[] apiSecret = Convert.FromBase64String(apiPrivateKey);



            // HERE IS THE HMAC CALCULATION

        }
    }

    public static String SHA256_Hash(string value)
    {
        StringBuilder Sb = new StringBuilder();

        using (var hash = SHA256.Create())
        {
            Encoding enc = Encoding.UTF8;
            Byte[] result = hash.ComputeHash(enc.GetBytes(value));

            foreach (Byte b in result)
                Sb.Append(b.ToString("x2"));
        }

        return Sb.ToString();
    }

So the next part is where I'm really struggling. There needs to be some HMAC-calculation that needs to be done but I'm completely lost there.

like image 825
Cainnech Avatar asked Mar 25 '20 22:03

Cainnech


People also ask

Does hashing provide authenticity?

Hashes are used in myriad cryptographic applications to assure data integrity (i.e., that no changes have been made to a set of data) and authenticity (i.e., that the source of data can be verified).

What hash algorithm is used for password authentication?

Commonly used hashing algorithms include Message Digest (MDx) algorithms, such as MD5, and Secure Hash Algorithms (SHA), such as SHA-1 and the SHA-2 family that includes the widely used SHA-256 algorithm.

What are the three most widely used hashing authentication methods?

The most common hash functions used in digital forensics are Message Digest 5 (MD5), and Secure Hashing Algorithm (SHA) 1 and 2.

How are passwords hashed?

Hashing turns your password (or any other piece of data) into a short string of letters and/or numbers using an encryption algorithm. If a website is hacked, cyber criminals don't get access to your password. Instead, they just get access to the encrypted “hash” created by your password.


Video Answer


1 Answers

The main task here is to reverse the API-Sign SHA-512 HMAC calculation. Use DateTimeOffset.Now.ToUnixTimeMilliseconds to get the API nonce, it will return a Unix timestamp milliseconds value. Then it all boils down concating byte arrays and generating the hashes. I'm using a hardcoded api_nonce time just to demonstrate the result; you'll have to uncomment string ApiNonce = DateTimeOffset.Now.ToUnixTimeMilliseconds to get the current Unix timestamp milliseconds each time the API-Sign key is calculated.

Python API-Sign generation:

import time, base64, hashlib, hmac, urllib.request, json

# Hardcoce API_PRIVATE_KEY base 64 value
API_PRIVATE_KEY = base64.encodebytes(b"some_api_key_1234")

# time_use = time.time()
# Hardcode the time so we can confirm the same result to C#
time_use = 1586096626.919

api_nonce = bytes(str(int(time_use*1000)), "utf-8")

print("API nonce: %s" % api_nonce)

api_request = urllib.request.Request("https://www.website.com/getToken", b"nonce=%s" % api_nonce)
api_request.add_header("API-Key", "API_PUBLIC_KEY_1234")

print("API_PRIVATE_KEY: %s" % API_PRIVATE_KEY)

h256Dig = hashlib.sha256(api_nonce + b"nonce=%s" % api_nonce).digest()

api_sign = base64.b64encode(hmac.new(base64.b64decode(API_PRIVATE_KEY), b"/getToken" + h256Dig, hashlib.sha512).digest())

# api_request.add_header("API-Sign", api_sign)
# print(json.loads(urllib.request.urlopen(api_request).read())['result']['token'])

print("API-Sign: %s" % api_sign)

Will output:

API nonce: b'1586096626919'
API_PRIVATE_KEY: b'c29tZV9hcGlfa2V5XzEyMzQ=\n'
API-Sign: b'wOsXlzd3jOP/+Xa3AJbfg/OM8wLvJgHATtXjycf5EA3tclU36hnKAMMIu0yifznGL7yhBCYEwIiEclzWvOgCgg=='

C# API-Sign generation:

static string apiPublicKey = "API_PUBLIC_KEY";
// Hardcoce API_PRIVATE_KEY base 64 value
static string apiPrivateKey = Base64EncodeString("some_api_key_1234");
static string endPoint = "https://www.website.com/getToken";

public static void Main()
{
    Console.WriteLine("API-Sign: '{0}'", GenApiSign());
}

static private string GenApiSign()
{
    // string ApiNonce = DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString();
    // Hardcode the time so we can confirm the same result with Python
    string ApiNonce = "1586096626919";

    Console.WriteLine("API nonce: {0}", ApiNonce);
    Console.WriteLine("API_PRIVATE_KEY: '{0}'", apiPrivateKey);

    byte[] ApiNonceBytes = Encoding.Default.GetBytes(ApiNonce);

    byte[] h256Dig = GenerateSHA256(CombineBytes(ApiNonceBytes, Encoding.Default.GetBytes("nonce="), ApiNonceBytes));
    byte[] h256Token = CombineBytes(Encoding.Default.GetBytes("/getToken"), h256Dig);

    string ApiSign = Base64Encode(GenerateSHA512(Base64Decode(apiPrivateKey), h256Token));

    return ApiSign;
}

// Helper functions ___________________________________________________

public static byte[] CombineBytes(byte[] first, byte[] second)
{
    byte[] ret = new byte[first.Length + second.Length];
    Buffer.BlockCopy(first, 0, ret, 0, first.Length);
    Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
    return ret;
}

public static byte[] CombineBytes(byte[] first, byte[] second, byte[] third)
{
    byte[] ret = new byte[first.Length + second.Length + third.Length];
    Buffer.BlockCopy(first, 0, ret, 0, first.Length);
    Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
    Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
                     third.Length);
    return ret;
}


public static byte[] GenerateSHA256(byte[] bytes)
{
    SHA256 sha256 = SHA256Managed.Create();
    return sha256.ComputeHash(bytes);
}

public static byte[] GenerateSHA512(byte[] key, byte[] bytes)
{
    var hash = new HMACSHA512(key);
    var result = hash.ComputeHash(bytes);

    hash.Dispose();

    return result;
}

public static string Base64EncodeString(string plainText)
{
    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
    return System.Convert.ToBase64String(plainTextBytes);
}

public static string Base64Encode(byte[] bytes)
{
    return System.Convert.ToBase64String(bytes);
}

public static byte[] Base64Decode(string base64EncodedData)
{
    var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
    return base64EncodedBytes;
}

Will output:

API nonce: 1586096626919
API_PRIVATE_KEY: 'c29tZV9hcGlfa2V5XzEyMzQ='
API-Sign: 'wOsXlzd3jOP/+Xa3AJbfg/OM8wLvJgHATtXjycf5EA3tclU36hnKAMMIu0yifznGL7yhBCYEwIiEclzWvOgCgg=='

You can see it working and the result in this .NET Fiddle.

like image 57
Christos Lytras Avatar answered Sep 18 '22 02:09

Christos Lytras