How to perform HMAC-SHA1 with Base64 Encode? - php

I'm trying to setup an app to sign with my URLs so they may authenticate but I can't seem to figure out how to replicate the code that I'm trying from the following page: https://help.sendowl.com/help/signed-urls#example
order_id=12345&buyer_name=Test+Man&buyer_email=test%40test.com&product_id=123&signature=QpIEZjEmEMZV%2FHYtinoOj5bqAFw%3D
buyer_email=test#test.com&buyer_name=Test Man&order_id=12345&product_id=123
buyer_email=test#test.com&buyer_name=Test Man&order_id=12345&product_id=123&secret=t0ps3cr3t
publicStr&t0ps3cr3t
This is the steps:
First order the parameters (removing the signature) and unescape
them:
Next append your Signing secret:
Generate the key to sign with:
Perform the HMAC-SHA1 digest with Base 64 encode: QpIEZjEmEMZV/HYtinoOj5bqAFw=
The following is what I tried but end up not getting the same result:
$signKey = "t0ps3cr3t";
$signData = "buyer_email=test#test.com&buyer_name=Test Man&order_id=12345&product_id=123&secret=t0ps3cr3t";
$passData = hash_hmac("sha1", $signData, base64_decode(strtr($signKey)), true);
$passData = base64_encode($passData);
echo $passData;
I keep getting x8NXmAmkNBPYCXwtj65mdVJ8lPc=

I was able to replicate with the following: took me a bit to figure out something so simple.. been coding for 11 hours straight.
Thanks.
$data = "buyer_email=test#test.com&buyer_name=Test Man&order_id=12345&product_id=123&secret=t0ps3cr3t";
$key = "publicStr&t0ps3cr3t";
$pass1 = hash_hmac('sha1', $data, $key, true);
$pass = base64_encode($pass1);
echo $pass;
$pass will return "QpIEZjEmEMZV/HYtinoOj5bqAFw=", the correct value.

$current_timestamp = Carbon::now()->timestamp;
$signstring = "z001-line-anime-gif." . $current_timestamp . ".aaaabbbbccccdddd";
$secret = 'STG-7f*(:hsM-1_eQ_ZD175QgEoJhI$:oR.zEQ<z';
$sig = hash_hmac('sha1', $signstring, $secret);
$signature = hex2bin($sig);
$signature = base64_encode($signature);
return $signature;

Related

How to generate a signature for OAuth

