Google 使用签名时云服务放置失败 URL
Google Cloud Services put Fails when Using Signed URL
我无法通过签名 URL 将文件 PUT 到 google 云服务。当我尝试从 JS 客户端执行 PUT 时,我得到:
"SignatureDoesNotMatch...The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method."
当我尝试使用 CURL post 文件时,我得到了同样的错误。
我使用的curl命令是:
#!/bin/bash
URL="https://storage.googleapis.com/..."
echo $URL
curl $URL -H "Content-Type: image/jpg" --upload-file b.jpg
我已经根据文档配置了我打算 post 数据的存储桶,我已经用密钥生成了一个服务帐户,这个密钥用于生成签名的 url。
我签署的请求格式为:
PUT
image/jpg
1234567890
my-bucket/b.jpg
设置和计算过期名称和存储桶名称的位置。
我有以下 Groovy 代码来生成带符号的 urls:
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);
}
感谢您提供的任何见解,我已经解决这个问题一段时间了。
调试签名 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 has a signUrl() method 而不是自己实现逻辑。
可能导致此错误的一个原因(我以前遇到过)是当您使用签名字符串生成 base64 编码签名时,编码签名可能包含非法 url 个字符 + 和 /。确保将字符串中的它们分别替换为 %2B 和 %2F。
我无法通过签名 URL 将文件 PUT 到 google 云服务。当我尝试从 JS 客户端执行 PUT 时,我得到:
"SignatureDoesNotMatch...The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method."
当我尝试使用 CURL post 文件时,我得到了同样的错误。
我使用的curl命令是:
#!/bin/bash
URL="https://storage.googleapis.com/..."
echo $URL
curl $URL -H "Content-Type: image/jpg" --upload-file b.jpg
我已经根据文档配置了我打算 post 数据的存储桶,我已经用密钥生成了一个服务帐户,这个密钥用于生成签名的 url。
我签署的请求格式为:
PUT
image/jpg
1234567890
my-bucket/b.jpg
设置和计算过期名称和存储桶名称的位置。
我有以下 Groovy 代码来生成带符号的 urls:
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);
}
感谢您提供的任何见解,我已经解决这个问题一段时间了。
调试签名 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 has a signUrl() method 而不是自己实现逻辑。
可能导致此错误的一个原因(我以前遇到过)是当您使用签名字符串生成 base64 编码签名时,编码签名可能包含非法 url 个字符 + 和 /。确保将字符串中的它们分别替换为 %2B 和 %2F。