google 桶的签名 URL 与提供的签名不匹配

Signed URL for google bucket does not match signature provided

我很难使用 google 存储。

所以我想做一个 signed url, already have the client id (which is an email) and private key (as described here) 所以:

第 1 步:construct the string

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;
}

到目前为止一切顺利(我认为),输出看起来与示例相似,所以现在对字符串进行签名

第 2 步:signing the string 最初我使用 openssl_public_encrypt,在四处搜索后发现 google-api-php-client 有 Google_Signer_P12(实际使用的是openssl_sign),所以方法看起来像:

function googleSignString($certificatePath, $stringToSign) {
    return (new Google_Signer_P12(
        file_get_contents($certificatePath), 
        'notasecret'
    ))->sign($stringToSign);
}

我不确定这是否正确签名,最终构建最终 url

第 3 步:building the URL

function googleBuildSignedUrl($serviceEmail, $file, $expiration, $signature) {
    return "http://storage.googleapis.com{$file}"
    . "?GoogleAccessId={$serviceEmail}"
    . "&Expires={$expiration}"
    . "&Signature=" . urlencode($signature);
}

但在浏览器中打开 URL 将检索:

<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>GET 1437470250 /example/video.mp4</StringToSign>
</Error>

我在最终脚本中添加了一个 gist 以便于阅读

所以知道我做错了什么吗?

我找到了解决方案,我正在做的过期日期有一个错误:

$expiration = (new DateTime())->modify('+3h')->getTimestamp();

所以我已将 h 更改为 hours,这样它现在就可以工作了,例如:

$expiration = (new DateTime())->modify('+3hours')->getTimestamp();

但这并没有完全解决它,实际缺少的部分是 Google_Signer_P12::sign() 要求它在 base64 上编码,这在 google 文档中指定:

Google Cloud Storage expects the Base64 encoded signature in its APIs.

但是我(错误地)认为 Google_Signer_P12::sign() 已经这样做了,所以在我明白这是必需的之后,我将符号方法更改为:

function googleSignString($certificatePath, $stringToSign)
{
  return base64_encode((new Google_Signer_P12(
    file_get_contents($certificatePath),
    'notasecret'
  ))->sign($stringToSign));
}

它现在正在运行!!!

我还为任何想使用它的人更新了 gist:)