Hi Anatha (and Alena and others):

Thanks for your script.
I ended up finding this gem of a perl script:
   https://github.com/snumano/CloudStack-API/blob/master/generate-url.pl
in this really excellent article about signed and (mostly) unsigned api calls:
   
http://www.shapeblue.com/2012/05/10/using-the-api-for-advanced-network-management/
I used that gem of a script for my own perl script below:

#!/usr/bin/perl -w

use strict;
use warnings;

use Digest::SHA qw(hmac_sha1);
use Encode;
use Getopt::Std;
use File::Basename qw(basename);
use HTTP::Request;
use JSON::XS;
use LWP::UserAgent;
use MIME::Base64;
use URI::Encode;

my ($content, $debug, $field, $value);

my $my_filename = basename($0, '');

our ($opt_a, $opt_d, $opt_f, $opt_s, $opt_u);

$MY_CS_IP_AND_PORT = 'Enter your CS Server Name or IP here followed by :8080';

my $site = "http://$MY_CS_IP_AND_PORT/client/api?";;

getopt "adfsu";

if (!defined ($opt_u) || !defined ($opt_a) || !defined ($opt_s) || !defined 
($opt_f)){
    my $usage = "Usage:\n$my_filename -d <0|1> -f <flag: 1. URL ";
    $usage = "$usage" . "only, OR 2. URL and Output> -u \"command=<cmd>\" ";
    $usage = "$usage" .  "-a <api_key> -s <secret_key>\n";
    die ("$usage");
}

######################################################################
# Process input parameters.
######################################################################

my $api_key = $opt_a;
$debug = $opt_d;
my $secret_key = $opt_s;
my $command = $opt_u;

# Flag for output: 1. URL only, 2. Both URL and Output
my $flag = $opt_f;

# URL encoded for special characters.
my $uri = URI::Encode->new();

######################################################################
### Step 1
### Combine parts to form initial URL.
######################################################################

my $query = $command."&apiKey=".$api_key;
my @list = split (/&/, $query);
foreach (@list){
    if (/(.+)\=(.+)/) {
        $field = $1;
        # Encode_reserved option is set to 1.
        $value = $uri->encode($2, 1);
        $_ = $field."=".$value;
    }
}

######################################################################
### Step 2
### Lower-case each parameter and parameter value.
######################################################################

foreach (@list){
    $_ = lc ($_);
}

######################################################################
### Step 3
# Sort parameter=parameter_values by parameter and join each
# with an ampersand.
######################################################################

my $output = join ("&", sort @list);

######################################################################
### Step 4
### HMAC SHA1 encode the URL.
######################################################################

my $digest = hmac_sha1($output, $secret_key);
my $base64_encoded = encode_base64 ($digest);
chomp($base64_encoded);

# Encode_reserved option is set to 1.
my $url_encoded = $uri->encode($base64_encoded, 1);

######################################################################
### Step 5
### Combine parts for the Final URL.
######################################################################

my $final_url = 
$site."apikey=".$api_key."&".$command."&signature=".$url_encoded;

######################################################################
### Step 6
### Print out the Final URL and/or the output from
### executing the Final URL.
######################################################################

if ($flag == 1 || $flag == 2){
    if (defined $debug) {
        if ($debug != 0) {
            print "FINAL URL:\n";
        }
    }
    print "$final_url";
    if ($flag == 1){
        exit;
    }
    if ($flag == 2) {
        my $agent = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1, timeout 
=> 30);
        my $header = HTTP::Request->new(GET => $final_url);
        my $request = HTTP::Request->new('GET', $final_url, $header);
        my $response = $agent->request($request);

        # Check the outcome of the response
        if ($response->is_success){
            if (defined $debug) {
                if ($debug != 0) {
                    print "\nHEADERS:\n";
                    print $response->headers_as_string;
                }
            }
            print "\nCONTENT:\n";
            $content = $response->as_string;
            print $content;
            exit 0;
        } elsif ($response->is_error){
            print "\nError My Final URL: $final_url\n";
            print $response->error_as_HTML;
            exit 1;
        }
    }
}

