Java tutorial
// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package com.cloud.stack; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.SignatureException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; /** * CloudStackCommand wraps command properties that are being sent to CloudStack * */ public class CloudStackCommand { Map<String, String> _params = new HashMap<String, String>(); public CloudStackCommand(String cmdName) { this(cmdName, "json"); } public CloudStackCommand(String cmdName, String responseType) { _params.put("command", cmdName); if (responseType != null) _params.put("response", responseType); } public CloudStackCommand setParam(String paramName, String paramValue) { assert (paramName != null); assert (paramValue != null); _params.put(paramName, paramValue); return this; } public String signCommand(String apiKey, String secretKey) throws SignatureException { assert (_params.get("command") != null); List<String> paramNames = new ArrayList<String>(); for (String paramName : _params.keySet()) paramNames.add(paramName); paramNames.add("apikey"); Collections.sort(paramNames); StringBuffer sb = new StringBuffer(); for (String name : paramNames) { String value; if ("apikey".equals(name)) value = apiKey; else value = _params.get(name); assert (value != null); value = urlSafe(value); if (sb.length() == 0) { sb.append(name).append("=").append(value); } else { sb.append("&").append(name).append("=").append(value); } } String signature = calculateRFC2104HMAC(sb.toString().toLowerCase(), secretKey); return composeQueryString(apiKey, signature); } private String composeQueryString(String apiKey, String signature) { StringBuffer sb = new StringBuffer(); String name; String value; // treat command specially (although not really necessary ) name = "command"; value = _params.get(name); if (value != null) { value = urlSafe(value); sb.append(name).append("=").append(value); } for (Map.Entry<String, String> entry : _params.entrySet()) { name = entry.getKey(); if (!"command".equals(name)) { value = urlSafe(entry.getValue()); if (sb.length() == 0) sb.append(name).append("=").append(value); else sb.append("&").append(name).append("=").append(value); } } sb.append("&apikey=").append(urlSafe(apiKey)); sb.append("&signature=").append(urlSafe(signature)); return sb.toString(); } private String calculateRFC2104HMAC(String signIt, String secretKey) throws SignatureException { String result = null; try { SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); Mac hmacSha1 = Mac.getInstance("HmacSHA1"); hmacSha1.init(key); byte[] rawHmac = hmacSha1.doFinal(signIt.getBytes()); result = new String(Base64.encodeBase64(rawHmac)); } catch (Exception e) { throw new SignatureException("Failed to generate keyed HMAC on soap request: " + e.getMessage()); } return result.trim(); } private String urlSafe(String value) { try { if (value != null) return URLEncoder.encode(value, "UTF-8").replaceAll("\\+", "%20"); else return null; } catch (UnsupportedEncodingException e) { assert (false); } return value; } }