在OSS中,使用HTTP請求的Authorization Header來攜帶簽名信息是進行身份驗證的最常見方法。除了使用POST簽名和URL簽名之外,所有的OSS操作都需要通過Authorization Header來進行身份驗證。本文介紹如何使用V4簽名算法來在Header中包含簽名。
SDK簽名實現
OSS SDK已實現自動完成V4簽名。采用OSS SDK的方式發起請求,可以免去手動簽名的過程。如何在初始化客戶端時使用V4簽名,以及SDK如何實現V4簽名的代碼示例如下表所示:
SDK | 客戶端初始化示例 | SDK簽名實現 |
Java | ||
PHP | ||
Node.js | ||
Browser.js | ||
Python | ||
Go | ||
C++ | ||
C |
Authorization參數說明
如果上述方式無法滿足您的需求,例如使用REST API發起請求時,您需要手動計算SDK簽名,需要獲取發起REST API請求時所需的Authorization參數。Authorization
字段以空格的形式分隔簽名版本和簽名信息。
Authorization字段 | 說明 |
簽名版本 | 僅支持填寫OSS4-HMAC-SHA256。 |
簽名信息 | 以鍵值對(key=value)的形式呈現。鍵值對之間用逗號分隔,鍵與值之間用等號連接。其中,簽名信息支持的key包括兩個必選字段(
|
格式
Authorization: "OSS4-HMAC-SHA256 Credential=" + AccessKeyId + "/" + SignDate + "/" + SignRegion + "/oss/aliyun_v4_request, " + [ "AdditionalHeaders=" + AdditionalHeadersVal + ", " ] + "Signature=" + SignatureVal
示例
Authorization: OSS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20231203/cn-hangzhou/oss/aliyun_v4_request, AdditionalHeaders=host;userdefine, Signature=4b663e424d2db9967401ff6ce1c86f8c83cabd77d9908475239d9110642c63fa
通過STS服務獲取的臨時訪問憑證發送請求時,您還需要將獲得的security-token值以x-oss-security-token:security-token
的形式加入到請求頭中。關于如何獲取Security-Token的具體操作,請參見AssumeRole - 獲取扮演角色的臨時身份憑證。
簽名計算流程
如下是簽名計算流程圖。
步驟一:通過Http verb等參數構建出的CanonicalReques字符串保留,在之后需要經過Hash轉化;
步驟二:將步驟一中獲得的CanonicalRequest進行一次sha256Hex轉換得到字符串“XXX”,再將"OSS4-HMAC-SHA256"等參數與所得到的字符串“XXX”拼接,構建出StringToSign;
步驟三:根據圖示使用HMAC-SHA256格式逐步轉換封裝參數,構建出SigningKey,最后將SigningKey與步驟二中獲得的StringToSign使用HMAC-SHA256格式轉換,得到的字符串就是Signature的值。
關于每個步驟的具體參數說明,請參考流程圖下方各步驟內容。具體計算過程示例,請參考簽名計算示例。
步驟1:構造CanonicalRequest
按照OSS簽名定義的規范,對請求參數進行拼接并使用Hash格式化轉換為字符串。
拼接CanonicalRequest格式
HTTP Verb + "\n" +
Canonical URI + "\n" +
Canonical Query String + "\n" +
Canonical Headers + "\n" +
Additional Headers + "\n" +
Hashed PayLoad
各參數說明如下:
如下涉及的專業術語可參考HTTP協議。
參數 | 類型 | 是否必選 | 示例 | 說明 |
HTTP Verb | 枚舉值 | 是 | PUT | HTTP請求的Method,包含PUT、GET、POST、HEAD、DELETE、OPTIONS等。 |
Canonical URI | 字符串 | 是 | /examplebucket/exampleobject | URI進行UrIEncode后的字符串,其中正斜線(/)不需要編碼。
根據請求URI包含的資源有差異,Canonical URI的填寫方法說明如下: 說明 Canonical URI需要加入Bucket的描述信息,通常情況下Bucket是體現在域名上的。
|
Canonical Query String | 字符串 | 是 | UrlEncode("marker") + "=" + UrlEncode("someMarker") + "&" + UrlEncode("max-keys") + "=" + UrlEncode("20") + "&" + UrlEncode("prefix") + "=" + UrlEncode("somePrefix") | 針對QueryString執行UrIEncode后的字符串,單獨對key和value進行編碼。
|
Canonical Headers | 字符串 | 是 | host:cname.com x-oss-content-sha256:UNSIGNED-PARYLOAD x-oss-date:20231203T121212Z | 對請求Header的列表格式化后的字符串,各個Header之間需要添加換行符分隔。
Canonical Headers包含以下兩類:
|
Additional Headers | 字符串 | 是 | content-length;host | 除了Content-Type、Content-MD5、x-oss-*以外,其他需要加入簽名的Header。所有的Header必須是小寫,且按照字典序排列。 |
Hashed PayLoad | 字符串 | 是 | UNSIGNED-PAYLOAD | 僅支持UNSIGNED-PAYLOAD。 |
示例
"GET" | "PUT" | ... + "\n" +
UrlEncode(<Resource>) + "\n" +
UrlEncode(<QueryParam1>) + "=" + UrlEncode(<Value>) + "&" + UrlEncode(<QueryParam2>) + "\n" +
Lowercase(<HeaderName1>) + ":" + Trim(<value>) + "\n" + Lowercase(<HeaderName2>) + ":" + Trim(<value>) + "\n" +
Lowercase(<AdditionalHeaderName1>) + ";" + Lowercase(<AdditionalHeaderName2>) + "\n" +
UNSIGNED-PAYLOAD
步驟2:構造待簽名字符串(StringToSign)
將其他三個參數與步驟一中格式化后的請求再進行拼接,拼接完成后再通過Hash轉換格式化,得到待簽名的字符串。
格式
"OSS4-HMAC-SHA256" + "\n" + TimeStamp + "\n" + Scope + "\n" + Hex(SHA256Hash(<CanonicalRequest>))
參數說明如下:
參數
類型
是否必選
示例
說明
OSS4-HMAC-SHA256
枚舉值
是
OSS4-HMAC-SHA25
指明使用的簽名哈希算法,取值必須是OSS4-HMAC-SHA256。
TimeStamp
字符串
是
20231203T121212Z
指明當前的UTC時間,格式必須是 ISO8601。
Scope
字符串
是
20231203/cn-hangzhou/oss/aliyun_v4_request
指明獲取Region派生密鑰的參數集,格式如下:
<SigningDate>/<SigningRegion>/oss/aliyun_v4_request
SigningDate:填寫請求的日期。
SigningRegion:填寫請求所在的Region。
oss:請求的服務名稱,固定為oss。
aliyun_v4_request:請求的版本說明,固定為aliyun_v4_request。
CanonicalRequest
字符串
是
PUT
/examplebucket/exampleobject
content-md5:eB5eJF1ptWaXm4bijSPyxw
content-type:text/html
host:examplebucket.oss-cn-hangzhou.aliyuncs.com
x-oss-content-sha256:UNSIGNED-PAYLOAD
x-oss-date:20231203T121212Z
x-oss-meta-author:alice
x-oss-meta-magic:abracadabra
host
UNSIGNED-PAYLOAD
上一步驟構造出來的字符串。
示例
"OSS4-HMAC-SHA256" + "\n" + FormatISO8601 + "\n" + 20231203/cn-hangzhou/oss/aliyun_v4_request + "\n" + Hex(SHA256Hash(<CanonicalRequest>))
步驟3:計算Signature
使用簽名密鑰對待簽名字符串進行計算。
計算SigningKey,使用HMAC-SHA256格式逐步封裝,具體參考如下示例。
說明計算過程中HMAC-SHA256所需參數,第一個為KEY,第二個是MESSAGE;
其中SK為用戶個人密鑰,Date為當期時間(格式參照ISO8601,例如20231203),Region為請求所在的地區。
DateKey = HMAC-SHA256("aliyun_v4" + SK, Date); DateRegionKey = HMAC-SHA256(DateKey, Region); DateRegionServiceKey = HMAC-SHA256(DateRegionKey, "oss"); SigningKey = HMAC-SHA256(DateRegionServiceKey, "aliyun_v4_request");
上面這些計算步驟也可以使用一個式子完成封裝,示例如下:
SigningKey = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("aliyun_v4" + SK, Date), Region), "oss"), "aliyun_v4_request");
將上面計算得到的SigningKey與步驟2計算得到的StringToSign進行HMAC-SHA256格式轉換,得到Signature。
Signature = HEX(HMAC-SHA256(SigningKey, StringToSign))
簽名計算示例
以PutObject為例,演示如何在Header包含V4簽名。
計算示例參數說明
參數
值
AccessKeyId
accesskeyid
AccessKeySecret
accesskeysecret
Timestamp
20231203T121212Z
Bucket
examplebucket
Object
exampleobject
Region
cn-hangzhou
PutObject
PUT /exampleobject HTTP/1.1 Content-MD5: eB5eJF1ptWaXm4bijSPyxw Content-Type: text/html Date: Sun, 03 Dec 2023 12:12:12 GMT Host: examplebucket.oss-cn-hangzhou.aliyuncs.com Authorization: SignatureToBeCalculated x-oss-date: 20231203T121212Z x-oss-meta-author: alice x-oss-meta-magic: abracadabra x-oss-content-sha256: UNSIGNED-PAYLOAD
在Header包含V4簽名的步驟如下:
構造出的CanonicalRequest格式如下。
PUT /examplebucket/exampleobject content-md5:eB5eJF1ptWaXm4bijSPyxw content-type:text/html host:examplebucket.oss-cn-hangzhou.aliyuncs.com x-oss-content-sha256:UNSIGNED-PAYLOAD x-oss-date:20231203T121212Z x-oss-meta-author:alice x-oss-meta-magic:abracadabra host UNSIGNED-PAYLOAD
構造待簽名字符串(StringToSign)格式如下。
OSS4-HMAC-SHA256 20231203T121212Z 20231203/cn-hangzhou/oss/aliyun_v4_request 129b14df88496f434606e999e35dee010ea1cecfd3ddc378e5ed4989609c1db3
計算簽名。
計算SigningKey。
HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("aliyun_v4" + "accesskeysecret", "20231203"), "cn-hangzhou"), "oss"), "aliyun_v4_request");
通過計算公式逐層計算得到Signature,結果格式示例如下。
說明計算公式為Signature = HEX(HMAC-SHA256(Signingkey,StringToSign));
4b663e424d2db9967401ff6ce1c86f8c83cabd77d9908475239d9110642c63fa
將簽名添加到Authorization中。
OSS4-HMAC-SHA256 Credential=accesskeyid/20231203/cn-hangzhou/oss/aliyun_v4_request,AdditionalHeaders=host,Signature=4b663e424d2db9967401ff6ce1c86f8c83cabd77d9908475239d9110642c63fa
代碼示例
import com.aliyun.oss.common.utils.BinaryUtil;
import org.apache.commons.codec.digest.DigestUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* Signature Demo
*/
public class Demo1 {
/**
* 簽名計算工具
*
* @return Authorization
*/
public static void main(String[] args) throws Exception {
// 步驟1:構造CanonicalRequest
String canonicalRequest =
"PUT\n" +
"/examplebucket/exampleobject\n" +
"\n" +
"content-md5:eB5eJF1ptWaXm4bijSPyxw\n" +
"content-type:text/html\n" +
"host:examplebucket.oss-cn-hangzhou.aliyuncs.com\n" +
"x-oss-content-sha256:UNSIGNED-PAYLOAD\n" +
"x-oss-date:20231203T121212Z\n" +
"x-oss-meta-author:alice\n" +
"x-oss-meta-magic:abracadabra\n" +
"\n" +
"host\n" +
"UNSIGNED-PAYLOAD";
// 步驟2:構造待簽名字符串(StringToSign)
String stringToSign = "OSS4-HMAC-SHA256\n" +
"20231203T121212Z\n" +
"20231203/cn-hangzhou/oss/aliyun_v4_request\n" +
DigestUtils.sha256Hex(canonicalRequest);
// 步驟3:計算Signature。
// "accesskeysecret"填入RAM用戶的AccessKeySecret,data參數填入實際日期如"20231203”
byte[] dateKey = hmacsha256(("aliyun_v4" + "accesskeysecret").getBytes(), "20231203");
// 參數填入所在地區,如所在地域為杭州則填入"cn-hangzhou”
byte[] dateRegionKey = hmacsha256(dateKey, "cn-hangzhou");
byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
byte[] result = hmacsha256(signingKey, stringToSign);
String signature = BinaryUtil.toHex(result);
System.out.println("signature:" + signature);
String authorization = "OSS4-HMAC-SHA256 " +
"Credential=accesskeyid/20231203/cn-hangzhou/oss/aliyun_v4_request," +
"AdditionalHeaders=host," +
"Signature=" + signature;
System.out.println("Authorization:" + authorization);
}
public static byte[] hmacsha256(byte[] key, String data) {
try {
// 初始化HMAC密鑰規格,指定算法為HMAC-SHA256并使用提供的密鑰。
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
// 獲取Mac實例,并通過getInstance方法指定使用HMAC-SHA256算法。
Mac mac = Mac.getInstance("HmacSHA256");
// 使用密鑰初始化Mac對象。
mac.init(secretKeySpec);
// 執行HMAC計算,通過doFinal方法接收需要計算的數據并返回計算結果的數組。
byte[] hmacBytes = mac.doFinal(data.getBytes());
return hmacBytes;
} catch (Exception e) {
throw new RuntimeException("Failed to calculate HMAC-SHA256", e);
}
}
}