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