I'm currently attempting to generate a signature to make API calls to quickbooks online, however I keep getting authentication errors. I'm sure the signature portion is where I'm going wrong. Is this incorrect:
//method to generate signature
//$this->method = "GET"
//QBO_SANDBOX_URL = 'https://some_url.com/'
//$this->_query = 'something=something'
public function generate_signature()
{
$base = $this->_method.'&'.rawurlencode($this->_url.QBO_SANDBOX_URL.'v3/company/'.$this->_realm_id).'&'
.rawurlencode("oauth_consumer_key=".rawurlencode($this->_consumer_key).'&'
.'&oauth_nonce='.rawurlencode('34604g54654y456546')
.'&oauth_signature_method='.rawurlencode('HMAC-SHA1')
.'&oauth_timestamp='.rawurlencode(time())
.'&oauth_token='.rawurlencode($this->_auth_token)
.'&oauth_version='.rawurlencode('1.0')
.'&'.rawurlencode($this->_query));
$key = rawurlencode($this->_consumer_secret.'&'.$this->_token_secret);
$this->_signature = base64_encode(hash_hmac("sha1", $base, $key, true));
}
Now when I go to send my request, here are the headers:
$this->_headers = array(
'Authorization: '.urlencode('OAuth oauth_token="'.$this->_auth_token.'",oauth_nonce="ea9ec8429b68d6b77cd5600adbbb0456",oauth_consumer_key="'.$this->_consumer_key.'",oauth_signature_method="HMAC-SHA1", oauth_timestamp="'.time().'", oauth_version ="1.0"oauth_signature="'.$this->_signature.'"').''
);
I get a 401 Authorization response. Am I signing incorrectly?
EDIT: All fields not included here (i.e $this->_auth_token) are set.
For anyone that might use this as a basis for their own integration, there is one other issue with the code originally posted:
$key = rawurlencode($this->_consumer_secret.'&'.$this->_token_secret);
should be
$key = rawurlencode($this->_consumer_secret).'&'.rawurlencode($this->_token_secret);
This issue was in the base string:
.rawurlencode("oauth_consumer_key=".rawurlencode($this->_consumer_key).'&'
.'&oauth_nonce='.rawurlencode('34604g54654y456546')
The & after the consumer key and once again before the oauth_nonce.
In the signature creation I think it lacks the call to rawurlencode():
$this->_signature = rawurlencode(base64_encode(hash_hmac("sha1", $base, $key, true)));
instead :
$this->_signature = base64_encode(hash_hmac("sha1", $base, $key, true));

SCRAM-SHA-1 auth by PHP

help me please with implementing semantic code from manual about SCRAM-SHA-1 authorization in XMPP server. So, we got:
clientFinalMessageBare = "c=biws,r=" .. serverNonce
saltedPassword = PBKDF2-SHA-1(normalizedPassword, salt, i)
clientKey = HMAC-SHA-1(saltedPassword, "Client Key")
storedKey = SHA-1(clientKey)
authMessage = initialMessage .. "," .. serverFirstMessage .. "," .. clientFinalMessageBare
clientSignature = HMAC-SHA-1(storedKey, authMessage)
clientProof = clientKey XOR clientSignature
clientFinalMessage = clientFinalMessageBare .. ",p=" .. base64(clientProof)
My PHP code:
$cfmb = 'c=biws,r='.$salt;
$saltpass = hash_pbkdf2('sha1', 'IDoMdGuFE9S0', $ps, $iter);
//hash_pbkdf2('sha1', 'IDoMdGuFE9S0', $salt, $iter, 0, true); maybe like that???
$ckey = hash_hmac('sha1', $saltpass, 'Client Key');
$sckey = sha1($ckey);
$authmsg = $im.','.$chal.','.$cfmb;
$csign = hash_hmac('sha1', $sckey, $authmsg);
$cproof = bin2hex(pack('H*',$ckey) ^ pack('H*',$csign));
$cfm = $cfmb.',p='.base64_encode($cproof);
Somewhere error (maybe ALL big error ;)) and I very need your help for correcting my code, maybe I am use wrong functions, or arguments in wrong positions? Because result - fail, server sends me that:
"The response provided by the client doesn't match the one we calculated."
PS: Sorry for my bad English ;)
First of all, it's very confusing to use $salt for the serverNonce and $ps for the salt.
But more importantly, you should take some care to keep track of whether the functions you use return binary data or hexadecimal encoded strings. hash_pbkdf2, sha1 and hash_hmac by default return hexadecimal encoded strings. You call pack('H*', ...) to decode them for the $cproof, but not when you calculate $ckey and $csign.
A much easier way is to compute binary data directly, by always passing $raw_data = TRUE:
$cfmb = 'c=biws,r='.$salt;
$saltpass = hash_pbkdf2('sha1', 'IDoMdGuFE9S0', $ps, $iter, 0, TRUE);
$ckey = hash_hmac('sha1', 'Client Key', $saltpass, TRUE);
$sckey = sha1($ckey, TRUE);
$authmsg = $im.','.$chal.','.$cfmb;
$csign = hash_hmac('sha1', $authmsg, $sckey, TRUE);
$cproof = $ckey ^ $csign;
$cfm = $cfmb.',p='.base64_encode($cproof);
Also, your hash_hmac calls were the wrong way around: first the data, then the key.

AWS S3 QueryString Authorization

Background
I am trying to create a signed url to fetch a resource from AWS S3. There are several examples in aws s3 documentation , and as a first step I am trying to replicate the last example on that page.
My code is :
$access_key = "AKIAIOSFODNN7EXAMPLE";
$secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
$string_to_sign = "GET\n\n\n1175139620\n/johnsmith/photos/puppy.jpg";
$encode = hash_hmac('sha1', utf8_encode($string_to_sign) , $secret_key) ;
echo base64_encode($encode). "\r\n" ;
The above code outputs
MzY5ODAyOGU3MGYzYWNjZjk2MTczYTA0MzU3OWE5MzQzNTJjNGE3Zg==
According to the example, the result should be
NpgCjnDzrM%2BWFzoENXmpNDUsSn8%3D&
I do understand that the result needs to be url encoded , but I think I am still far off. Can I get some help please ?
I found the solution
$encode = hash_hmac('sha1', utf8_encode($string_to_sign), $secret_key);
is replaced by
$encode = hash_hmac('sha1', utf8_encode($string_to_sign), $secret_key, true );
Final script to simulate the example is :
$access_key = "AKIAIOSFODNN7EXAMPLE";
$secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
$string_to_sign = "GET\n\n\n1175139620\n/johnsmith/photos/puppy.jpg";
$encode = hash_hmac('sha1', utf8_encode($string_to_sign), $secret_key , true ) ;
echo urlencode(base64_encode($encode)) . "\r\n" ;

How to properly generate a WSSE Password Digest in PHP

