1
votes

Im able to put a file (image.png) on to my Google Cloud Storage bucket using the google-api-php-client, but now im having trouble trying to create a signed url to get access to the file from my website. Sample code:

$bucketName = 'bucket-name';
$id = 'image.png';
$serviceAccountName = '[email protected]';
$privateKey = file_get_contents($location_to_key_file);
$signer = new \Google_P12Signer($privateKey, "notasecret");
$ttl = time() + 3600;
$stringToSign = "GET\n" . "\n" . "\n" . $ttl . "\n". '/' . $bucketName . '/' . $id;
$signature = $signer->sign(utf8_encode($stringToSign));
$finalSignature = \Google_Utils::urlSafeB64Encode($signature);
$host = "https://".$bucketName.".storage.googleapis.com";
echo  $host. "/".$id."?GoogleAccessId=" . $serviceAccountName . "&Expires=" . $ttl . "&Signature=" . $finalSignature;

Returns:

<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 1387590477 /bucketname/image.png</StringToSign></Error>

im using google-api-php-client with php 5.5

ive followed a few examples:

https://groups.google.com/forum/#!topic/gs-discussion/EjPRAWbWKbw

https://groups.google.com/forum/#!msg/google-api-php-client/jaRYDWdpteQ/xbNTLfDhUggJ

Maybe a config value im not passing correctly ? i assume the Service Account email should be used. Also tried to include md5hash and content-type in the $stringToSign, same results.

any help/tips would be appreciated.

4

4 Answers

4
votes

The rdb almost do the trick for me. I used a working python example from GoogleCloudPlatform for python in order to debug what was wrong with the url and find the following:

  • The GoogleAccessId have to be urlencoded
  • You've to replace in the Signature the following characters: '-' => '%2B', '_' => '%2F
  • The signature must end with '%3D'

Code:

$host. "/".$id."?Expires=" . $ttl . "&GoogleAccessId=" . 
        urlencode($serviceAccountName) . "&Signature=" . 
        str_replace(array('-','_',), array('%2B', '%2F'),urlencode($finalSignature)).'%3D';

Now the url should work and you can use some advanced operators like response-content-disposition or response-content-type

0
votes

Can you try constructing signed URL by using $host as -

$host = "https://".$bucketName.".commondatastorage.googleapis.com";

There is one difference I found with the doc you are referring.

Thanks

0
votes

can you try this codes :)

$finalSignature = base64_encode($signature);
echo $host. "/".$id."?GoogleAccessId=" . $serviceAccountName . "&Expires=" . $ttl . "&Signature=" . urlencode($finalSignature);
0
votes

I think your error is in the $finalSignature = \Google_Utils::urlSafeB64Encode($signature); line. This method does something weird with the URL and replaces certain characters.

In the end I got it all working with the following code:

$expires = time() + 60 * 30; // Half an hour

// Get the key from the key file
$privateKeyPath = Config::get('gcs.signing.key');
$privateKey = file_get_contents($privateKeyPath);
$signer = new Google_Signer_P12($privateKey, Config::get('gcs.signing.password'));

//Signing does not like spaces, however it also doesn't like urlencoding or html entities
$cloudStoragePath = str_replace(' ', '%20', $cloudStoragePath);

//Create string to sign
$stringToSign = "GET\n\n\n" . $expires . "\n" . "/" . $cloudStoragePath;

//Sign
$signature = $signer->sign(utf8_encode($stringToSign));

$query = array(
    'GoogleAccessId' => Config::get('gcs.signing.service_account'),
    'Expires' => $expires,
    'Signature' => base64_encode($signature)
);

$url = self::$storageBaseUrl . '/' . $cloudStoragePath . '?' . http_build_query($query);