使用签名网址时,Google云端服务会出现问题

时间:2016-08-27 22:30:41

标签: google-cloud-storage

我无法通过签名网址将文件投放到Google云端服务。当我尝试从JS客户端执行PUT时,我得到:

" SignatureDoesNotMatch ...我们计算的请求签名与您提供的签名不匹配。检查您的Google密钥和签名方法。"

当我尝试使用CURL发布文件时,我得到了同样的错误。

我使用的curl命令是:

#!/bin/bash 
URL="https://storage.googleapis.com/..."
echo $URL
curl $URL -H "Content-Type: image/jpg" --upload-file b.jpg

我已经根据文档配置了我打算发布数据的存储桶,我已经使用密钥生成了一个服务帐户,并且该密钥用于生成签名的URL。

我签署的请求的格式为:

PUT

image/jpg
1234567890
my-bucket/b.jpg

设置和计算到期和存储桶名称。

我有以下Groovy代码来生成签名的URL:

 public String sign(PrivateKey key, String toSign) {
    Signature signer = Signature.getInstance("SHA256withRSA");
    signer.initSign(key);
    signer.update(toSign.getBytes("UTF-8"));
    byte[] rawSignature = signer.sign();
    String s = new String(Base64.encodeBase64(rawSignature), "UTF-8");

    return s;
}

public String signUrl(PrivateKey key, String clientId, String method, String md5, String contentType,
                             long expiration, String gcsPath) {

    String toSign = "${method}\n${md5}\n${contentType}\n${expiration}\n${gcsPath}";
    String signature = sign(key, toSign);
    String url = java.net.URLEncoder.encode(signature);
    return url;
}

public String generateSignedUrl(PrivateKey key, String clientId, String method, String md5, String contentType,
                                       long expiration, String gcsPath) {

    String canonicalizedResource = "/${gcsPath}";
    String signature = signUrl(key, clientId, method, md5, contentType, expiration, canonicalizedResource);
    String finalUrl = "https://storage.googleapis.com/${gcsPath}?GoogleAccessId=${clientId}&Expires=${expiration}&Signature=${signature}"

    finalUrl
}

此代码附带以下传递单元测试直接从gsutils github项目(https://github.com/GoogleCloudPlatform/gsutil/blob/master/gslib/tests/test_signurl.py)中解除:

    @Test
void thatWeCanSignAPutUrlCorrectly() {
    String expected = """https://storage.googleapis.com/test/test.txt?GoogleAccessId=test@developer.gserviceaccount.com&Expires=1391816302&Signature=A6QbgTA8cXZCtjy2xCr401bdi0e7zChTBQ6BX61L7AfytTGEQDMD%2BbvOQKjX7%2FsEh77cmzcSxOEKqTLUDbbkPgPqW3j8sGPSRX9VM58bgj1vt9yU8cRKoegFHXAqsATx2G5rc%2FvEliFp9UWMfVj5TaukqlBAVuzZWlyx0aQa9tCKXRtC9YcxORxG41RfiowA2kd8XBTQt4M9XTzpVyr5rVMzfr2LvtGf9UAJvlt8p6T6nThl2vy9%2FwBoPcMFaOWQcGTagwjyKWDcI1vQPIFQLGftAcv3QnGZxZTtg8pZW%2FIxRJrBhfFfcAc62hDKyaU2YssSMy%2FjUJynWx3TIiJjhg%3D%3D""";

    long expiration = 1391816302;

    String signedUrl = gsUtils.generateSignedUrl(privateKey, "test@developer.gserviceaccount.com","PUT", "", "", expiration, "test/test.txt")

    assertEquals(expected, signedUrl);
}

感谢您提供的任何见解,我已经解决了这个问题。

2 个答案:

答案 0 :(得分:3)

调试签名的URL逻辑很困难。但是,有一个有用的技巧有所帮助。像您描述的那样的错误响应将如下所示:

<?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/jpeg
1472720161
/myBucket/test.txt</StringToSign></Error>

最后一点<StringToSign>是至关重要的。那里的字符串恰好是GCS将签名的字符串,它也是你应该签名的字符串。比较你的字符串与这个;它可能会在某种程度上有所不同。

另外,由于实现此签名逻辑很棘手,gcloud-java librarysignUrl() method我建议您使用而不是自己实现逻辑。

答案 1 :(得分:1)

可能导致此错误的一个原因(之前发生在我之前)是当您使用签名字符串生成base64编码签名时,编码签名可能包含非法网址字符 + / 即可。确保分别用%2B %2F 替换字符串。