I'm looking at building a REST API in Symfony2, and in their Custom Authentication Provider they show how to build a WSSE authentication system, which should be fine for what I need to do. I'm going to start off by building and testing the API through cURL, so I need to be able to quickly generate the headers. I found a JS generator that showed the headers I would need.
From what I read, the Password Digest should be a base64 encoded SHA1 of the nonce, timestamp, and user password concatenated together in that order. I started with the following data:
$nonce = '4c5625ec7af5bdff';
$timestamp = '2013-04-03T04:46:19Z';
$password = 'mypass';
and generated the digest:
$digest = base64_encode(sha1($nonce.$timestamp.$password));
What I don't understand is that the $digest variable is now set to YTgxMDUzOWQzMDBiZmU1MmI2NWQ0YjYwNDc3ZmY5OWI3MmVlZTQyNA==, but the sample PasswordDigest from the JS generator comes up as qBBTnTAL/lK2XUtgR3/5m3Lu5CQ=. I must be missing a step somewhere, but I'm not sure what it is.
Looks like I need to use the binary SHA1 result, not the hex representation. My digest should look like this:
$digest = base64_encode(sha1($nonce.$timestamp.$password, true));
I know it's an old post, just tumbled on this post, recently I have developed API in symfony2 with WSSE authentication and this is how I generated full WSSE header with the help of below function:
public static function getWsseHeader($username, $apikey, $created, $nonce)
{
$digest = sha1($nonce.$created.$apikey, true);
return sprintf(
'X-WSSE: UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"',
$username,
base64_encode($digest),
base64_encode($nonce),
$created
);
}
//you can use this php code to generate the Digest password
<?php
date_default_timezone_set('UTC');
$t = microtime(true);
$micro = sprintf("%03d",($t - floor($t)) * 1000);
$date = new DateTime( date('Y-m-d H:i:s.'.$micro) );
echo $timestamp = $date->format("Y-m-d\TH:i:s").$micro . 'Z';
$nonce = mt_rand(10000000, 99999999);
echo $nounce = base64_encode($nonce);//we have to decode the nonce and then apply the formula on it and in xml we have to send the encoded nonce
$password = "AMADEUS"; //clear password
echo $passSHA = base64_encode(sha1($nonce . $timestamp . sha1($password, true), true));
?>

The request signature we calculated does not match AMAZON AWS PHP

I have looked at most samples of code based on this issue on stack overflow but I still cant get the request to work. I keep getting this error:
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
Here is my code:
$access_key = "ACCESS_KEY";
$associateTag = "AOSSOCIATE_TAG";
$secretkey = "SECRET_KEY";
$keywords = "harry%20potter";
$timestamp = gmdate("Y-m-d\TH:i:s\Z");
$operation = "AWSECommerceService";
function createSignature($operation,$timestamp,$secretkey){
$the_string=$operation.$timestamp;
return base64_encode(hash_hmac("sha256",$the_string,$secretkey,true));
}
$signature = createSignature ($operation,$timestamp,$secretkey);
$APIcall =
"http://ecs.amazonaws.com/onca/xml?".
"AWSAccessKeyId=$access_key&".
"AssociateTag=$associateTag&".
"BrowseNode=1000&".
"ItemPage=1&".
"Keywords=$keywords&".
"Operation=ItemSearch&".
"ResponseGroup=Medium&".
"SearchIndex=Books&".
"Service=AWSECommerceService&".
"Timestamp=$timestamp&".
"Version=2011-08-01&".
"Signature=$signature";
$response = simplexml_load_file($APIcall);
Can anyone help?
I had this issue long time and it worked for me with this code :
require_once 'Crypt/HMAC.php';
require_once 'HTTP/Request.php';
$keyId = "adasdasd";
$secretKey = "asdasdasdasdasd+";
function hex2b64($str) {
$raw = '';
for ($i=0; $i < strlen($str); $i+=2) {
$raw .= chr(hexdec(substr($str, $i, 2)));
}
return base64_encode($raw);
}
function constructSig($str) {
global $secretKey;
$str = utf8_encode($str);
$secretKey = utf8_encode($secretKey);
$hasher =& new Crypt_HMAC($secretKey, "sha1");
$signature = hex2b64($hasher->hash($str));
return ($signature);
}
$expire = time()+1000;
$resource = "/demo/files/clouds.jpg";
$date = gmdate("D, d M Y G:i:s T");
$mime = "image/jpeg";
$stringToSign = "PUT\n";
$stringToSign .= "\n";
$stringToSign .= "$mime\n";
$stringToSign .= "$date\n";
$stringToSign .= $resource;
$req =& new HTTP_Request("http://nameofmine.s3.amazonaws.com/files/clouds.jpg");
$req->setMethod("PUT");
$req->addHeader("Date",$date);
$req->addHeader("Authorization", "AWS " . $keyId . ":" . constructSig($stringToSign));
$req->addHeader("Content-Type",$mime);
$req->setBody(file_get_contents($file_path));
$req->sendRequest();
$responseCode = $req->getResponseCode();
$responseString = $req->getResponseBody();
echo $responseCode;
As you see you have to use Crypto, HTTP pear plugins
The function seems ok (it is the same as the one used in amazon AWS SDK) so make sure that there is no whitespace in front or after the copied key.
When I typed in my credentials by hand, I got the same error a couple of times.
Then I tried Console for Windows so I could copy/paste my credentials. This removed the error message. Either I sucked at typing, or sucked at reading.
Long story short: Don't type by hand, copy and past credentials to avoid typos.
EDIT:
My problem was when trying to add my credentials via EB CLIx3.

Categories