2016-08-27 28 views
3

我無法通過簽名URL將文件放入Google雲服務。當我嘗試從JS客戶端執行PUT時,得到:使用簽名URL時Google雲服務失敗

「SignatureDoesNotMatch ...我們計算的請求籤名與您提供的簽名不匹配。請檢查您的Google祕密密鑰和簽名方法。

當我嘗試使用CURL發佈文件時,出現同樣的錯誤。

捲曲命令我用的是:

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

我已經配置我打算數據發佈到基於文檔桶中,我已生成與密鑰服務帳戶,並且該鍵用於生成簽名的網址。

我註冊的請求的形式是:

PUT 

image/jpg 
1234567890 
my-bucket/b.jpg 

其中期滿和剷鬥的名稱被設置和計算。

我有以下Groovy代碼生成簽署的網址:

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/[email protected]&Expires=1391816302&Signature=A6QbgTA8cXZCtjy2xCr401bdi0e7zChTBQ6BX61L7AfytTGEQDMD%2BbvOQKjX7%2FsEh77cmzcSxOEKqTLUDbbkPgPqW3j8sGPSRX9VM58bgj1vt9yU8cRKoegFHXAqsATx2G5rc%2FvEliFp9UWMfVj5TaukqlBAVuzZWlyx0aQa9tCKXRtC9YcxORxG41RfiowA2kd8XBTQt4M9XTzpVyr5rVMzfr2LvtGf9UAJvlt8p6T6nThl2vy9%2FwBoPcMFaOWQcGTagwjyKWDcI1vQPIFQLGftAcv3QnGZxZTtg8pZW%2FIxRJrBhfFfcAc62hDKyaU2YssSMy%2FjUJynWx3TIiJjhg%3D%3D"""; 

    long expiration = 1391816302; 

    String signedUrl = gsUtils.generateSignedUrl(privateKey, "[email protected]","PUT", "", "", expiration, "test/test.txt") 

    assertEquals(expected, signedUrl); 
} 

感謝您提供的任何見解,我一直在解決這個問題。

+0

我想知道,如果您通過「gsutil signurl」命令直接生成簽名的URL,它是否仍然失敗? –

+0

嗨布蘭登,非常感謝你的幫助!使用gsutils生成URL會產生不同的錯誤:'<?xml version ='1.0'encoding ='UTF-8'?>AccessDenied訪問被拒絕。

調用方沒有storage.objects.create訪問bucket my-bucket。
' – scipio

+1

嗨布蘭登,使服務帳戶成爲桶的「所有者」解決了這個問題。所以,基本上,我可以使用gsutil簽署url並通過curl發佈,這太棒了!我正在通過下面的建議,這是一點點。這太棒了! – scipio

回答

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 library有一個signUrl() method,我建議您使用它而不是自己實現邏輯。這可能會導致此錯誤(前發生在我身上)

+0

嗨布蘭登,我從未發現問題的根源。我切換到了你所建議的API,而且事情看起來好多了。謝謝。 – scipio

0

一個原因是,當你生成帶有簽名的串base64編碼的簽名,該編碼的簽名可能含有非法字符的URL + /。請確保您分別用%2B%2F替換它們。

相關問題