2014-02-10 4 views
1

Проблема решена - см. Обновление 2 в конце описания. Ниже код прекрасноAmazon Alexa Web Service всегда дает 401

Разрывая мои волосы здесь ... но вот он идет:

Я пытаюсь подключить к API Amazon Alexa (http://docs.aws.amazon.com/AlexaWebInfoService/latest/index.html?ApiReference_UrlInfoAction.html) данные из API ... мне нужно используйте C#.

Я обновил это сообщение ниже с помощью кода Java. Я использовал, чтобы увидеть, является ли это проблемой моего кода или проблемой AWIS.

Что касается C#, парень в конце этого поста утверждает, что он работает: https://forums.aws.amazon.com/message.jspa?messageID=476573#476573

Это C# код, который вызывает класс:

var awis = new AmazonAWIS 
       { 
        AWSAccessKeyId = "ABCDRFGHIJKLMNOP", 
        AWSSecret = "GpC0PcXnnzG/TCpoi9r7RxBtqCzdKaHeEkq7Mfs6"  
       }; 

    awis.UrlInfo("bbc.co.uk"); 

И это код класса взято непосредственно из ссылки, размещенной выше ... Я не изменил его:

public class AmazonAWIS 
{ 
    public string AWSAccessKeyId { get; set; } 
    public string AWSSecret { get; set; } 

    protected string GenerateSignature(string param) 
    { 
     var sign = "GET\n" + "awis.amazonaws.com" + "\n/\n" + param; 

     // create the hash object 
     var shaiSignature = new HMACSHA256(Encoding.UTF8.GetBytes(AWSSecret)); 

     // calculate the hash 
     var binSig = shaiSignature.ComputeHash(Encoding.UTF8.GetBytes(sign)); 

     // convert to hex 
     var signature = Convert.ToBase64String(binSig); 

     return signature; 
    } 

    // this is one of the key problems with the Amazon code and C#.. C# by default returns excaped values in lower case 
    // for example %3a but Amazon expects them in upper case i.e. %3A, this function changes them to upper case.. 
    // 
    public static string UpperCaseUrlEncode(string s) 
    { 
     char[] temp = HttpUtility.UrlEncode(s).ToCharArray(); 
     for (int i = 0; i < temp.Length - 2; i++) 
     { 
      if (temp[i] == '%') 
      { 
       temp[i + 1] = char.ToUpper(temp[i + 1]); 
       temp[i + 2] = char.ToUpper(temp[i + 2]); 
      } 
     } 
     return new string(temp); 
    } 

    string GetQueryParams(string action, Dictionary<string, string> extra) 
    { 
     var time = DateTime.UtcNow; 

     // set the correct format for the date string 
     var timestamp = time.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", System.Globalization.CultureInfo.InvariantCulture); 

     // create a sortable dict 
     var vals = new Dictionary<string, string>(); 

     vals.Add("AWSAccessKeyId", AWSAccessKeyId); 
     vals.Add("Action", action); 
     vals.Add("ResponseGroup", "Rank,ContactInfo,LinksInCount"); 
     vals.Add("Timestamp", timestamp); 
     vals.Add("Count", "10"); 
     vals.Add("Start", "1"); 
     vals.Add("SignatureVersion", "2"); 
     vals.Add("SignatureMethod", "HmacSHA256"); 

     // add any extra values 
     foreach (var v in extra) 
     { 
      if (vals.ContainsKey(v.Key) == false) 
       vals.Add(v.Key, v.Value); 
     } 

     // sort the values by ordinal.. important! 
     var sorted = vals.OrderBy(p => p.Key, StringComparer.Ordinal).ToArray(); 

     var url = new StringBuilder(); 

     foreach (var v in sorted) 
     { 
      if (url.Length > 0) 
       url.Append("&"); 

      url.Append(v.Key + "=" + UpperCaseUrlEncode(v.Value)); 
     } 

     return url.ToString(); 
    } 

    public void UrlInfo(string domain) 
    { 
     string request = "UrlInfo"; 

     // add the extra values 
     var extra = new Dictionary<string, string>(); 
     extra.Add("Url", domain); 

     // run the request with amazon 
     try 
     { 
      var res = RunRequest(request, extra); 

      // process the results... 
      Console.WriteLine(res); 
     } 
     catch (Exception ex) 
     { 
      throw; 
     } 
    } 

    private string RunRequest(string request, Dictionary<string, string> extra) 
    { 
     // generate the query params 
     var queryParams = GetQueryParams(request, extra); 

     // calculate the signature 
     var sig = GenerateSignature(queryParams); 

     // generate the url 
     var url = new StringBuilder(); 
     url.Append("http://awis.amazonaws.com?"); 
     url.Append(queryParams); 
     url.Append("&Signature=" + UpperCaseUrlEncode(sig)); 

     // get the request 

     var c = new WebClient(); 
     var res = c.DownloadString(url.ToString()); 
     return res; 
    } 
} 

Это не будет работать на линии:

var res = c.DownloadString(url.ToString()); 

Я всегда получаю 401 Несанкционированное ...

Любая идея, что я делаю не так?

Update

я могу воспроизвести эту же проблему с их Sample приложения на Java. Я изменил их приложение, чтобы просто скопировать код AWSAccessId и SecretKey, и я также не использую sun.misc.BASE64Encoder, который находится в их приложении.

Точный код ниже ... опять же, если я хватаю Ури из (Uri) заявление makeRequest, вставить в Скрипач, я могу видеть, что это тот же самый 401 ответ:

<?xml version="1.0"?> 

AuthFailure AWS был не в состоянии подтвердить получение доступа credentialsff8f1853-b816-47a0-2283-be9941e7f2a9

и код, который вызвал выше (я изменил Accesskey и SecretKey):

package urlinfo.com; 
import javax.crypto.Mac; 
import javax.crypto.spec.SecretKeySpec; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.UnsupportedEncodingException; 
import java.net.URL; 
import java.net.URLConnection; 
import java.net.URLEncoder; 
import java.security.SignatureException; 
import java.text.SimpleDateFormat; 
import java.util.*; 

/** 
* Makes a request to the Alexa Web Information Service UrlInfo action. 
*/ 
public class UrlInfo { 

private static final String ACTION_NAME = "UrlInfo"; 
private static final String RESPONSE_GROUP_NAME = "Rank,ContactInfo,LinksInCount"; 
private static final String SERVICE_HOST = "awis.amazonaws.com"; 
private static final String AWS_BASE_URL = "http://" + SERVICE_HOST + "/?"; 
private static final String HASH_ALGORITHM = "HmacSHA256"; 

private static final String DATEFORMAT_AWS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; 

private String accessKeyId; 
private String secretAccessKey; 
private String site; 

public UrlInfo(String accessKeyId, String secretAccessKey, String site) { 
    this.accessKeyId = accessKeyId; 
    this.secretAccessKey = secretAccessKey; 
    this.site = site; 
} 

/** 
* Generates a timestamp for use with AWS request signing 
* 
* @param date current date 
* @return timestamp 
*/ 
protected static String getTimestampFromLocalTime(Date date) { 
    SimpleDateFormat format = new SimpleDateFormat(DATEFORMAT_AWS); 
    format.setTimeZone(TimeZone.getTimeZone("GMT")); 
    return format.format(date); 
} 

/** 
* Computes RFC 2104-compliant HMAC signature. 
* 
* @param data The data to be signed. 
* @return The base64-encoded RFC 2104-compliant HMAC signature. 
* @throws java.security.SignatureException 
*   when signature generation fails 
*/ 
protected String generateSignature(String data) 
     throws java.security.SignatureException { 
    String result; 
    try { 
     // get a hash key from the raw key bytes 
     SecretKeySpec signingKey = new SecretKeySpec(
       secretAccessKey.getBytes(), HASH_ALGORITHM); 

     // get a hasher instance and initialize with the signing key 
     Mac mac = Mac.getInstance(HASH_ALGORITHM); 
     mac.init(signingKey); 

     // compute the hmac on input data bytes 
     byte[] rawHmac = mac.doFinal(data.getBytes()); 

     // base64-encode the hmac 
     // result = Encoding.EncodeBase64(rawHmac); 
     // result = new BASE64Encoder().encode(rawHmac); 
     result = javax.xml.bind.DatatypeConverter.printBase64Binary(rawHmac); 

    } catch (Exception e) { 
     throw new SignatureException("Failed to generate HMAC : " 
       + e.getMessage()); 
    } 
    return result; 
} 

/** 
* Makes a request to the specified Url and return the results as a String 
* 
* @param requestUrl url to make request to 
* @return the XML document as a String 
* @throws IOException 
*/ 
public static String makeRequest(String requestUrl) throws IOException { 
    URL url = new URL(requestUrl); 
    URLConnection conn = url.openConnection(); 
    InputStream in = conn.getInputStream(); 

    // Read the response 
    StringBuffer sb = new StringBuffer(); 
    int c; 
    int lastChar = 0; 
    while ((c = in.read()) != -1) { 
     if (c == '<' && (lastChar == '>')) 
      sb.append('\n'); 
     sb.append((char) c); 
     lastChar = c; 
    } 
    in.close(); 
    return sb.toString(); 
} 


/** 
* Builds the query string 
*/ 
protected String buildQuery() 
     throws UnsupportedEncodingException { 
    String timestamp = getTimestampFromLocalTime(Calendar.getInstance().getTime()); 

    Map<String, String> queryParams = new TreeMap<String, String>(); 
    queryParams.put("Action", ACTION_NAME); 
    queryParams.put("ResponseGroup", RESPONSE_GROUP_NAME); 
    queryParams.put("AWSAccessKeyId", accessKeyId); 
    queryParams.put("Timestamp", timestamp); 
    queryParams.put("Url", site); 
    queryParams.put("SignatureVersion", "2"); 
    queryParams.put("SignatureMethod", HASH_ALGORITHM); 

    String query = ""; 
    boolean first = true; 
    for (String name : queryParams.keySet()) { 
     if (first) 
      first = false; 
     else 
      query += "&"; 

     query += name + "=" + URLEncoder.encode(queryParams.get(name), "UTF-8"); 
    } 

    return query; 
} 

/** 
* Makes a request to the Alexa Web Information Service UrlInfo action 
*/ 
public static void main(String[] args) throws Exception { 

    String accessKey = "REMOVED"; 
    String secretKey = "REMOVED"; 
    // String site = args[2]; 
    String site = "www.google.com"; 

    UrlInfo urlInfo = new UrlInfo(accessKey, secretKey, site); 

    String query = urlInfo.buildQuery(); 

    String toSign = "GET\n" + SERVICE_HOST + "\n/\n" + query; 

    System.out.println("String to sign:\n" + toSign + "\n"); 

    String signature = urlInfo.generateSignature(toSign); 

    String uri = AWS_BASE_URL + query + "&Signature=" + 
      URLEncoder.encode(signature, "UTF-8"); 

    System.out.println("Making request to:\n"); 
    System.out.println(uri + "\n"); 

    // Make the Request 

    String xmlResponse = makeRequest(uri); 

    // Print out the XML Response 

    System.out.println("Response:\n"); 
    System.out.println(xmlResponse); 
    } 
} 

ОБНОВЛЕНИЕ 2:

Нет проблем с этим кодом. Проблема заключалась в установке кнопки AWIS. Используйте тот, который находится внизу страницы ... не тот, который находится наверху (это то, что большинство людей нажимает). Amazon подтвердила, что верхняя кнопка не работает в настоящее время.

+0

Вы включили правильный ключ доступа и секрет? – svenv

+0

Привет, Да определенно. Вышеуказанные, очевидно, неверны (я изменил их для этого сообщения). Я получаю тот же результат даже при использовании своего приложения Java Sample, а также используя новую учетную запись AWS, которая недавно подписывается на эту услугу. Я начинаю полагать, что есть проблема с кнопкой регистрации для Alexa AWIS специально ... – collumbo

+0

Длинный снимок, но попробуйте поставить @ перед строковыми литералами; возможно, в тайне есть контрольный характер. – svenv

ответ

2

Я получил ту же ошибку через свой PHP-код даже после того, как была обработана ошибка дисфункциональных кнопок. See here

Для меня ошибка была из-за учетных данных IAM, которые я использовал, которые все еще не совместимы с AWIS, и это не было зарегистрировано here. Для AWIS следует использовать данные учетной записи root. See here

Смежные вопросы