Regards,
Evan



-----Original Message-----
From: Anantha Kasetty [mailto:[email protected]] 
Sent: Saturday, June 09, 2012 6:52 AM
To: [email protected]
Subject: Re: Having trouble getting a CloudStack API URL with api_key and 
generated signature to work from the browser (Firefox).

Had to dust up my long forgotten Perl skills,  here is a sample program

use URI::Escape;
use Digest::SHA qw(hmac_sha1_base64);

sub hmac_digest {
    my $uri = shift @_;
    my $key = shift @_;

    $digest = hmac_sha1_base64($uri, $key);

    #@#$%! perl does not pad the output of base64

    while (length($digest) % 4) {
        $digest .= '=';
    }   
    return $digest;
}

sub escape_hash {
    my %hash = @_; 
    my @pairs;
    
    for my $key (sort keys %hash) {
        push @pairs, join "=", map { uri_escape($_) } lc($key), lc($hash{$key});
    }   
    return join "&", @pairs;
}

my $secret_key =
'kNd2VxlXxCXwyJGlidr0ZcmcqXSH2refwxZTStD6If4vJu4QmJPOIui0rgr88mDI6DuGQTzP9e
QNOjlZBTReKg' ;


my $uri = escape_hash(
'apikey'=>'8v_gevjjgdjbbhibmlle4yyhkseqhrefztnv4up2fu3k9y12th7lscsn6-7seago
1ycctnute1ot0v7npcus8q',
                       'command'=>'listZones',
                       'response'=>'json');

$signature = uri_escape(hmac_digest($uri, $secret_key));

$uri .= "&signature=$signature";
print "$uri\n";



Let me know if this works for you.



regards,
Anantha Kasetty 











On 6/8/12 8:40 PM, "Alena Prokharchyk" <[email protected]>
wrote:

