0

I've been trying to perform file uploads to GCE via POST with an HTML form although so far I keep on getting the following error message:

The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.

Here's the sample code I have in a file called gcs.php:

 <?php
       $bucketName = “BUCKET_NAME";
       $accessId = “ACCESS_ID";

function generateGcsSignature($bucketName, $filePath, $params=array()){

         if($params['ttl'] < 300) {
          $params['ttl'] = 300; // 5 mins
        }

        $expiry = time() + $params['ttl'];

        $valid_methods = array("PUT","GET", "POST");
        $method = "";
        if(!isset($params["method"]) || !in_array($params['method'], $valid_methods))
           $method = "GET";
        else
            $method = $params["method"];

        $parts = array($params['method'], "", "", $expiry, "/$bucketName/$filePath");
        $stringPolicy = implode("\n", $parts);

        $fp = fopen("../gcs-key.pem", "r");
        $priv_key = fread($fp, 8192);
        fclose($fp);
        $pkeyid = openssl_get_privatekey($priv_key, "");

        $signSuccess = openssl_sign($stringPolicy, $signature, $pkeyid, 'sha256' );
        $openssl_error = openssl_error_string();

        if ($signSuccess) {
           $signature = urlencode( base64_encode( $signature ) );
           global $accessId;
           return $signature;
        }

}

function generatePolicy(){

         $policy = '
         {"expiration": "'.date("c", time()+300).'",
           "conditions": [
                ["starts-with", "key", "" ],
                {"acl": "bucket-owner-read" },
                {"bucket": "BUCKET_NAME"},
                {"success_action_redirect": "http://www.example.com/success_notification.html" },
                ["eq", "Content-Type", "image/jpeg" ],
                ["content-length-range", 0, 1000000]
           ]
         }';

         $policy = base64_encode(trim($policy));

         $fp = fopen("../gcs-key.pem", "r");
        $priv_key = fread($fp, 8192);
        fclose($fp);
        $pkeyid = openssl_get_privatekey($priv_key, "");

        $signSuccess = openssl_sign($policy, $signature, $pkeyid, 'sha256' );
        $openssl_error = openssl_error_string();

        if ($signSuccess) {
           $signature = urlencode( base64_encode( $signature ) );
           global $accessId;
           return $signature;
        }

}


       $signature = generateGcsSignature($bucketName, $key, array("ttl" => "300", "method" => "POST"));
       $policy = generatePolicy();
    ?>
    <form action="http://<?=$bucketName?>.storage.googleapis.com" method="post" enctype="multipart/form-data">
    <input type="text" name="key" value="asdfasdf.jpg">
    <input type="hidden" name="bucket" value="<?=$bucketName?>">
    <input type="hidden" name="Content-Type" value="image/jpeg">
    <input type="hidden" name="GoogleAccessId" value="<?=$accessId?>">
    <input type="hidden" name="acl" value="bucket-owner-read">
    <input type="hidden" name="success_action_redirect" value="http://www.example.com/success_notification.html">
    <input type="hidden" name="policy" value="<?=$policy?>"/>
    <input type="hidden" name="signature" value="<?=$signature?>">

    <input name="file" type="file">
    <input type="submit" value="Upload">
    </form>

Please note that the BUCKET_NAME and ACCESS_ID are set to their proper values in my code, I can confirm this as I've been able to perform a GET on files within my specified bucket.

I've been looking around for a solution to this issue of mine but with no luck so far, so I figured I'd post my code here in hopes that someone can point out what I'm doing incorrectly. I would appreciate any help I can get!

hartfordfive
  • 1,275
  • 2
  • 11
  • 18

1 Answers1

0

Signature parameters used with POSTing forms do not share the common HTTP_verb + MD5 + type + Expiration + etc pattern that GET and PUT requests do.

Instead, when using a POST request with a policy document, you sign the policy document and only the policy document. See https://developers.google.com/storage/docs/reference-methods#policydocument and scroll down to the bit that starts "The following pseudocode combines the steps above to create your signature".

The steps are:

Policy = Base-64-Encoding-Of(PolicyDocument)
MessageDigest = SHA256withRSA(SecretKey, Policy)
Signature = Base64-Encoding-Of(MessageDigest)

Good question. Sorry for the confusion!

Brandon Yarbrough
  • 37,021
  • 23
  • 116
  • 145
  • I'm a bit confused because the formfield section [link](https://developers.google.com/storage/docs/reference-methods#formfields) mentions this: _The signature of the Base64-encoded policy document you are including in the request. Your signature must be signed with the secret key associated with the GoogleAccessID you specified. (This is the email form of the client ID, as described in the section on Signed URLs)._ So if I only sign the policy as you mentioned, then what does the signature in that form consist of? – hartfordfive Jul 23 '14 at 13:05
  • The signature only contains the policy document. Since the HTML form is visible to everybody, signing the policy document provides a guarantee that the constraint you set in the policy cannot be tempered with by the users. – Zhihong Yao Jul 23 '14 at 21:33