我正在使用 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 上进行标准化,或者当我使用该库时是否有特定的原因它失败了?