kctang
kctang

Reputation: 11192

PUT files to Google Cloud Storage (GCS) via Signed URLs

I am trying to use "signed URLs" to access/upload files to Google Cloud Storage (GCS).

Followed the instructions at https://developers.google.com/storage/docs/accesscontrol#Signing-Strings

What I did:

However, I am still getting this (Status: 403 Forbidden):

<?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

text/plain
1384084959392
x-goog-api-version:2
x-goog-project-id:99999
/mybucket/myfile.txt</StringToSign>
</Error>

Even when I retry by signing the string based on values in "StringToSign", I still get the same error.

Read various related postings here but could not find any solution and most of it refers to x-goog-api-version:1 while i am using version 2.

What am i missing? Any help would be greatly appreciated.

Upvotes: 13

Views: 13664

Answers (5)

Micro
Micro

Reputation: 10891

I was struggling with PUT and the signed URL (with GAE Cloud Endpoints) but here are the two things I needed to do:

  1. Make sure you have the latest GAE gradle dependencies. The auto generated ones but android studio are not always the latest.
  2. For PUT you need to have your string to sign look like this:

    String url_signature = sign(verb + "\n" + contentMD5 + "\n" + contentType + "\n" + expiration + "\n" + "/" + BUCKET_NAME + "/" + objectName );

As explained here: https://cloud.google.com/storage/docs/access-control/signed-urls the format is:

StringToSign = HTTP_Verb + "\n" +
               Content_MD5 + "\n" +
               Content_Type + "\n" +
               Expiration + "\n" +
               Canonicalized_Extension_Headers +
               Canonicalized_Resource

Those return "\n" are important. If you have too many or not enough, you will get that error. If you are not passing a contentMD5, for example, just pass in an empty string so you get the right number of return "\n"

Upvotes: 2

abishkar bhattarai
abishkar bhattarai

Reputation: 7641

To run signed url from browser you have to set HTTP header . In https://developers.google.com/storage/docs/accesscontrol#Construct-the-String

Content_Type Optional. If you provide this value the client (browser) must provide this HTTP header set to the same value.There is a word must

So if you are providing Content_Type for sign string you must provide same Content_Type in browser http header.I was also running from the same problem when i set Content_Type in browser header i was able to download a document.

Upvotes: 1

gaga5lala
gaga5lala

Reputation: 1228

Though the doc says Content-Type is optional, it actually means you must set correct content-Type correspond to your http request header.

In this case, your must add content-type: text/plain in the signing string.

https://cloud.google.com/storage/docs/access-control/create-signed-urls-program

Upvotes: 5

kctang
kctang

Reputation: 11192

Finally managed to PUT files to Google Cloud Storage using signed URLs. This was done by creating a simple Java program to simulate:

  • Server to sign & encode a string as signature.
  • Uploader as an unauthenticated user submitting the PUT request using only the signature provided by Server. Browser is simulated using Apache's HTTP Client library.

You can see the demo app here.

I do not really understand why it did not work when I submitted through Chrome's Postman extension.

Upvotes: 10

Neelam Sharma
Neelam Sharma

Reputation: 2855

On looking into your code, can you please try creating stringToSign by omitting following headers -

"x-goog-api-version:2\n" +
"x-goog-project-id:1234\n" +

As shown below -

 String stringToSign = "PUT\n" +
                "\n" +
                "text/plain\n" +
                "1384084959392\n" +
                "/test-bucket/bob.txt";

Now try generating Sign URL.

Thanks

Upvotes: 1

Related Questions