0

我正在使用 Apache Cloudstack API。我通常使用 org.apache.commons.codec.binary.Base64 base64 编码器。在生成命令时,我使用以下代码(来自给出的示例):

private String generateUrl(String commands) throws Exception {
        //Signature: This is the hashed signature of the Base URL that is generated using a combination of the user’s Secret Key and the HMAC SHA-1 hashing algorithm.

        /*
        1. For each field-value pair (as separated by a '&') in the Command String, URL encode each value so that it can be safely sent via HTTP GET.
        2. Lower case the entire Command String and sort it alphabetically via the field for each field-value pair. The result of this step would look like the following.
        3. Take the sorted Command String and run it through the HMAC SHA-1 hashing algorithm (most programming languages offer a utility method to do this) with the user’s Secret Key. Base64 encode the resulting byte array in UTF-8 so that it can be safely transmitted via HTTP. The final string produced after Base64 encoding should be "Lxx1DM40AjcXU%2FcaiK8RAP0O1hU%3D".
           By reconstructing the final URL in the format Base URL+API Path+Command String+Signature, the final URL should look like:
        */

        commands += "&id=" + apiReferenceId;
        commands += "&response=json";

        // Step 1: Make sure your APIKey is toLowerCased and URL encoded
        String encodedApiKey = URLEncoder.encode(apiKey.toLowerCase(), "UTF-8");

        // Step 2: toLowerCase all the parameters, URL encode each parameter value, and the sort the parameters in alphabetical order
        // Please note that if any parameters with a '&' as a value will cause this test client to fail since we are using '&' to delimit
        // the string

        List<String> sortedParams = new ArrayList<String>();
        sortedParams.add("apikey="+encodedApiKey);
        StringTokenizer st = new StringTokenizer(commands, "&");
        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);

        // Step 3: Construct the sorted URL and sign and URL encode the sorted URL with your secret key
        String sortedUrl = null;
        boolean first = true;
        for (String param : sortedParams) {
            if (first) {
                sortedUrl = param;
                first = false;
            } else {
                sortedUrl = sortedUrl + "&" + param;
            }
        }
        Logger.debug("sorted URL : " + sortedUrl);

        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
        mac.init(keySpec);
        mac.update(sortedUrl.getBytes());
        byte[] encryptedBytes = mac.doFinal();
        String encodedSignature = URLEncoder.encode(Base64.encodeBase64String(encryptedBytes), "UTF-8");

        // Step 4: Construct the final URL we want to send to the CloudStack Management Server
        // Final result should look like:
        // http(s)://://client/api?&apiKey=&signature=

        String finalUrl = apiUrl + "?" + commands + "&apiKey=" + apiKey + "&signature=" + encodedSignature;
        return finalUrl;
}

但是,这会产生 401 错误。

<?xml version="1.0" encoding="UTF-8"?><stopvirtualmachineresponse cloud-stack-version="4.2.1"><errorcode>401</errorcode><errortext>unable to verify user credentials and/or request signature</errortext></stopvirtualmachineresponse>

当我使用:

String encodedSignature = URLEncoder.encode(org.postgresql.util.Base64.encodeBytes(encryptedBytes), "UTF-8");

它工作正常。有没有一种方法可以在 Apache Commons 上进行标准化,或者当我使用该库时是否有特定的原因它失败了?

4

1 回答 1

0

一个推测。 我确实看到secretKey.getBytes()和其他getBytes()。假设这secretKey是一个字符串,它应该是:

secretKey.getBytes(StandardCharsets.UTF_8)

因为默认字符集是当前操作系统的字符集。你可能在不同的机器上运行,Windows 用于开发,Linux 用于服务器。

唯一的其他变化在于换行和终止填充符(破折号)。Apache commons codecs Base64有一些控制参数。

顺便说一句,JavaEE 中的几个地方还有一些其他(非 Sun!)Base64 API,尽管 apache commons 是一个不错的选择。示例:javax.xml.bind.DatatypeConverter.parseBase64Binary

于 2014-02-27T17:38:32.880 回答