Как мне подключиться к Apple News API из c #? - PullRequest
0 голосов
/ 12 июня 2018

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

{"errors":[{"code":"WRONG_SIGNATURE"}]}

Вот мой код c # для создания заголовка аутентификации.

public class Security
{
  public static string AuthHeader(string method, string url, object content=null)
  {
    var apiKeyId = "<YOUR KEY HERE...>"; //we get this value from our settings file.
    var apiKeySecret = "<YOUR SECRET HERE...>"; //we get this value from our settings file.
    if ( string.IsNullOrEmpty(apiKeyId) || string.IsNullOrEmpty(apiKeySecret)) return string.Empty;
    var encoding = new ASCIIEncoding();
    var dt = DateTime.Now.ToString(Constants.DateFormat);
    var canonicalRequest = string.Format("{0}{1}{2}", method, url, dt);
    var key = Convert.FromBase64String(apiKeySecret);
    var hmac = new HMACSHA256(key);
    var hashed = hmac.ComputeHash(encoding.GetBytes(canonicalRequest));
    var signature = Convert.ToBase64String(hashed);
    var authorizaton = string.Format(@"HHMAC; key={0}; signature={1}; date={2}", apiKeyId, signature, dt);
    return authorizaton;
  }
}

Короткая версия класса Constants

public static class Constants
{
  public static readonly string ChannelId = "<YOUR CHANNEL ID HERE...>"; //again from our settings file
  public static readonly string BaseUrl = "https://news-api.apple.com";
  public static readonly string DateFormat = "yyyy-MM-ddTHH:mm:ssK";    
}

Короткая версия класса Actions (SendCommand - метод, выполняющий запрос)

public class Actions
{
  public static string SendCommand(string action, string method)
  {
    var url = $"{Constants.BaseUrl}{action}";      
    var authheader = Security.AuthHeader(method, url, null);
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = method;
    request.Timeout = 1000;
    request.Headers.Add("Authorization", authheader);
    request.Accept = "application/json";
    request.ContentType = "application/json";
    var output = string.Empty;
    try
    {
      using (var response = request.GetResponse())
      {
        using (var reader = new StreamReader(response.GetResponseStream()))
          output = reader.ReadToEnd();
      }
    }
    catch (WebException e)
    {
      using (var reader = new StreamReader(e.Response.GetResponseStream()))
      {
        output = reader.ReadToEnd();
      }
    }          
    return output;
  }

  public static string ReadChannel()
  {
    var action = $"/channels/{Constants.ChannelId}";
    const string method = "GET";
    return SendCommand(action, method);
  }
}

Я использую метод ReadChannel для тестирования.

Я также пробовал примеры в php иРубин без удачи.

Есть идеи, как это правильно сделать?

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

Вставил строку авторизации, сгенерированную из исходного кода в этом сообщении, в fiddler, и я смог получить успешный ответ от Apple News API.Похоже, что HttpWebRequest неправильно включает заголовок Authorization и отправляет тот же запрос со свойством PreAuthenticate = true, исправляет эту проблему ( HttpWebRequest.PreAuthenticate ).Кроме того, в запросе GET необходимо исключить ContentType, поэтому я добавил условное выражение, чтобы учесть это тоже.

public class Actions
{
  public static string SendCommand(string action, string method)
  {
    var url = $"{Constants.BaseUrl}{action}";      
    var authheader = Security.AuthHeader(method, url, null);
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = method;
    request.Timeout = 1000;
    request.PreAuthenticate = true;
    request.Headers.Add("Authorization", authheader);
    request.Accept = "application/json";
    if(method.Equals("post", StringComparison.InvariantCultureIgnoreCase)) 
       request.ContentType = "application/json";
     var output = string.Empty;
    try
    {
      using (var response = request.GetResponse())
      {
        using (var reader = new StreamReader(response.GetResponseStream()))
          output = reader.ReadToEnd();
      }
    }
    catch (WebException e)
    {
      using (var reader = new StreamReader(e.Response.GetResponseStream()))
      {
        output = reader.ReadToEnd();
      }
    }          
    return output;
  }

  public static string ReadChannel()
  {
    var action = $"/channels/{Constants.ChannelId}";
    const string method = "GET";
    return SendCommand(action, method);
  }
}
0 голосов
/ 12 июня 2018

Ошибка жалуется, что что-то не так в том, как мы вычисляем подпись.Давайте посмотрим на пример кода Apple для получения правильной подписи, которая находится здесь:

https://developer.apple.com/documentation/apple_news/apple_news_api/about_the_news_security_model

К сожалению, я нашел только код Python.Я тоже не знаю Python, но я могу понять достаточно, чтобы адаптировать его, чтобы просто показать подпись.Нам также необходимо точно знать, какое значение даты использовалось.

import base64
from hashlib import sha256
import hmac
from datetime import datetime

channel_id = 'cdb737aa-FFFF-FFFF-FFFF-FFFFFFFFFFFF'
api_key_id = '240ab880-FFFF-FFFF-FFFF-FFFFFFFFFFFF'
api_key_secret = 'HgyfMPjFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF='
url = 'https://news-api.apple.com/channels/%s' % channel_id
date = str(datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"))
canonical_request = 'GET' + url + date
key = base64.b64decode(api_key_secret)
hashed = hmac.new(key, canonical_request, sha256)
signature = hashed.digest().encode("base64").rstrip('\n')
print date
print signature

Результат можно увидеть здесь (короткая ссылка указывает на tutorialspoint.com, который был первым онлайн-интерпретатором Python, который я нашел в Google):

http://tpcg.io/e1W4p1

Если вы не доверяете ссылке, просто знайте, что я смог использовать ее, чтобы выяснить следующую известную правильную подпись на основена этих известных входах:

Метод: GET
URL: https://news-api.apple.com/channels/cdb737aa-FFFF-FFFF-FFFF-FFFFFFFFFFFF
DateTime: 2018-06-12T18:15:45Z
API Secret: HgyfMPjFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=
Подпись: f3cOzwH7HGYPg481noBFwKgVOGAhH3jy7LQ75jVignA=

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

Исходя из этого, я смог написать этот код C #:

public static void Main()
{
    string method = "GET";
    string url = "https://news-api.apple.com/channels/cdb737aa-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
    string dateString = "2018-06-12T18:15:45Z";
    string apiKeySecret = "HgyfMPjFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=";

    var MyResult = GetSignature(method, url, dateString, apiKeySecret);
    var DocumentedResult = "f3cOzwH7HGYPg481noBFwKgVOGAhH3jy7LQ75jVignA=";

    Console.WriteLine(MyResult);
    Console.WriteLine(MyResult == DocumentedResult);
}

public static string GetSignature(string method, string url, string dt, string APISecret)
{
    var hmac = new HMACSHA256(Convert.FromBase64String(APISecret));
    var hashed = hmac.ComputeHash(Encoding.ASCII.GetBytes(method + url + dt));

    return Convert.ToBase64String(hashed);
}

Какой выможно увидеть в действии здесь:

https://dotnetfiddle.net/PQ73Zv

У меня нет своего собственного ключа Apple API для тестирования, так что это насколько я могу вам помочь,

Одна вещь, на которую я обратил внимание в этом вопросе, это то, что в примере Apple есть «Z» в конце строки даты, которая здесь отсутствует с исходным кодом.

...