2014-02-10 42 views
1

问题已解决 - 请参阅说明末尾的更新2。下面的代码是罚款亚马逊Alexa Web服务始终提供401

撕裂我的头发在这里...但这里有云:

我试图钩到亚马逊Alexa的API(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未授权...

任何想法,我做错了什么?

更新

我可以重现这个同样的问题,他们的Java示例应用程序。我修改了他们的应用程序,只是硬编码AWSAccessId和SecretKey,而且我也不使用他们的应用程序中的sun.misc.BASE64Encoder。

确切的代码如下......再次,如果我抓住从makeRequest的(URI)声明的URI,粘贴到提琴手,我可以看到这是相同的401响应:

<?xml version="1.0"?> 

AuthFailure AWS是不能够验证所提供的访问credentialsff8f1853-b816-47a0-2283-be9941e7f2a9

以及造成上面的代码(我已经改变了ACCESSKEY和秘密密钥):

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

UPDATE 2:

这段代码没有问题。问题出在AWIS注册按钮上。使用页面底部的那个...而不是顶部的那个(这是大多数人会点击的)。亚马逊证实,顶部按钮目前无法正常工作。

+0

您是否包含正确的访问密钥和机密? – svenv

+0

嗨,是的肯定。上述显然不正确(我改变了他们的这篇文章)。即使在使用他们的Java示例应用程序时,我也可以得到完全相同的结果,并使用全新的AWS账户新注册该服务。我开始相信Alexa AWIS的注册按钮有一个问题,特别是... – collumbo

+0

远射,但请尝试在@字符串文字前加@;也许在秘密中有一个控制角色。 – svenv

回答

2

即使处理了功能失常的按钮错误,我仍然通过我的php代码收到了同样的错误。 See here

对我来说,错误是因为我使用的IAM证书仍然与AWIS不兼容,并且这没有被记录在here。应该使用AWIS的root帐户详细信息。 See here