>On 6/8/12 7:08 PM, "Evan Miller" <[email protected]> wrote:
>
>>Hi Alena:
>>
>>-----Original Message-----
>>From: Alena Prokharchyk [mailto:[email protected]]
>>Sent: Friday, June 08, 2012 3:56 PM
>>To: [email protected]
>>Subject: Re: Having trouble getting a CloudStack API URL with api_key and
>>generated signature to work from the browser (Firefox).
>>
>>On 6/8/12 12:23 PM, "Evan Miller" <[email protected]> wrote:
>>
>>>Hi:
>>>
>>>I am not doing something quite right yet generating a good
>>>CloudStack API URL.
>>>
>>>I still get this same error from the browser when
>>>I try to execute the final url:
>>>
>>>{ "listvirtualmachinesresponse" : {"errorcode" : 401, "errortext" :
>>>"unable to verify user credentials"}  }
>>>
>>>Here is what my perl script is doing:
>>>
>>>Original Command String:
>>> 
>>>apiKey=8v_GEvJJgDjbbHIBmlle4yyHKseQhRefztnv4UP2fU3K9y12TH7lscsn6-7SEaGO1
>>>y
>>>C
>>>CTnUtE1oT0v7npCuS8Q&command=listVirtualMachines&response=json
>>>
>>>Sorted (by field), lower-case Command String:
>>> 
>>>apikey=8v_gevjjgdjbbhibmlle4yyhkseqhrefztnv4up2fu3k9y12th7lscsn6-7seago1
>>>y
>>>c
>>>ctnute1ot0v7npcus8q&command=listvirtualmachines&response=json
>>>
>>>Encoded, sorted, lower-case Command String:
>>> 
>>>apikey%3D8v_gevjjgdjbbhibmlle4yyhkseqhrefztnv4up2fu3k9y12th7lscsn6-7seag
>>>o
>>>1
>>>ycctnute1ot0v7npcus8q%26command%3Dlistvirtualmachines%26response%3Djson
>>
>>Here is the problem - you have to encode just parameter values, not the
>>parameters themselves and definitely not "=" and "&" special chars.
>>
>>You can look at the java code sample I wrote for the dev bootcamp, here
>>is
>>the link in the source tree:
>>
>>http://git.cloud.com/cgit/cloudstack-oss/tree/test/src/com/cloud/test/dem
>>o
>>/Demo.java?h=3.0.x
>>
>>
>>- Begin -
>>
>>I don't really know Java, but I did see your numbered steps
>>at the bottom.
>>
>>I still get an error for the final url:
>>
>>{ "listvirtualmachinesresponse" : {"errorcode" : 401, "errortext" :
>>"unable to verify user credentials and/or request signature"}  }
>>
>>Here is what I tried ... continuing in perl.
>>I encoded just the parameters (not the field values).
>>The parameters didn't really require any encoding.
>>No special characters, but I did it anyway.
>>So, after encoding, the parameters didn't change.
>>I did encode the signature before appending to the
>>final url.
>>
>>Here's the flow ...
>>
>>Original Command String:
>> 
>>apiKey=8v_GEvJJgDjbbHIBmlle4yyHKseQhRefztnv4UP2fU3K9y12TH7lscsn6-7SEaGO1y
>>C
>>CTnUtE1oT0v7npCuS8Q&command=listVirtualMachines&response=json
>>
>>Command String with Encoded Parameters:
>> 
>>apiKey=8v_GEvJJgDjbbHIBmlle4yyHKseQhRefztnv4UP2fU3K9y12TH7lscsn6-7SEaGO1y
>>C
>>CTnUtE1oT0v7npCuS8Q&command=listVirtualMachines&response=json
>>
>>Sorted (by field), lower-case, encoded Command String:
>> 
>>apikey=8v_gevjjgdjbbhibmlle4yyhkseqhrefztnv4up2fu3k9y12th7lscsn6-7seago1y
>>c
>>ctnute1ot0v7npcus8q&command=listvirtualmachines&response=json
>>
>>SHA1 HEX String:
>> f8d4d96dd59c3bd562dc32586539fa9162c5ed70
>>
>>SHA1 Base64 Signature (using SHA1 HEX String):
>> 3wOrhy/SstxN+NbdoT8h/bkla2E
>>
>>Encoded Signature:
>> 3wOrhy%2FSstxN%2BNbdoT8h%2Fbkla2E
>>
>>Final URL:
>> 
>>http://10.217.5.192:8080/client/api?command=listVirtualMachines&apiKey=8v
>>_
>>GEvJJgDjbbHIBmlle4yyHKseQhRefztnv4UP2fU3K9y12TH7lscsn6-7SEaGO1yCCTnUtE1oT
>>0
>>v7npCuS8Q&response=json&signature=3wOrhy%2FSstxN%2BNbdoT8h%2Fbkla2E
>>
>>Does the syntax of the final url, at least, look right?
>>
>>If so, then there must be something wrong with the signature.
>>
>>Regards,
>>Evan
>
>
>
>The syntax looks right to me.
>
>-Alena.
>
>>
>>- End -
>>
>>
>>-Alena.
>>
>>>
>>>SHA1 HEX String:
>>> 9066d795102c0cf8a12322507887122b6b4a6095
>>>
>>>SHA1 Base64 Signature (using SHA1 HEX String and Secret Key):
>>> SvDq03i4Tql9qkXuZwUDi3HfbH4
>>>
>>>Final URL:
>>> 
>>>http://10.217.5.192:8080/client/api?command=listVirtualMachines&apiKey=8
>>>v
>>>_
>>>GEvJJgDjbbHIBmlle4yyHKseQhRefztnv4UP2fU3K9y12TH7lscsn6-7SEaGO1yCCTnUtE1o
>>>T
>>>0
>>>v7npCuS8Q&response=json&signature=SvDq03i4Tql9qkXuZwUDi3HfbH4
>>>
>>>I am generating the SHA1 HEX String like so:
>>>  $digest = sha1_hex ($encode, $secret_key);
>>>using this module:
>>>  use Digest::SHA qw(sha1 sha1_hex sha1_base64);
>>>
>>>I am generating the SHA1 Base64 Signature like so:
>>>  $signature = sha1_base64 ($digest, $secret_key);
>>>
>>>
>>>Curious - Is SHA1 HEX different from HMAC SHA1?
>>>
>>>
>>>Regards,
>>>Evan
>>>
>>>-----Original Message-----
>>>From: Prasanna Santhanam [mailto:[email protected]]
>>>Sent: Friday, June 08, 2012 1:20 AM
>>>To: [email protected]
>>>Subject: Re: Having trouble getting a CloudStack API URL with api_key
>>>and
>>>generated signature to work from the browser (Firefox).
>>>
>>>On Thu, Jun 07, 2012 at 10:38:20PM -0400, Evan Miller wrote:
>>>> Hi:
>>>> 
>>>> Right now, I am just testing a simple, final API-based url with
>>>>signature in a browser.
>>>> I am getting the following authentication related error in the
>>>>browser:
>>>> 
>>>> { "listvirtualmachinesresponse" : {"errorcode" : 401, "errortext" :
>>>> "unable to verify user credentials and/or request signature"}  }
>>>> 
>>>> Here is the final API-based url:
>>>> 
>>>>  
>>>> http://10.217.5.192:8080/client/api?command=listVirtualMachines&apiKey
>>>> =8v_GEvJJgDjbbHIBmlle4yyHKseQhRefztnv4UP2fU3K9y12TH7lscsn6-7SEaGO1yCCT
>>>> nUtE1oT0v7npCuS8Q&response=json&signature=1ca7bc1bbc67b8f578c7d094c523
>>>> 537571ee17b1
>>>> 
>>>> Here is how I built that final url:
>>>> 
>>>> Using perl, it is based on the following pieces:
>>>> 
>>>> my $cs_ip = '10.217.5.192';
>>>> my $base_url = 'http://' . "$cs_ip" . ':8080'; my $api_path =
>>>> '/client/api?'; my $api_key =
>>>> '8v_gevjjgdjbbhibmlle4yyhkseqhrefztnv4up2fu3k9y12th7lscsn6-7seago1ycct
>>>> nute1ot0v7npcus8q'; my $secret_key =
>>>> 'kNd2VxlXxCXwyJGlidr0ZcmcqXSH2refwxZTStD6If4vJu4QmJPOIui0rgr88mDI6DuGQ
>>>> TzP9eQNOjlZBTReKg';
>>>> 
>>>> The api_key and secret key come from my account (evan) in domain 1.
>>>> The keys were just generated in a new CloudStack GUI session.
>>>> The evan account has ROOT Domain privileges.
>>>> 
>>>> Then, following directions in the Developer's Guide ...
>>>> 
>>>> The sorted, lower-case command string ($sorted_lc_cmd) is:
>>>> 
>>>> apikey=8v_gevjjgdjbbhibmlle4yyhkseqhrefztnv4up2fu3k9y12th7lscsn6-7seag
>>>> o1ycctnute1ot0v7npcus8q&command=listvirtualmachines&response=json
>>>> 
>>>> 
>>>> I obtained the hex signature in perl as follows:
>>>> 
>>>> 
>>>> $signature = hmac_sha1_hex ($sorted_lc_cmd, $secret_key);
>>>> 
>>>> 
>>>> And, then, put together the final url, as above, with the calculated
>>>>signature.
>>>> 
>>>> 
>>>> What am I doing wrong or missing?
>>>
>>>Before you obtain the hmac SHA-1 you need to url encode the request url
>>>stripping it off reserved characters like "+, !, $" etc. Then you
>>>perform
>>>the HMAC on the resultant string. This then is passed through a base64
>>>encoder to obtain the signature. It looks like you missed this step. Can
>>>you base64 encode the hmac result and give it a shot?
>>>
>>>
>>>
>>>--
>>>Prasanna.,
>>>
>>>
>>
>>
>>
>
>

Reply via email to