Аутентификация с хешированием - PullRequest
6 голосов
/ 26 марта 2020

Мне нужно установить соединение с API, используя сложный процесс аутентификации, который я не понимаю. Я знаю, что это включает в себя несколько шагов, и я пытался имитировать c это, но я нахожу документацию очень запутанной ...

Идея состоит в том, что я делаю запрос к конечной точке, которая будет возвращать мне токен, который мне нужно использовать для подключения к веб-сокету.

Я получил пример кода, который находится в Python, синтаксис которого мне неизвестен, но я могу использовать его как руководство чтобы преобразовать его в C# -синтаксис.

Это пример кода Python:

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'])

Итак, я попытался преобразовать это в C#, и это код Я так далеко:

    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();
    }

Так что следующая часть - это то, где я действительно борюсь. Должен быть какой-то HMA C -счет, который нужно сделать, но я полностью потерян.

1 Ответ

7 голосов
/ 05 апреля 2020

Основной задачей здесь является обратный расчет API-Sign SHA-512 HMA C. Используйте DateTimeOffset.Now.ToUnixTimeMilliseconds для получения API nonce, он вернет значение Unix метки времени в миллисекундах. Затем все сводится к объединению массивов байтов и генерации хешей. Я использую жестко api_nonce время, чтобы продемонстрировать результат; вам нужно раскомментировать string ApiNonce = DateTimeOffset.Now.ToUnixTimeMilliseconds, чтобы получать текущие Unix метки времени при каждом вычислении клавиши API-Sign.

Python API-Sign генерация:

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)

Будет выводить:

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

C# API-Sign поколение:

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;
}

Будет выводиться:

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

Вы можете видеть, как это работает, и результат в этом . NET скрипка .

...