Scala: HMAC SHA-1 Hashing and Base64 Encording for CloudStack API Authentication

To send HTTP request to CloudStack API endpoint, you need to implement a little bit comlicated authentication system. http://docs.cloudstack.apache.org/en/latest/dev.html Following is the format of URL to send HTTP Request.

Base URL+API Path+Command String + Signature

where, Base URL is http://cloudstacl_ip:8080 API Path is /client/api? Command String: command, its parameters, in addition, API_Key which is like "miVr6X7u6bN_sdahOBpjNejPgEsT35eXqjB8CG20YI3yaxXcgpyuaIRmFI_EJTVwZ0nUkkJbPmY3y2bciKwFQ" that identifies the account. You can find it at CloudStack Management GUI. and Signature is a hashed and encoded string generated by HMAC SHA-1 hashing with "Secret Key" and Base64 Encoding. You can find "Secret Key" at Management GUI as well.

What comlicating part is that you need to generate "Signature". To generate signature, you need to follow the steps. First, lower case the entire Command String and sort it alphabetically. Second, combine all the key-value pairs with '&'. Finally, hash the combined string using HMAC SHA-1 with Secret Key and encode by Base64.

There is a tutorial for signature generation in the document, however, it is written in python. So I neeed to write code for the signature generation process by myself. The following codes are what I implemented for this authentication to connect to CloudStack REST API.

  1. Map Command String parameters
    val signature_map = Map[String, String]()
    signature_map += "command" -> command
    signature_map += "response" -> response_type
    signature_map += "apikey" -> apikey

where, command = "listUsers" and response_type = "json".

  1. Lowercasing and Combining
    val alphabetically_sorted_signature_map = ListMap(signature_map.toSeq.sortWith(_._1 < _._1): _*)
    var combined_http_parameter_string = ""
    alphabetically_sorted_signature_map.foreach { case (key, value) =>
      if (combined_http_parameter_string != "") {
        combined_http_parameter_string = combined_http_parameter_string.concat("&")
      }
      combined_http_parameter_string = combined_http_parameter_string.concat(key)
      combined_http_parameter_string = combined_http_parameter_string.concat("=")
      combined_http_parameter_string = combined_http_parameter_string.concat(value)
    }

    val http_request_string = combined_http_parameter_string
    val cloudstack_signature = getHmacSha1_hashing_and_base64_encoding(secretkey, combined_http_parameter_string.toLowerCase)

At the bottom of the code, you pass Secret Key and lowercased and combined Command String to getHmacSha1_hashing_and_base64_encoding() function.

  1. HMAC SHA-1 Hashing and Base64 Encoding

Passing Secret Key and Combined Command String, the below function returns Signature through HMAC SHA-1 Hashing and Base64 Encoding.

  def getHmacSha1_hashing_and_base64_encoding(secretkey: String, input: String) = {
        //HMAC SHA-1 hashing
    val signingKey = new SecretKeySpec(secretkey.getBytes, "HmacSHA1")
    val mac = Mac.getInstance("HmacSHA1")
    mac.init(signingKey)
    var result: Array[Byte] = mac.doFinal(input.getBytes)
        //Base64 Encoding
    Base64.getEncoder.encodeToString(result)

Recommended Posts

Scala: HMAC SHA-1 Hashing and Base64 Encording for CloudStack API Authentication
Password hashing and authentication using JBcrypt