Sygnatura HMAC SHA256, przepisana z PHP na C#

0

Robię integrację do Apaczka.pl i kolejny mądry wynalazek do autoryzacji użytkownika. Czy ktoś jest w stanie mi wytłumaczyć jak mam to napisać w C# za pomocą RestSharp?

Opis pola signature
Wszystkie przesyłane dane muszą zawierać signature wygenerowany na podstawie przesyłanych danych. Signature musi być wygenerowana na podstawie App ID, nazwy endpoint'u, danych w request oraz daty wygaśnięcia ważności request`u używając metody HMAC, z wykorzystaniem algorytmu SHA256, podając jako klucz App Secret. Przykładowy kod do generowania signature w PHP:

function getSignature( $string, $key ) {
    return hash_hmac( 'sha256', $string, $key );
}

function stringToSign( $appId, $route, $data, $expires ) {
    return sprintf( "%s:%s:%s:%s", $appId, $route, $data, $expires );
}

$signature = getSignature( stringToSign( $appId, $route, $data, $expires ), $appSecret );
$requestData = [
    'app_id'    => $appId,
    'request'   => $data,
    'expires'   => $expires,
    'signature' => $signature
];

app_id - identyfikator uzyskany po założeniu aplikacji w zakładce Web Api.
request - zestaw wymaganych danych zapisanych w strukturze JSON. Dane są opisane przy każdym endpoint.
expires - timestamp do kiedy ważny jest request. Timestamp musi być większy niż obecny. Maksymalna ważność request`u to 30 minut.
signature - podpis zestawu danych. Sposób generowania tego klucza została opisana poniżej.

1

A czego dokładnie nie łapiesz z tego kodu? musisz znaleźć odpowiednik hash_hmac w c# z tego co widze jest nawet taki wątek na SO a text który tam wrzucasz to kolejne stringi rozdzielone dwukropkiem.

0

Ok, mówimy pewnie o tym samym poście na SO, więc zerżnąłem cały kod jak się dało

private string HashMap(string request, string timestamp)
{
  string message = $"{_appId}:orders:{request}:{timestamp}";
  var keyByte = Encoding.ASCII.GetBytes(_appSecret);

  using(var hmacsha = new HMACSHA256(keyByte))
    {
      hmacsha.ComputeHash(Encoding.UTF8.GetBytes(message));
      return ByteToString(hmacsha.Hash);
    }
}

static string ByteToString(byte[] buff)
{
  string sbinary = "";
  for (int i = 0; i < buff.Length; i++)
    sbinary += buff[i].ToString("X2"); /* hex format */
  return sbinary;
}

RESTSHARP REQUEST

public async void TestOrdersApi()
{
  string timestamp = DateTime.UtcNow.AddMinutes(10).Subtract(new DateTime(1970, 1, 1)).TotalSeconds.ToString();
  string requestData = JsonConvert.SerializeObject(new { page = 1, limit = 10 });
  RestClient client = new RestClient("https://www.apaczka.pl/api/v2/");
  RestRequest request = new RestRequest("orders/", Method.Post);
  request.AddParameter("app_id", _appId);
  request.AddParameter("request", requestData);
  request.AddParameter("expires", timestamp);
  request.AddParameter("signature", HashMap(requestData, timestamp));

  var response = await client.ExecuteAsync(request);
}

Dostaje zwrotkę

{"status":400,"message":"Signature doesn't match.","response":[]}
1

Zrób prosty test. Wywołaj tutaj https://onlinephp.io

<?php
echo hash_hmac( 'sha256', 'test', 'asd' );

i porównaj sobie z tym co sam otrzymujesz. Będziesz wiedział czy algo działa poprawnie.

0

Wychodzi to samo i na tej stronie, i u mnie, a mimo to w C# dostaje zwrotkę z błędem 🤦‍♂️

0

no to albo masz cos nie tak w innym miejscu albo masz problem opisany w ostatnim poście na SO.

0

Dobrze rozumiem, chodzi o

static byte[] PackH(string hex)
{
  if ((hex.Length % 2) == 1) hex += '0';
    byte[] bytes = new byte[hex.Length / 2];
  for (int i = 0; i < hex.Length; i += 2)
  {
    bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
  }
  return bytes;
}

Ale mój przypadek jest chyba inny, bo niebardzo wiem gdzie mam to wykorzystać, w którym miejscu 🤔

1

Ok, rozwiązane... Moja metoda przerabiania DateTime na timestamp dodawała wartość z przecinkiem, a on nie jest wspierany przez API.

1 użytkowników online, w tym zalogowanych: 0, gości: 1