Search code examples
node.jsgoogle-cloud-storagesha

Creating signed URLs for Google Cloud Storage using NodeJS


I'm trying to create a signature for a privately stored file in Google Cloud Storage; so that I can distribute a time-limited link.

Currently doing this and it makes a signature that's too short ... where am I going wrong?

var crypto = require("crypto");

var ttl = new Date().getTime() + 3600;
var id = 'the_target_file.txt';
var bucketName = 'bucket_name';
var POLICY_JSON = "GET\n" + "\n" + "\n" + ttl + "\n" + '/' + bucketName + '/' + id;

// stringify and encode the policy
var stringPolicy = JSON.stringify(POLICY_JSON);
var base64Policy = Buffer(stringPolicy, "utf-8").toString("base64");

// sign the base64 encoded policy
var privateKey = "MY_PRIVATE_KEY";
var sha256 = crypto.createHmac("sha256", privateKey);
var signature = sha256.update(new Buffer(base64Policy, "utf-8")).digest("base64");

console.log ( signature );

Solution

  • Realised what I was doing wrong ... I was hashing the policy string instead of signing it. The below code now gives me the correct output.

    var crypto = require("crypto");
    var fs = require("fs");
    
    var expiry = new Date().getTime() + 3600;
    var key = 'the_target_file';
    var bucketName = 'bucket_name';
    var accessId = 'my_access_id';
    var stringPolicy = "GET\n" + "\n" + "\n" + expiry + "\n" + '/' + bucketName + '/' + key; 
    var privateKey = fs.readFileSync("gcs.pem","utf8");
    var signature = encodeURIComponent(crypto.createSign('sha256').update(stringPolicy).sign(privateKey,"base64"));   
    var signedUrl = "https://" + bucketName + ".commondatastorage.googleapis.com/" + key +"?GoogleAccessId=" + accessId + "&Expires=" + expiry + "&Signature=" + signature;
    
    console.log(signedUrl);
    

    For completeness ... here is a PHP version that does the same thing, which I used to check my results

    $expiry = time() + 3600;
    $key = 'the_target_file';
    $bucketName = 'bucket_name';
    $accessId = 'my_access_id';
    $stringPolicy = "GET\n\n\n".$expiry."\n/".$bucketName."/".$key;
    $fp = fopen('gcs.pem', 'r');
    $priv_key = fread($fp, 8192);
    fclose($fp);
    $pkeyid = openssl_get_privatekey($priv_key,"password"); 
    if (openssl_sign( $stringPolicy, $signature, $pkeyid, 'sha256' )) {
        $signature = urlencode( base64_encode( $signature ) );    
        echo 'https://'.$bucketName.'.commondatastorage.googleapis.com/'.
                  $key.'?GoogleAccessId='.$accessId.'&Expires='.$expiry.'&Signature='.$signature;
    }