Recently I have used same code on PHP and it's working fine but when I tried Node Js, it's not working for me. Please check once:
PHP
$signature = $ACCID . "POST" . strtolower(urlencode($url)).$requestContentBase64String;
$hmacsignature = base64_encode(hash_hmac("sha256", $signature, base64_decode($APIKey), true));
NODE CODE :
var signature = ACCID+"POST"+encodeURI(url).toLowerCase()+requestContentBase64String;
var hmacsignature = base64.encode(crypto.createHmac('sha256', APIKey).update(signature).digest('base64'))
Please check what's wrong in this code.
Your second line has two mistakes:
PHP APIKey is BASE64_DECODE(APIKey) while in Node.js code it is just APIKey
PHP hmacsignature is BASE64(HEX(HMAC)) while in Node.js code it is BASE64(BASE64(HMAC))
Try this:
var hmacsignature = crypto.createHmac('sha256', Buffer.from(APIKey, 'base64')).update(signature).digest('base64')
Related
I have a work to do and I need to login to the webpage, and extract content from it.
The query has to be made of a username and an access key.
The problem is that I don't really know how to make a query with thos 2 elements, and in PHP.
So, I have found this code have this code :
$ak = "accesskey";
$username = 'admin';
$password = '123';
$remote_url = 'http://192.168.1.78/index.php';
// Create a stream
$opts = array(
'http'=>array(
'method'=>"GET",
'header' => "Authorization: Basic " . base64_encode("$username:$ak")
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents($remote_url, false, $context);
print($file);
But the output is simply the Webpage code, from the <!DOCTYPE html><html> to </html>
According to the webpage REST API, the result that I need to get is this :
{
success: true,
result: json_result
}
Any idea why it doesn't work ?
PS : here is the API documentation : https://www.vtiger.com/docs/rest-api-for-vtiger#/Authentication
The vTiger API (in 7.2 at least) has an odd combination of parameter and form POST payloads. Here is the Python script I used to get started. Note that it uses param in the getchallenge hit but data in the login hit:
import requests
from hashlib import md5
import urllib3
urllib3.disable_warnings() # shut up whining about that security stuff
encoding = 'ascii'
userName = 'Michael'
userToken = (access key from my preferences page)
APIURL='https://our.crm.instance/webservice.php'
session = requests.Session()
def getSession(session, name, token):
response = session.get(APIURL, params={'operation':'getchallenge', 'username':userName}, verify=False)
token_key = response.json()['result']['token']
combined = token_key + userToken
accessKey = md5(combined.encode(encoding)).hexdigest()
response1 = session.post(APIURL,
data={'operation':'login', 'username':userName, 'accessKey':accessKey},
verify=False)
return response1.json()['result']['sessionName']
sessionToken = getSession(session, userName, userToken)
types = session.get(APIURL, params={'operation':'listtypes', 'sessionName':sessionToken})
print(types.json())
I've just started to upgrade my Google Cloud Storage code from API version 1.0 to version 2.0 and I'm having some troubles.
With version 1.0 I used Signed URLs with great success, using .p12 files. However that's deprecated in the new version and I have to use Firebase/php-jwt instead, using JSON files.
The problem is that it's just not working, I get the error:
<?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>PUT
image/png
1483626991
/myBucket/folder/test.PNG</StringToSign></Error>
This is the simplified code used to sign it.
$string = ($method . "\n" .
$contentMd5 . "\n" .
$contentType . "\n" .
$expiration . "\n" .
$file);
$signedURL = base64_encode(Firebase\JWT\JWT::encode($string,
file_get_contents($credentialsFilePath)));
After the signedURL is received I build an URL with the correct data. The only part I've changed from 1.0 and 2.0 is the part where you sign the URL. Furthermore I've checked that the string in "StringToSign"-field of the response is exactly the same as the one I'm signing.
In version 1.0 I signed the URL like this:
$signedURL = base64_encode((new Google_Signer_P12(
file_get_contents($p12FilePath),
'notasecret'
))->sign($string));
All of this leads me to believe that I'm singing the correct contents but using the JWT function the wrong way. Has anyone else done this? How did yo do it?
In case it's interesting this is the URL I build (works with 1.0):
$returnArr['url'] = "https://{$bucket}.commondatastorage.googleapis.com/"
. $prefix . '/' . rawurlencode($file)
. "?GoogleAccessId=" . rawurlencode($serviceEmail)
. "&Expires={$expiration}"
. "&Signature=" . rawurlencode($signature);
Looking at the source for that JWT library the first thing that jumps out at me, and I see was noted in comments, is that your payload should be an array or object, not a string... "JSON web tokens".
* #param object|array $payload PHP object or array
public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
Second, it looks like you are double base64 encoding it... base128? :)
The return value of encode should be the three Base64url strings concatenated together, so you shouldn't need to do it again.
I'd give this a try:
$payload = ['HTTP_Verb' => $method,
'Content_MD5' => $contentMd5,
'Content_Type' => $contentType,
'Expiration' => $expiration,
'Canonicalized_Resource' => $file];
$key = file_get_contents($credentialsFilePath);
$signedURL = Firebase\JWT\JWT::encode($payload, $key); //think base64_encode here is redundant.
Ref: Overview of Signed URLs page. They sure don't explain things very well in those docs.
I assume you've looked at SDK?
If you wanted to go the string route you would need to sign using RSA signatures with SHA256... opensssl_sign or also maybe easier to lean on Google's PHP SDKs?
Later...
OK, decided to test it. Saw Google Cloud had a free trial. Installed gsutil, read a bunch of docs. Damned if I understand this JWT approach though. Share if anyone can even provide the docs on that topic.
This code works:
<?php
$method = 'GET';
$expires = '1503532674';
$container = '/example-bucket/cat.jpeg';
$payload = "{$method}\n\n\n{$expires}\n{$container}";
//assume you have this 'json' formatted key too? Otherwise just load the private key file as is.
$key = file_get_contents('~/oas_private_key.json');
$key = json_decode($key, true);
$key = $key['private_key'];
//if sucessful the encypted string is assigned to $signature
openssl_sign($payload, $signature, $key, OPENSSL_ALGO_SHA256);
$signature = urlencode(base64_encode($signature));
die("https://storage.googleapis.com/{$container}?GoogleAccessId=oastest#foo.iam.gserviceaccount.com&Expires={$expires}&Signature={$signature}");
Finally no "SignatureDoesNotMatch" error! Personally I'd use the SDK. Little bit of init and you can just do something like the following:
$url = $object->signedUrl(new Timestamp(new DateTime('tomorrow')), [
'method' => 'PUT'
]);
It would also make upgrades easier in the future.
everyone!
I have some PHP code to sign some text and it works fine. I need to have equivalent of this code on actionscript 3. I need your help.
$privateKeyPath = "private.key";
$message = "hello";
$privateKey = file_get_contents($privateKeyPath);
openssl_sign($message, $signature, $privateKey);
echo base64_encode($signature);
In AS3 I using as3crypto library to make sign:
private function readPrivateKey():String {
var f:File = new File("/Users/ivan/Desktop/private.key");
var fs:FileStream = new FileStream();
fs.open(f,FileMode.READ);
var key:String = fs.readUTFBytes(fs.bytesAvailable);
fs.close();
return key;
}
private function getSign():void {
var message:String = "hello";
var privateKey:String = readPrivateKey();
var srcBA:ByteArray = new ByteArray();
var resultBA:ByteArray = new ByteArray();
var rsaKey:RSAKey;
var base64encoder:Base64Encoder = new Base64Encoder();
srcBA.writeUTFBytes(message);
rsaKey = PEM.readRSAPrivateKey(privateKey);
rsaKey.sign(srcBA, resultBA, srcBA.length);
b64encoder.encodeBytes(resultBA);
trace(b64encoder.toString());
}
I have same private key file. I expect that the output values are equals. But these values are different =(
What am I doing wrong?
UPDATE: I tried to verify my encoded base64 string using public key and verify method - everything is ok inside Actionscript.
Example:
var text:String = "hello";
var srcBA:ByteArray;
var desBA:ByteArray;
var rsaKey:RSAKey;
var encodedB64:String;
// ENCODING
srcBA = new ByteArray();
srcBA.writeUTFBytes(text);
desBA = new ByteArray();
rsaKey = PEM.readRSAPrivateKey( readPrivateKey() );
rsaKey.sign(srcBA, desBA, srcBA.length);
encodedB64 = Base64.encodeByteArray(desBA);
trace("Original: " + text);
trace("Encoded: " + encodedB64 );
// DECODING
var srcBA2:ByteArray = new ByteArray();
var desBA2:ByteArray = new ByteArray();
var rsaKey2:RSAKey = PEM.readRSAPublicKey( readPublicKey() );
srcBA2 = Base64.decodeToByteArray( encodedB64 );
rsaKey2.verify(srcBA2, desBA2, srcBA2.length);
trace("Decoded: " + desBA2.toString() );
My original text and decoded value are equals. So, I conclude that AS3 signing methods are different than PHP.
Is anyone have idea to make it equals?
Thanks.
Maybe it's late answer, but anyway...
AS3 works fine in your second code, PHP needs some tweaks, like this:
$privateKeyPath = "private.key";
$message = "hello";
$privateKey = openssl_pkey_get_private(file_get_contents($privateKeyPath));
openssl_private_encrypt($message, $signature, $privateKey);
echo base64_encode($signature);
I just checked with key genereted on this site:
http://www.selfsignedcertificate.com/ and everything works fine, I'm getting similar results in both PHP and AS3 versions.
I have been trying to convert a php script to a python3 script and have been failing to send through a successful signature after troubleshooting through the issue.
The below PHP example code successfully goes through so I've copied the sent timestamp and signature to try and replicate it in my python3 code.
PHP Code
<?
$nonce = "2015-10-26 04:53:49 EDT";
$secret = "mkdaklmdflfkdsmaflkdmsfdkasmfdsmflks";
$req = array();
$req['t'] = $nonce;
$req['secret'] = $secret;
$post_data = json_encode($req);
$post_data = bin2hex($post_data);
$sign = hash_hmac("sha256", $post_data, $secret);
print($sign);
?>
Python 3 Code
import json
import hmac
import binascii
import hashlib
nonce = "2015-10-26 04:53:49 EDT"
secret = "mkdaklmdflfkdsmaflkdmsfdkasmfdsmflks"
payload = {}
payload["t"] = nonce
payload["secret"] = secret
payload_json = json.dumps(payload)
post_data = binascii.b2a_hex(payload_json.encode('utf-8'))
sign = hmac.new(bytes(secret, "utf-8"), post_data, hashlib.sha256).hexdigest()
print(sign)
Based on the successful request using PHP my signature should be:
'c30ddc5878ff7b1a9b1c9078ccbdc38afef25ed510681a3d3bfc56f6c9e2f26a'
Instead I'm getting:
'f9fc8749389137252e7f207468d88a5c871110403ec533496720efd715541ec2'
Any help troubleshooting this issue would be greatly appreciated as I'm new to Python.
Your problem is that by default, Python inserts spaces into the JSON string. Output of the PHP json_encode($req):
{"t":"2015-10-26 04:53:49 EDT","secret":"mkdaklmdflfkdsmaflkdmsfdkasmfdsmflks"}
Output of the Python json.dumps(payload):
{"t": "2015-10-26 04:53:49 EDT", "secret": "mkdaklmdflfkdsmaflkdmsfdkasmfdsmflks"}
You can fix this by specifying separators, i.e.:
json.dumps(payload, separators=(',', ':'))
Also, you can control the order of the elements by using OrderedDict instead of a plain dictionary:
from collections import OrderedDict
payload = OrderedDict()
I have the PHP application integrated with Dropbox API. It worked good but since some time ago it stopped to work with non-latin symbols.
For example, if i try to create a dropbox folder with the API and use Cyrillic letters in folder name then API requests fail with error {"error": "Unauthorized"}
I have the PHP function
function createOAuthSignature($apiurl, $params, $oauth_secret, $oauth_token_secret, $method='GET'){
$urlencoded_apiurl = urlencode($apiurl);
$urlencoded_params = urlencode($params);
$basestring = $method .'&'.$urlencoded_apiurl.'&'.$urlencoded_params;
$oauth_signature = base64_encode(hash_hmac('sha1', $basestring, $oauth_secret.'&'.$oauth_token_secret, true));
$oauth_signature = urlencode($oauth_signature);
return $oauth_signature;
}
And the calling code is
$fullname = $parent . $foldername;
$fullnameEnc = rawurlencode($fullname);
$apiurl = $this->api_url .'fileops/create_folder/';
$params = 'oauth_consumer_key='.$this->oauth_consumer_key
.'&oauth_nonce='. $this->createNonce()
.'&oauth_signature_method=HMAC-SHA1'
.'&oauth_timestamp='. time()
.'&oauth_token='.$oauth_token
.'&oauth_version=1.0'
.'&path='. $fullnameEnc
.'&root=dropbox';
$oauth_signature = $this->createOAuthSignature($apiurl, $params, $this->oauth_secret, $oauth_token_secret);
$params .= '&oauth_signature='.$oauth_signature;
$action = $apiurl .'?'. $params;
$s = $this->curl->get($action);
What can be the problem there? I presume it is related to signature generating. But what is wrong there? This code worked fine just couple weeks ago.
Thanks
As Greg mentioned in a comment above, we're investigating, but if you want to switch to PLAINTEXT signing, it's quite easy. Change createOAuthSignature to just do this:
function createOAuthSignature($apiurl, $params, $oauth_secret, $oauth_token_secret, $method='GET'){
return urlencode($oauth_secret.'&'.$oauth_token_secret);
}
And in the calling code, change the signature method to PLAINTEXT:
$params = ...
.'&oauth_signature_method=PLAINTEXT'
...