Last active
June 19, 2016 11:46
-
-
Save mloureiro/14503293c3cb2191e2f6 to your computer and use it in GitHub Desktop.
Generate a Google Signed URL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<? | |
/** | |
* Google Bucket GET file with signed string example | |
*/ | |
require_once("vendor/autoload.php"); | |
/* | |
* Helper Methods | |
*/ | |
function googleBuildConfigurationString($method, $expiration, $file, array $options = []) | |
{ | |
$allowedMethods = ['GET', 'HEAD', 'PUT', 'DELETE']; | |
// initialize | |
$method = strtoupper($method); | |
$contentType = $options['Content_Type']; | |
$contentMd5 = $options['Content_MD5'] | |
? base64_encode($options['Content_MD5']) | |
: ''; | |
$headers = $options['Canonicalized_Extension_Headers'] | |
? $options['Canonicalized_Extension_Headers'] . PHP_EOL | |
: ''; | |
$file = $file ? $file : $options['Canonicalized_Resource']; | |
// validate | |
if(array_search($method, $allowedMethods) === false) | |
{ | |
throw new RuntimeException("Method '{$method}' is not allowed"); | |
} | |
if(!$expiration) | |
{ | |
throw new RuntimeException("An expiration date should be provided."); | |
} | |
return <<<TXT | |
{$method} | |
{$contentMd5} | |
{$contentType} | |
{$expiration} | |
{$headers}{$file} | |
TXT; | |
} | |
function googleSignString($p12FilePath, $string) | |
{ | |
$certs = []; | |
if (!openssl_pkcs12_read(file_get_contents($p12FilePath), $certs, 'notasecret')) | |
{ | |
echo "Unable to parse the p12 file. OpenSSL error: " . openssl_error_string(); exit(); | |
} | |
$RSAPrivateKey = openssl_pkey_get_private($certs["pkey"]); | |
$signed = ''; | |
if(!openssl_sign( $string, $signed, $RSAPrivateKey, 'sha256' )) | |
{ | |
error_log( 'openssl_sign failed!' ); | |
$signed = 'failed'; | |
} | |
else | |
{ | |
$signed = base64_encode($signed); | |
} | |
return $signed; | |
} | |
function googleBuildSignedUrl($serviceEmail, $file, $expiration, $signature) | |
{ | |
return "http://storage.googleapis.com{$file}" | |
. "?GoogleAccessId={$serviceEmail}" | |
. "&Expires={$expiration}" | |
. "&Signature=" . urlencode($signature); | |
} | |
/* | |
* Initialization | |
*/ | |
$serviceEmail = '<service-email>'; | |
$p12FilePath = 'keys/google.certificate.p12'; | |
$expiration = (new DateTime())->modify('+3hours')->getTimestamp(); | |
$bucket = 'example'; | |
$fileToGet = 'video.mp4'; | |
/* | |
* Building! | |
*/ | |
$file = "/{$bucket}/{$fileToGet}"; | |
$string = googleBuildConfigurationString('GET', $expiration, $file); | |
$signedString = googleSignString($p12FilePath, $string); | |
$signedUrl = googleBuildSignedUrl($serviceEmail, $file, $expiration, $signedString); | |
?> | |
<html> | |
<body> | |
<style>pre {background: #eee;}</style> | |
<h2>String</h2> | |
<pre><?= $string ?></pre> | |
<br> | |
<h2>Final Url</h2> | |
<pre><?= $signedUrl ?></pre> | |
<br> | |
<video controls> | |
<source src="<?= $signedUrl ?>" type="video/mp4"> | |
</video> | |
</body> | |
</html> |
Each variable on the return string represents a line (or multiple in case of headers).
The required ones are method (called HTTP_VERB on goolge), expiration and the file.
So that will return a string similar to the examples at Sign Urls#Construct-the-String
For instance:
PUT
rmYdCNHKFXam78uCt7xQLw==
text/plain
1388534400
x-goog-acl:public-read
x-goog-meta-foo:bar,baz
/bucket/objectname
Are you positive this works? I'm getting a 'SignatureDoesNotMatch' error.. And my although it looks like it signed the url and creates a correct signed string.
Edit: The problem seemed to be with the way the signString was constructed. It might be a problem isolated to me but sometimes i had one too few linebreaks between GET and the expire date :/. I resorted to build it myself
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for writing this up. I'm trying to figure this part out but this is kinda confusing. I've never seen anything like this. Care to elaborate?