全部產品
Search
文件中心

Object Storage Service:在Header中包含V4簽名(推薦)

更新時間:Aug 22, 2024

在OSS中,使用HTTP請求的Authorization Header來攜帶簽名資訊是進行身分識別驗證的最常見方法。除了使用POST簽名和URL簽名之外,所有的OSS操作都需要通過Authorization Header來進行身分識別驗證。本文介紹如何使用V4簽名演算法來在Header中包含簽名。

SDK簽名實現

OSS SDK已實現自動完成V4簽名。採用OSS SDK的方式發起請求,可以免去手動簽名的過程。如何在初始化用戶端時使用V4簽名,以及SDK如何?V4簽名的程式碼範例如下表所示:

SDK

用戶端初始化樣本

SDK簽名實現

Java

初始化

OSSV4Signer.java

PHP

初始化

SignerV4.php

Node.js

初始化

client.js

Browser.js

初始化

Python

初始化

auth.py

Go

初始化

v4.go

C++

初始化

SignerV4.cc

C

初始化

oss_auth.c

Authorization參數說明

如果上述方式無法滿足您的需求,例如使用REST API發起請求時,您需要手動計算SDK簽名,需要擷取發起REST API請求時所需的Authorization參數。Authorization欄位以空格的形式分隔簽名版本和簽名資訊。

Authorization欄位

說明

簽名版本

僅支援填寫OSS4-HMAC-SHA256。

簽名資訊

以索引值對(key=value)的形式呈現。索引值對之間用逗號分隔,鍵與值之間用等號串連。其中,簽名資訊支援的key包括兩個必選欄位(CredentialSignature)以及一個可選欄位(AdditionalHeaders)。

  • Credential描述具體使用哪個衍生金鑰,中間以正斜線(/)分隔。

  • Signature描述計算得到的簽名值。

  • AdditionalHeaders:允許使用者指定計算簽名過程中可選的Header,如有多項需以分號(;)分隔,忽略大小寫,且按字典序排列。

  • 格式

    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後的字串,其中正斜線(/)不需要編碼。

  • 如果URL中不包含QueryString,則從正斜線(/)開始到URL末尾。

  • 如果URL中包含QueryString,則從正斜線(/)開始到問號(?)結束。

根據請求URI包含的資源有差異,Canonical URI的填寫方法說明如下:

說明

Canonical URI需要加入Bucket的描述資訊,通常情況下Bucket是體現在網域名稱上的。

  • 如果請求的URI中既包含Bucket也包含Object,則Canonical URI填寫樣本為

    /examplebucket/exampleobject

  • 如果請求的URI中只包含Bucket不包含Object,則Canonical URI填寫樣本為/examplebucket/

  • 如果請求的URI中不包含Bucket且不包含Object,則Canonical URI填寫樣本為/

Canonical Query String

字串

UrlEncode("marker") + "=" + UrlEncode("someMarker") + "&" + UrlEncode("max-keys") + "=" + UrlEncode("20") + "&" + UrlEncode("prefix") + "=" + UrlEncode("somePrefix")

針對QueryString執行UrIEncode後的字串,單獨對key和value進行編碼。

  • 按QueryString的key進行排序,先編碼,再排序。如果有多個相同的key,按照原來添加的順序放置即可,中間使用&進行串連。

  • 只有key沒有value的情況下,只添加 key即可。

  • 如果沒有QueryString,則只需要放置Null 字元串“”,末尾仍然需要有分行符號。

Canonical Headers

字串

host:cname.com

x-oss-content-sha256:UNSIGNED-PARYLOAD

x-oss-date:20231203T121212Z

對請求Header的列表格式化後的字串,各個Header之間需要添加分行符號分隔。

  • 單個Header中的key和value通過冒號:分隔,Header與Header之間通過分行符號分隔。

  • Header的key必須小寫,value必須經過Trim(去除頭尾的空格)。

  • 按Header中key的字典序進行排列。

  • 請求時間通過x-oss-date來描述,要求格式必須是ISO8601標準時間格式(樣本值為20231203T121212Z)。

Canonical Headers包含以下兩類:

  • 必須存在且參與簽名的Header包括:

    • x-oss-content-sha256(其值為UNSIGNED-PARYLOAD)

    • AdditionalHeader指定必須存在且參與簽名的Header

  • 如果存在則加入簽名的Header包括:

    • Content-Type

    • Content-MD5

    • x-oss-*

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

使用簽名金鑰組待簽名字串進行計算。

  1. 計算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");
  2. 將上面計算得到的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簽名的步驟如下:

    1. 構造出的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
    2. 構造待簽名字串(StringToSign)格式如下。

      OSS4-HMAC-SHA256
      20231203T121212Z
      20231203/cn-hangzhou/oss/aliyun_v4_request
      129b14df88496f434606e999e35dee010ea1cecfd3ddc378e5ed4989609c1db3
    3. 計算簽名。

      1. 計算SigningKey。

        HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("aliyun_v4" + "accesskeysecret", "20231203"), "cn-hangzhou"), "oss"), "aliyun_v4_request");
      2. 通過計算公式逐層計算得到Signature,結果格式樣本如下。

        說明

        計算公式為Signature = HEX(HMAC-SHA256(Signingkey,StringToSign));

        4b663e424d2db9967401ff6ce1c86f8c83cabd77d9908475239d9110642c63fa
    4. 將簽名添加到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);
        }
    }
}