I am developing a PHP script to consume the Iconomi API, but I have problems with authentication.
According to the documentation, you have to create a signature and attach it in the header. However the curl_exec function returns false, which means it has failed.
These are the instructions:
Creating a request
All REST requests must contain the following headers:
ICN-API-KEY - The api key as a string.
ICN-SIGN - The base64-encoded signature (see Signing a Message).
ICN-TIMESTAMP - A timestamp for your request in epoch milliseconds.
Signing a Message
You generate the ICN-SIGN header by creating a sha512 HMAC using the base64-decoded secret key on the > prehash string timestamp + method + requestPath + body (where + represents string concatenation) and base64-encode the output, where:
the timestamp value is the same as the ICN-TIMESTAMP header.
the body is the request body string or omitted if there is no request body (typically for GET requests).
method must always be in upper case
And this is my code:
function call_iconomi_api()
{
$url = 'https://api.iconomi.com';
$api_key = get_field('iconomi_api_key', 'option');
$api_secret = get_field('iconomi_api_secret', 'option');
$timestamp = round(microtime(true) * 1000);
$request_path = '/v1/assets';
$message = $timestamp . 'GET' . $request_path;
$signature = base64_encode(hash_hmac('sha512', $message, $api_secret));
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url . $request_path);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'ICN-API-KEY: ' . $api_key,
'ICN-SIGN: ' . $signature,
'ICN-TIMESTAMP: ' . $timestamp
));
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
Try to use
$signature = base64_encode(hash_hmac('sha512', $message, $api_secret, true));
If you look into https://iconomi-ag.github.io/iconomi-api/#section/Introduction/Authentication under section "Signing a Message" (Example of GET with query parameters) this function with last param as true returns identical string as base64 encoded signature
Related
I am trying to get a list of all my Twilio numbers using cURL inside of a php function. I am getting the information I need back from Twilio however it is just a string of all the information with no whitespace or anything.
This is what I am getting back. I have removed my account number and sid:
string(1621) " {{ SID }}ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX(936) 585-6544+19365856544https://demo.twilio.com/welcome/sms/reply/POSTPOSTfalseWed, 11 May 2016 14:39:22 +0000Wed, 18 May 2016 15:41:25 +0000https://demo.twilio.com/welcome/sms/reply/POSTPOSTnonefalsetruetruetruePOST2010-04-01/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/IncomingPhoneNumbers/PN5e0ef24464a1fbbcf4e2bd127c471892"
Here is the function I am using to cURL Twilio.
function post_incoming($POST) {
// resource url & authentication
$uri = 'https://api.twilio.com/2010-04-01/Accounts/' . $this->sid . '/IncomingPhoneNumbers';
$auth = $this->sid . ':' . $this->authtoken;
$res = curl_init();
// set cURL options
curl_setopt( $res, CURLOPT_URL, $uri );
curl_setopt( $res, CURLOPT_USERPWD, $auth ); // authenticate
curl_setopt( $res, CURLOPT_RETURNTRANSFER, true ); // don't echo
// send cURL
$result = curl_exec( $res );
var_dump($result);
return $result;
}
I need to get the information back as a json array or some kind of array that way I can parse out the Twilio phone number for later.
It is a lot easier if you use the PHP helper library. It woudl look something like this:
// Your Account Sid and Auth Token from twilio.com/user/account
$sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$token = "{{ auth_token }}";
$client = new Services_Twilio($sid, $token);
$numbers_arr = array();
// Loop over the list of numbers and echo a property for each one
foreach ($client->account->incoming_phone_numbers as $number) {
$numbers_arr[] = $number->phone_number;
}
return json_encode($numbers_arr);
}
This function uses the Twilio PHP SDK to get a list of all numbers in the account then puts them into an array variable $numbers_arr and returns that variable as JSON.
I am trying to implement Vault of Satoshi's API in Google App Engine Go. Their reference API is in PHP:
<?php
$serverURL = 'https://api.vaultofsatoshi.com';
$apiKey = 'ENTER_YOUR_API_KEY_HERE';
$apiSecret = 'ENTER_YOUR_API_SECRET_HERE';
function usecTime() {
list($usec, $sec) = explode(' ', microtime());
$usec = substr($usec, 2, 6);
return intval($sec.$usec);
}
$url = 'https://api.vaultofsatoshi.com';
$endpoint = '/info/currency';
$url = $serverURL . $endpoint;
$parameters= array();
$parameters['nonce'] = usecTime();
$data = http_build_query($parameters);
$httpHeaders = array(
'Api-Key: ' . $apiKey,
'Api-Sign:' . base64_encode(hash_hmac('sha512', $endpoint . chr(0) . $data, $apiSecret)),
);
// Initialize the PHP curl agent
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, "something specific to me");
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
?>
My Go code looks like this:
func GenerateSignatureFromValues(secretKey string, endpoint string, values url.Values) string {
query:=[]byte(values.Encode())
toEncode:=[]byte(endpoint)
toEncode = append(toEncode, 0x00)
toEncode = append(toEncode, query...)
key:=[]byte(secretKey)
hmacHash:=hmac.New(sha512.New, key)
hmacHash.Write(toEncode)
answer := hmacHash.Sum(nil)
return base64.StdEncoding.EncodeToString(([]byte(strings.ToLower(hex.EncodeToString(answer)))))
}
func Call(c appengine.Context) map[string]interface{} {
serverURL:="https://api.vaultofsatoshi.com"
apiKey:="ENTER_YOUR_API_KEY_HERE"
apiSecret:="ENTER_YOUR_API_SECRET_HERE"
endpoint:="/info/order_detail"
tr := urlfetch.Transport{Context: c}
values := url.Values{}
values.Set("nonce", strconv.FormatInt(time.Now().UnixNano()/1000, 10))
signature:=GenerateSignatureFromValues(apiSecret, endpoint, values)
req, _:=http.NewRequest("POST", serverURL+endpoint, nil)
req.Form=values
req.Header.Set("Api-Key", apiKey)
req.Header.Set("Api-Sign", signature)
resp, err:=tr.RoundTrip(req)
if err != nil {
c.Errorf("API post error: %s", err)
return nil
}
defer resp.Body.Close()
body, _:= ioutil.ReadAll(resp.Body)
result := make(map[string]interface{})
json.Unmarshal(body, &result)
return result
}
Both of those pieces of code generate the same signature for the same input. However, when I run the PHP code (with the proper Key and Secret), the server responds with a proper response, but while I run the Go code, the server responds with "Invalid signature". This error indicates that the HTTP request generated by Go must be somehow malformed - either HTTP Header's values are wrong (if the header values are completely missing a different error appears), or the way the POST fields are encoded is wrong for some reason.
Can anyone help me find some reason why those two pieces of code generate different HTTP requests and how can I make Go generate requests like the PHP code?
See the documentation for Request.Form:
// Form contains the parsed form data, including both the URL
// field's query parameters and the POST or PUT form data.
// This field is only available after ParseForm is called.
// The HTTP client ignores Form and uses Body instead.
Form url.Values
Specifically "HTTP client ignores Form and uses Body instead."
With this line:
req, _:= http.NewRequest("POST", serverURL+endpoint, nil)
You should use this instead of nil:
bytes.NewBufferString(values.Encode())
Also keep in mind that the order of map is not guaranteed. url.Values is map[string][]string. So you should be using Encode() once and use the same result in the body and signature. There is a chance that by using Encode() twice the order could be different. This is an important difference between Go and PHP.
You should also make a habit of handling error instead of ignoring it.
I'm trying to call the DocuSign REST login information within a CodeIgniter application. The Docusign sample code shows:
// Input your info here:
$integratorKey = '...';
$email = '...#....com';
$password = '...';
$name = 'John Doe';
// construct the authentication header:
$header = "<DocuSignCredentials><Username>" . $email . "</Username><Password>" . $password . "</Password><IntegratorKey>" . $integratorKey . "</IntegratorKey></DocuSignCredentials>";
/////////////////////////////////////////////////////////////////////////////////////////////////
// STEP 1 - Login (to retrieve baseUrl and accountId)
/////////////////////////////////////////////////////////////////////////////////////////////////
$url = "https://demo.docusign.net/restapi/v2/login_information";
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("X-DocuSign-Authentication: $header"));
$json_response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ( $status != 200 ) {
echo "error calling webservice, status is:" . $status;
exit(-1);
}
I keep getting a status response of 0. When I echo out the curl all the xml tags have been converted to lowercase (which won't work with Docusign). Has anyone done this call within CodeIgniter? How did you accomplish it? I know my credentials are good because I can do a command line curl and get a response.
I'm not sure what CodeIgniter is doing and why the tags would be converted to lower case but you're right in that DocuSign expects xml tags to begin with a capital letter so that might not work. What you can do, though, is use JSON headers and request bodies instead.
For instance, instead of an XML formatted authentication header like
<DocuSignCredentials>
<Username>username</Username>
<Password>password</Password>
<IntegratorKey>integrator_key</IntegratorKey>
</DocuSignCredentials>
You could use the corresponding JSON auth header like
{
"Username": "username",
"Password": "password",
"IntegratorKey": "integrator_key"
}
The "nodes" still start with a capital but since it's not xml I'm wondering if CodeIgniter maybe leaves it alone. Examples of this can be found at the DocuSign Developer Center on this page.
<?php
error_reporting(E_ALL);
ini_set("display_errors", 1);
$AWS_ACCESS_KEY_ID = "KEY";
$AWS_SECRET_ACCESS_KEY = "ACCESS KEY";
$base_url = "http://webservices.amazon.com/onca/xml?";
$url_params = array('Operation'=>"ItemSearch",'Service'=>"AWSECommerceService",
'AWSAccessKeyId'=>$AWS_ACCESS_KEY_ID,'AssociateTag'=>"associateTag",
'Version'=>"2011-08-01",'Availability'=>"Available",'Condition'=>"All",
'ItemPage'=>"1",'ResponseGroup'=>"Images,ItemAttributes,EditorialReview",
'Keywords'=>"Amazon");
// Add the Timestamp
$url_params['Timestamp'] = gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time());
// Sort the URL parameters
$url_parts = array();
foreach(array_keys($url_params) as $key)
$url_parts[] = $key."=".$url_params[$key];
sort($url_parts);
// Construct the string to sign
$string_to_sign = "GET\webservices.amazon.com\n/onca/xml?\n".implode("&",$url_parts);
$string_to_sign = str_replace('+','%20',$string_to_sign);
$string_to_sign = str_replace(':','%3A',$string_to_sign);
$string_to_sign = str_replace(';',urlencode(';'),$string_to_sign);
// Sign the request
$signature = hash_hmac("sha256",$string_to_sign,$AWS_SECRET_ACCESS_KEY,TRUE);
// Base64 encode the signature and make it URL safe
$signature = base64_encode($signature);
$signature = str_replace('+','%2B',$signature);
$signature = str_replace('=','%3D',$signature);
$url_string = implode("&",$url_parts);
$url = $base_url.$url_string."&Signature=".$signature;
print $url;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$xml_response = curl_exec($ch);
echo $xml_response;
?>
this returns an signature error;
why?
this is the output, keys and tag are replaced for privacy
http://webservices.amazon.com/onca/xml?AWSAccessKeyId=KEY&AssociateTag=ASSIOCATE TAG&Availability=Available&Condition=All&ItemPage=1&Keywords=Amazon&Operation=ItemSearch&ResponseGroup=Images,ItemAttributes,EditorialReview&Service=AWSECommerceService&Timestamp=2012-05-27T09:35:43.000Z&Version=2011-08-01&Signature=KEVlbW6G9ygvHheTf5m0ymguE64LEaYGDtQZQe0bCLQ%3D
Not sure if you still need help with this, but it's most likely failing due not providing a valid associate tag in your call.
'AssociateTag'=>"associateTag"
So you need to change this value to:
'AssociateTag'=>"(my-assigned-associate-tag)"
You MUST use the associate tag given to you by Amazon. I think associate tags usually end with the number '20', but I can't verify that. If you don't know your associate tag, login to your affiliate account here: Amazon affiliate page
...and it will be the 'Signed in as/Tracking ID' value in the upper left hand corner of the page.
signature error is mainly due to the mismatch of access key id and secret key id
please cross verify this ids.
also check the associateTag
I'm trying to create a bucket on GCS using API v1.0 (interoperable mode) in PHP but I'm getting a 'signature does not match' error response.
Here's what I'm doing:
$access_id = "GOOGxxxxxx";
$secret_key = "xyxyxyxyx/xyxyxyxyx";
$bucket = "random_bucket_name";
$url = 'https://'.$bucket.'commondatastorage.googleapis.com';
$timestamp = date("r");
$canonicalizedResources = "/ HTTP 1.1";
$stringToSign = utf8_encode("PUT "."\n"."\n"."\n".$canonicalizedResources);
$signature = base64_encode(hash_hmac("sha1",$stringToSign,$secret_key,true));
$authSignature = $access_id.":".$signature;
$headers = array('Host: '.$bucket.'.commondatastorage.googleapis.com',
'Date: '.$timestamp, 'x-goog-api-version: 1',
'x-goog-project-id: xxxyyyxy','Content-Length: 0',
'Authorization: GOOG1 '.$authSignature);
$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c,CURLOPT_HTTPHEADER,$headers);
$xml = curl_exec($c);
And here's the response that I get:
<?xml version='1.0' encoding='UTF-8'?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you
provided. Check your Google secret key and signing method.</Message>
<StringToSign>
GET
Sat, 03 Mar 2012 14:56:53 -0800
x-goog-api-version:1
x-goog-project-id:xxxyyyxy
/random_bucket_name/
</StringToSign>
</Error>
Any ideas where I'm going wrong?
Here's Google's documentation on this:
https://developers.google.com/storage/docs/reference-methods#putbucket
One thing I noticed is that even though I specify "PUT" in the "stringToSign" variable ... the response says that I used "GET" ... ?
Any help would be appreciated.
There are a few problems here:
Your canonicalized resource should be "/bucket/", not "/ HTTP 1.1".
You need to include your two custom headers (x-goog-version and x-goog-project-id) in the string to sign.
The string to sign must include the timestamp sent in the Date: header.
You need to set CURLOPT_PUT so that curl knows to send a PUT request, rather than the default GET request (that's why your error response alludes to a GET request).
Here's a corrected version of your code, which I tested and used to create a new bucket:
<?php
$access_id = "REDACTED";
$secret_key = "REDACTED";
$bucket = "your-bucket";
$url = 'https://'.$bucket.'commondatastorage.googleapis.com';
$timestamp = date("r");
$version_header = "x-goog-api-version:1";
$project_header = "x-goog-project-id:REDACTED";
$canonicalizedResources = "/".$bucket."/";
$stringToSign = utf8_encode("PUT\n\n\n".$timestamp."\n".$version_header."\n".$project_header."\n".$canonicalizedResources);
$signature = base64_encode(hash_hmac("sha1",$stringToSign,$secret_key,true));
$authSignature = $access_id.":".$signature;
$headers = array('Host: '.$bucket.'.commondatastorage.googleapis.com',
'Date: '.$timestamp, $version_header,
$project_header,'Content-Length: 0',
'Authorization: GOOG1 '.$authSignature);
$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c,CURLOPT_HTTPHEADER,$headers);
curl_setopt($c, CURLOPT_PUT, TRUE);
$xml = curl_exec($c);
print($xml);
?>
P.S. All the details on HMAC authentication for Google Cloud Storage are provided here: https://developers.google.com/storage/docs/reference/v1/developer-guidev1#authentication