问题
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 );
回答1:
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;
}
回答2:
There is an API/module for getting signed URLs now.
module: https://www.npmjs.com/package/@google-cloud/storage
API docs: https://cloud.google.com/nodejs/docs/reference/storage/1.6.x/File#getSignedUrl
Example
var storage = require('@google-cloud/storage')();
var myBucket = storage.bucket('my-bucket');
var file = myBucket.file('my-file');
//-
// Generate a URL that allows temporary access to download your file.
//-
var request = require('request');
var config = {
action: 'read',
expires: '03-17-2025'
};
file.getSignedUrl(config, function(err, url) {
if (err) {
console.error(err);
return;
}
// The file is now available to read from this URL.
request(url, function(err, resp) {
// resp.statusCode = 200
});
});
回答3:
Assuming this question is to sign the CDN url backed by google bucket backend, here what works for me (code above did not work for me).
Opts and signing function calling:
const signUrlOptions = {
expires: '' + new Date().getTime() + 3600, // one hour
keyName: '_SIGNING_KEY_NAME_', // URL signing key name (the one one you created in the CDN backend bucket)
keyBase64: '_SIGNING_KEY_BASE64_', // the URL signing key base64 content (base64-encoded, 128-bit value, ~24 characters)
baseUrl: '_CDN_BASE_URL_' // your base CDN URL (can be IP http://123.... when dev env or https://cdn_dns_name or https dns name)
}
const signedUrl = signCdnUrl('demo.png', signedUrlOptions);
signing function:
import { createHmac } from 'crypto';
const BASE64_REPLACE = { '+': '-', '/': '_', '=': '' };
export function signCdnUrl(fileName, opts) {
// URL to sign
const urlToSign = `${opts.baseUrl}/${fileName}?Expires=${opts.expires}&KeyName=${opts.keyName}`;
// Compute signature
const keyBuffer = Buffer.from(opts.keyBase64, 'base64');
let signature = createHmac('sha1', keyBuffer).update(urlToSign).digest('base64');
signature = signature.replace(/[+/=]/g, c => (<any>BASE64_REPLACE)[c]); // might be a better way
// Add signature to urlToSign and return signedUrl
return urlToSign + `&Signature=${signature}`;
}
Hope this helps. Somehow google cloud doc does not have a nodejs example and the file.getSignedUrl() add confusion to the mix as it does not seem to be related to CDN URL signing.
Note:
Note: Probably want to move base64 -> buffer work to the caller as opts.keyBuffer
来源:https://stackoverflow.com/questions/20754279/creating-signed-urls-for-google-cloud-storage-using-nodejs