問題描述
從https://github.com/bji/libs3下載C版libs3庫源碼,編譯安裝(參見libs3-c:編譯安裝和基礎命令操作)后哎榴,put和get命令都能正確使用,執(zhí)行gqs命令生成對象URL涨缚,瀏覽器訪問該URL時報“SignatureDoesNotMatch”錯誤家制,如下:
分析思路
從提示看是url的簽名有誤導致服務端拒絕,大致思路為
1不同、分析源代碼厉膀,梳理gqs命令的執(zhí)行流程;
2二拐、從亞馬遜官網(wǎng)找到S3的
Authenticating Requests: Using Query Parameters (AWS Signature Version 4)章節(jié)服鹅,分析URL生成標準流程和參考example;
3百新、查找當前gqs流程與標準流程的差異點企软,解決差異并試驗。
S3官網(wǎng)中該文檔有幾處重點:
1饭望、提供了一個標準的預簽名URL仗哨,方便對照差異形庭;
2、提示換行符只是方便閱讀實際字符串是連續(xù)的厌漂,“/”也是方便閱讀實際是%2F萨醒;
3、對URL中每項都進行了詳細解釋桩卵;
4验靡、給出了計算簽名(Calculating Signature)的流程和詳細示例。
試驗流程
打開Signature調(diào)試開關雏节,重新編譯安裝胜嗓,并運行gqs指令
/* Macro in request.c */
#define SIGNATURE_DEBUG
$ sudo make uninstall && make clean && make && sudo make install
$ s3 gqs -u nvrbucket/dog.jpeg
詳細輸出(host域名做了替換)
--
Canonical Request:
GET
/nvrbucket/dog.jpeg
host:xxxx.xxxx.xxxx.cn
x-amz-content-sha256:UNSIGNED-PAYLOAD
x-amz-date:20191031T034215Z
host;x-amz-content-sha256;x-amz-date
UNSIGNED-PAYLOAD
--
String to Sign:
AWS4-HMAC-SHA256
20191031T034215Z
20191031/us-east-1/s3/aws4_request
49fd66dead2dfc009233c802aafdfd188bc8871a053531c452d0cd58845b77ed
--
Authorization Header:
Authorization: AWS4-HMAC-SHA256 Credential=cKc863uvDSWJZZkRijts/20191031/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=669b2ae3a0897f1ce5074564a255a2b8a9ca3652daf35a1574752866acf97f56
--
AMZ Headers:
x-amz-date: 20191031T034215Z
x-amz-content-sha256: UNSIGNED-PAYLOAD
http://xxxx.xxxx.xxxx.cn/nvrbucket/dog.jpeg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=cKc863uvDSWJZZkRijts/20191031/us-east-1/s3/aws4_request&X-Amz-Date=20191031T034215Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host;x-amz-content-sha256;x-amz-date&X-Amz-Signature=669b2ae3a0897f1ce5074564a255a2b8a9ca3652daf35a1574752866acf97f56
標準流程
正確示例
/* 基礎環(huán)境配置 */
AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE,
AWSSecretAccessKey=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY钩乍,
bucket=examplebucket辞州,
object=test.txt,
expires=86400,
region=us-east-1,
/* 正確參考示例 */
--
Canonical Request:
GET
/test.txt
X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host
host:examplebucket.s3.amazonaws.com
host
UNSIGNED-PAYLOAD
--
String to Sign:
AWS4-HMAC-SHA256
20130524T000000Z
20130524/us-east-1/s3/aws4_request
3bfa292879f6447bbcda7001decf97f4a54dc650c8942174ae0a9121cf58ad04
--
SigningKey:
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")
--
URL:
https://examplebucket.s3.amazonaws.com/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=aeeed9bbccd4d02ee5c0109b86d86835f995330da4c265957d157751f604d404
修改代碼寥粹,確保輸出與example一致
1变过、compose_auth_header( )中values->canonicalQueryString為NULL,與示例不符需要賦值涝涤,在setup_request()中添加代碼
/* request.c && setup_request() */
// Compute the canonicalized resource
canonicalize_resource(¶ms->bucketContext, computed->urlEncodedKey,
computed->canonicalURI,
sizeof(computed->canonicalURI));
snprintf(computed->authCredential, sizeof(computed->authCredential),
"%s%%2F%.8s%%2F%s%%2Fs3%%2Faws4_request", params->bucketContext.accessKeyId,
computed->requestDateISO8601, S3_DEFAULT_REGION);
// compose params
char queryParams[sizeof("X-Amz-Algorithm=AWS4-HMAC-SHA256") +
sizeof("&X-Amz-Credential=") +
sizeof(computed->authCredential) +
sizeof("&X-Amz-Date=") +
sizeof(computed->requestDateISO8601) +
sizeof("&X-Amz-Expires=") + 64 +
sizeof("&X-Amz-SignedHeaders=") +
sizeof(computed->signedHeaders) + 1] = { 0 };
snprintf(queryParams, sizeof(queryParams),
"X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=%s"
"&X-Amz-Date=%s&X-Amz-Expires=%d"
"&X-Amz-SignedHeaders=%s",
computed->authCredential, computed->requestDateISO8601, expires,
computed->signedHeaders);
canonicalize_query_string(queryParams, params->subResource,
computed->canonicalQueryString,
sizeof(computed->canonicalQueryString));
2媚狰、去掉對compose_auth_header()中的values->authCredential的重復賦值,上面添加代碼時阔拳,已經(jīng)賦值完成崭孤,且用%2F替換了‘/’
/* request.c && compose_auth_header() */
len = 0;
values->requestSignatureHex[0] = '\0';
for (i = 0; i < S3_SHA256_DIGEST_LENGTH; i++) {
buf_append(values->requestSignatureHex, "%02x", finalSignature[i]);
}
#if 0
snprintf(values->authCredential, sizeof(values->authCredential),
"%s/%.8s/%s/s3/aws4_request", params->bucketContext.accessKeyId,
values->requestDateISO8601, awsRegion);
#endif
snprintf(values->authorizationHeader,
sizeof(values->authorizationHeader),
"Authorization: AWS4-HMAC-SHA256 Credential=%s,SignedHeaders=%s,Signature=%s",
values->authCredential, values->signedHeaders,
values->requestSignatureHex);
3、去掉canonicalize_signature_headers()中對amzHeader的多余添加
/* request.c && canonicalize_signature_headers() */
// Make a copy of the headers that will be sorted
const char *sortedHeaders[S3_MAX_METADATA_COUNT + 3];
#if 0
memcpy(sortedHeaders, values->amzHeaders,
(values->amzHeadersCount * sizeof(sortedHeaders[0])));
// add the content-type header and host header
int headerCount = values->amzHeadersCount;
#else
int headerCount = 0;
#endif
if (values->contentTypeHeader[0]) {
sortedHeaders[headerCount++] = values->contentTypeHeader;
}
完成上述環(huán)境配置和代碼修改糊肠,即可實現(xiàn)gqs命令的正確URL輸出辨宠,協(xié)議對比符合預期且URL可以訪問,不過在重新進行put货裹,list等其他命令時發(fā)生“SignatureDoesNotMatch”錯誤嗤形,為了保持對原命令的兼容性,該部分修改需為gqs命令單獨執(zhí)行弧圆。
總結
以上是幾個關鍵點的修改茬缩,完成修改優(yōu)化并通過測試的libs3庫晶渠,已放入地址https://github.com/Doawen/protocol-libs3-c.git可參考茬暇。