12
votes

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:

  • Registered a "Web Application" at Google Cloud Console.
  • Generated new private key using the "Generate New Key" at the "Certificate" section of the registered web application screen.
  • Created my string to be signed and sign it using the sample code provided by "How to Sign" section of the page (just minor tweaks to hardcode values for the Java main program). Update: Created a gist for GcsSigner.java.
  • Ensure that the signed string is URL encoded.
  • HTTP headers specified: x-goog-api-version, x-goog-project-id, Content-Type.
  • URL Parameters specified: GoogleAccessId, Signature, Expires.
  • Use Postman (a Chrome extension) to simulate PUT request.

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.

5
Could you post the code you're using to perform the signature?jterrace
@jterrace I've created a gist for the modified GcsSigner.java - gist.github.com/kctang/2cfdf66c7f11391ae11b.kctang
@kctang can you please post an example complete signed URL that is constructed with given code?Neelam Sharma

5 Answers

9
votes

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.

4
votes

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

2
votes

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"

1
votes

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

1
votes

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.