0

I am working on CloudStack API now and I have the problem about making the API request. I always got "{ "listtemplatesresponse" : {"errorcode":401,"errortext":"unable to verify user credentials and/or request signature"} }" even though I change the parameter.

This error occurs in some commands that require the parameter and this is the command that I use:

    command=listTemplates&templatefilter=featured 

I don't know what I did wrong since it works with others. Here is the code I use to make the API request:

    try {
        String encodedApiKey = URLEncoder.encode(apiKey.toLowerCase(), "UTF-8");
        ArrayList<String> sortedParams = new ArrayList<String>();
        sortedParams.add("apikey="+encodedApiKey);
        StringTokenizer st = new StringTokenizer(apiUrl, "&");
        while (st.hasMoreTokens()) {
            String paramValue = st.nextToken().toLowerCase();
            String param = paramValue.substring(0, paramValue.indexOf("="));
            String value = URLEncoder.encode(paramValue.substring(paramValue.indexOf("=")+1, paramValue.length()), "UTF-8");
            sortedParams.add(param + "=" + value);
        }

        Collections.sort(sortedParams);
        System.out.println("Sorted Parameters: " + sortedParams);
        String sortedUrl = null;
        boolean first = true;
        for (String param : sortedParams) {
            if (first) {
                sortedUrl = param;
                first = false;
            } else {
                sortedUrl = sortedUrl + "&" + param;
            }
        }

        sortedUrl += "&response=json";          
        System.out.println("sorted URL : " + sortedUrl);
        String encodedSignature = signRequest(sortedUrl, secretKey);
        String finalUrl = host + "?" + apiUrl + "&response=json&apiKey=" + apiKey + "&signature=" + encodedSignature;
        StringBuilder str = new StringBuilder();
        HttpClient client = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet(finalUrl);
        HttpResponse response = client.execute(httpGet);
        StatusLine statusLine = response.getStatusLine();
        int statusCode = statusLine.getStatusCode();
        if (statusCode == 200) { // Status OK
            HttpEntity entity = response.getEntity();
            InputStream content = entity.getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(content));
            String line;
            while ((line = reader.readLine()) != null) {
                str.append(line);
            }
            System.out.println("str: "+str);
            result = str.toString();
            System.out.println("result: "+str);
        }
        else
            System.out.println("Error response!!");
    } catch (Throwable t) {
        System.out.println(t);
    }

And this is signRequest function:

public static String signRequest(String request, String key) {
    try {
        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1");
        mac.init(keySpec);
        mac.update(request.getBytes());
        byte[] encryptedBytes = mac.doFinal();
        return URLEncoder.encode(Base64.encodeBytes(encryptedBytes), "UTF-8");
    } catch (Exception ex) {
        System.out.println(ex);
    }
    return null;
}

Please feel free to ask me if you need more information. All comments and advice are welcome!

2 Answers2

0

Have you tried sorting after you've added "&response=json" to the list of parameters?

E.g.

try {
    String encodedApiKey = URLEncoder.encode(apiKey.toLowerCase(), "UTF-8");
    ArrayList<String> sortedParams = new ArrayList<String>();
    sortedParams.add("apikey="+encodedApiKey);
    sortedParams.add("response=json");
    StringTokenizer st = new StringTokenizer(apiUrl, "&");
    while (st.hasMoreTokens()) {
        String paramValue = st.nextToken().toLowerCase();
        String param = paramValue.substring(0, paramValue.indexOf("="));
        String value = URLEncoder.encode(paramValue.substring(paramValue.indexOf("=")+1, paramValue.length()), "UTF-8");
        sortedParams.add(param + "=" + value);
    }

    Collections.sort(sortedParams);
    System.out.println("Sorted Parameters: " + sortedParams);
    String sortedUrl = null;
    boolean first = true;
    for (String param : sortedParams) {
        if (first) {
            sortedUrl = param;
            first = false;
        } else {
            sortedUrl = sortedUrl + "&" + param;
        }
    }

    System.out.println("sorted URL : " + sortedUrl);
    String encodedSignature = signRequest(sortedUrl, secretKey);
    String finalUrl = host + "?" + apiUrl + "&response=json&apiKey=" + apiKey + "&signature=" + encodedSignature;
    StringBuilder str = new StringBuilder();
    HttpClient client = new DefaultHttpClient();
    HttpGet httpGet = new HttpGet(finalUrl);
    HttpResponse response = client.execute(httpGet);
    StatusLine statusLine = response.getStatusLine();
    int statusCode = statusLine.getStatusCode();
    if (statusCode == 200) { // Status OK
        HttpEntity entity = response.getEntity();
        InputStream content = entity.getContent();
        BufferedReader reader = new BufferedReader(new InputStreamReader(content));
        String line;
        while ((line = reader.readLine()) != null) {
            str.append(line);
        }
        System.out.println("str: "+str);
        result = str.toString();
        System.out.println("result: "+str);
    }
    else
        System.out.println("Error response!!");
} catch (Throwable t) {
    System.out.println(t);
}  
Donal Lafferty
  • 5,807
  • 7
  • 43
  • 60
0

Your API Key and Response parameters need to be part of the sorted Url used when signing, which they appear to be.

try changing return URLEncoder.encode(Base64.encodeBytes(encryptedBytes), "UTF-8"); to return URLEncoder.encode(Base64.encodeAsString(encryptedBytes), "UTF-8");

Mohsen Esmailpour
  • 11,224
  • 3
  • 45
  • 66
Chris Ryan
  • 131
  • 1
  • 3