すべてのプロダクト
Search
ドキュメントセンター

Object Storage Service:POST V4 署名

最終更新日:May 01, 2026

Object Storage Service (OSS) では、PostObject 操作を使用してオブジェクトをアップロードする際に、各リクエストに V4 署名を含める必要があります。これにより、セキュリティが確保されます。アプリケーションサーバは、ご利用の AccessKey Secret を使用して、ポリシーとその有効期限を含む一連のリクエストパラメーターを暗号化し、この署名を計算します。クライアントはこの情報を使用してアップロードリクエストを構築します。OSS がアップロードリクエストを受信すると、署名を検証し、署名が有効な場合のみリクエストを処理し、それ以外のリクエストはすべて拒否します。

POST 署名の仕組み

HTTP POST リクエストでは、V4 署名アルゴリズムを使用してセキュリティを強化します。フォームおよびポリシーは、アップロードリクエストの真正性とセキュリティを保証するために重要です。

フォーム要素

フォームとは、POST リクエスト内でファイルと関連メタデータを送信するためのフィールドのコレクションです。次の表は、V4 署名に特有のフォーム要素について説明しています。一般的なフォーム要素については、「PostObject フォーム要素」をご参照ください。

パラメーター

タイプ

必須

説明

x-oss-signature-version

String

はい

署名バージョンおよびアルゴリズム。値は OSS4-HMAC-SHA256 に固定されています。

x-oss-credential

String

はい

派生キー用のパラメーターセットを指定します。形式は以下のとおりです。

<AccessKeyId>/<date>/<region>/oss/aliyun_v4_request
  • AccessKeyId: ご利用の AccessKey ID。

  • date: リクエスト日付(YYYYMMDD 形式)。例:20231203。

  • region: Alibaba Cloud の汎用リージョン ID。例:cn-hangzhou

  • oss: リクエスト対象のサービス名。値は oss に固定されています。

  • aliyun_v4_request: リクエストバージョン。値は aliyun_v4_request に固定されています。

x-oss-date

String

はい

ISO 8601 形式でのリクエスト時刻。例:20231203T121212Z

  • 最大 15 分の遡及許容があります。つまり、サーバーは x-oss-date フィールドで指定された時刻から最大 15 分後にリクエストを受信しても受け入れます。これはネットワーク遅延やクライアントとサーバー間の時刻同期エラーを考慮したもので、クライアントの時計がわずかにずれていてもリクエストが正常に処理されることを保証します。

  • リクエストの有効期間は、x-oss-date で指定された時刻から最大 7 日間です。x-oss-date の値がリクエストの有効期限切れ(7 日以上経過)を示している場合、OSS サーバーはそのリクエストを拒否し、エラーメッセージを返します。これにより、リクエストの適時性とセキュリティが確保され、古い署名付きリクエストの悪意ある再送を防止します。

  • x-oss-date で指定された時刻は StringToSign 内の TimeStamp として使用され、その値は署名キー作成時に使用した Date と同じ日付であり、かつポリシー内の x-oss-date 値と同一である必要があります。

x-oss-signature

String

はい

認証に使用される署名。値は、Base64 エンコードされたポリシー文字列を HMAC-SHA256 アルゴリズムでハッシュ化し、結果を 16 進数文字列に変換することで計算されます。

ポリシー

ポリシーのフォームフィールドは、HTML フォームを通じて OSS へのファイルアップロードに関する権限と制約を定義します。JSON 形式で記述され、許可されたバケット名、オブジェクトプレフィックス、有効期限、許可された HTTP メソッド、コンテンツサイズ制限、コンテンツタイプ制限などのパラメーターを定義することで、アップロードを制限します。

重要

ポリシーには expiration および conditions フィールドを必ず含める必要があります。以下の例の conditions フィールドには、オプションのパラメーターである x-oss-security-token が含まれています。このパラメーターは、STS からの一時的なアクセス認証情報を使用して POST 署名を生成する場合にのみ必要です。長期的な AccessKey を使用して POST 署名を生成する場合は、x-oss-security-token フィールドを含めないでください。

{
  "expiration": "2023-12-03T13:00:00.000Z",
  "conditions": [
    {"bucket": "examplebucket"},
    {"x-oss-signature-version": "OSS4-HMAC-SHA256"},
    {"x-oss-credential": "AKIDEXAMPLE/20231203/cn-hangzhou/oss/aliyun_v4_request"},
    {"x-oss-security-token": "CAIS******"},
    {"x-oss-date": "20231203T121212Z"},
    ["content-length-range", 1, 10],
    ["eq", "$success_action_status", "201"],
    ["starts-with", "$key", "user/eric/"],
    ["in", "$content-type", ["image/jpg", "image/png"]],
    ["not-in", "$cache-control", ["no-cache"]]
  ]
}

ポリシーパラメーターの説明は以下のとおりです。

  • expiration

    ポリシーの有効期限を ISO 8601 GMT 形式で指定します。たとえば、2023-12-03T13:00:00.000Z という値は、POST リクエストを 2023 年 12 月 03 日 13:00 以前に開始する必要があることを意味します。

  • conditions

    POST リクエストのフォームフィールドに関する条件を定義します。

    パラメーター

    タイプ

    必須

    説明

    マッチングタイプ

    bucket

    String

    いいえ

    バケット名。

    bucket

    x-oss-signature-version

    String

    はい

    署名バージョンおよびアルゴリズム。値は OSS4-HMAC-SHA256 に固定されています。

    x-oss-signature-version

    x-oss-credential

    String

    はい

    派生キー用のパラメーターセットを指定します。形式は以下のとおりです。

    <AccessKeyId>/<date>/<region>/oss/aliyun_v4_request
    • AccessKeyId: ご利用の AccessKey ID。

    • date: リクエスト日付。

    • region: Alibaba Cloud の汎用リージョン ID。例:cn-hangzhou

    • oss: リクエスト対象のサービス名。値は oss に固定されています。

    • aliyun_v4_request: リクエストバージョン。値は aliyun_v4_request に固定されています。

    x-oss-credential

    x-oss-security-token

    String

    いいえ

    STS からの一時的なアクセス認証情報を使用して POST 署名を生成する場合にのみ必要です。セキュリティトークンは、STS の AssumeRole 操作を呼び出すことで取得できます。

    x-oss-security-token

    x-oss-date

    String

    はい

    ISO 8601 形式でのリクエスト時刻。例:20231203T121212Z

    • 最大 15 分の遡及許容があります。つまり、サーバーがリクエストを受信する実際の時刻は、x-oss-date ヘッダーで指定された時刻から最大 15 分後まで許容されます。これは主にネットワーク伝送遅延およびクライアントとサーバー間の潜在的な時刻同期エラーを考慮したもので、クライアントの時刻がわずかにずれていても一定の範囲内でリクエストが正常に処理されることを保証します。

    • リクエストの有効期間は、x-oss-date ヘッダーで指定された時刻から最大 7 日間です。x-oss-date の値がリクエストの有効期限切れ(7 日以上経過)を示している場合、OSS サーバーはそのリクエストを拒否し、エラーメッセージを返します。これにより、リクエストの適時性とセキュリティが確保され、古い署名付きリクエストの悪意ある再送を防止します。

    • x-oss-date フィールドは、StringToSign 内の TimeStamp として使用される時刻を指定します。このフィールドの値は、署名キー作成時に使用した Date と同じ日付であり、かつフォーム内の x-oss-date フィールドの値と同一である必要があります。

    x-oss-date

    content-length-range

    String

    いいえ

    アップロードするオブジェクトの許容最小サイズおよび最大サイズ(バイト単位)。

    content-length-range

    success_action_status

    String

    いいえ

    アップロード成功後に返す HTTP ステータスコード。

    eq、eq-ci、starts-with、starts-with-ci、in、in-ci、not-in、not-in-ci

    key

    String

    いいえ

    アップロードするオブジェクトの名前。

    eq、eq-ci、starts-with、starts-with-ci、in、in-ci、not-in、not-in-ci

    content-type

    String

    いいえ

    アップロードのファイルタイプを制限します。

    eq、eq-ci、starts-with、starts-with-ci、in、in-ci、not-in、not-in-ci

    cache-control

    String

    いいえ

    オブジェクトのキャッシュ動作を指定します。

    eq、eq-ci、starts-with、starts-with-ci、in、in-ci、not-in、not-in-ci

条件のマッチングタイプ

マッチングタイプ

説明

content-length-range

アップロードするファイルの許容最小サイズおよび最大サイズを指定します。たとえば、ファイルサイズを 1~10 バイトに制限するには、["content-length-range", 1, 10] を使用します。

eq

フォームフィールドの値が指定された値と完全に一致する必要があります。たとえば、key フィールドを厳密に "a" にするには、["eq", "$key", "a"] を使用します。

starts-with

フォームフィールドの値が指定されたプレフィックスで始まる必要があります。たとえば、key の値が "user/user1" で始まるようにするには、["starts-with", "$key", "user/user1"] を使用します。

in

フォームフィールドに許可される値のリストを指定します。たとえば、アップロードされた画像が JPG または PNG であることを確認するには、["in", "$content-type", ["image/jpg", "image/png"]] を使用します。

not-in

フォームフィールドに許可されない値のリストを指定します。たとえば、cache-control に "no-cache" を許可しないようにするには、["not-in", "$cache-control", ["no-cache"]] を使用します。

eq-ci

フォームフィールドの値が指定された値と完全に一致する必要があります(大文字・小文字を区別しません)。比較は小文字の文字列に対して行われます。たとえば、["eq-ci", "$key", "AbC"] の場合、"abc"、"ABC"、"aBc" などのオブジェクト名はすべて有効な一致となります。

starts-with-ci

フォームフィールドの値が指定されたプレフィックスで始まる必要があります(大文字・小文字を区別しません)。比較は小文字の文字列に対して行われます。たとえば、["starts-with-ci", "$key", "User/"] の場合、"user/"、"USER/"、"User/" で始まる値はすべて有効な一致となります。

in-ci

許可される値のリストを指定します(大文字・小文字を区別しません)。比較は小文字の文字列に対して行われます。たとえば、["in-ci", "$content-type", ["IMAGE/JPG", "image/PNG"]] の場合、"image/jpg" や "IMAGE/PNG" などの値はすべて有効な一致となります。

not-in-ci

許可されない値のリストを指定します(大文字・小文字を区別しません)。比較は小文字の文字列に対して行われます。たとえば、["not-in-ci", "$cache-control", ["No-Cache"]] の場合、"no-cache"、"NO-CACHE"、"No-Cache" などの値はすべて除外されます。

説明

ポリシーの conditions に同じフォームフィールドに対して複数の条件付きステートメント(例:in および not-in)が含まれている場合、最後のステートメントが優先されます。同じフォームフィールドに対して意味的に矛盾する条件を設定しないようにしてください。予期しない検証結果を回避できます。

ポリシーのエスケープ文字

ポリシー内では、ドル記号 ($) が変数を示します。リテラルのドル記号を使用するには、\$ としてエスケープする必要があります。次の表は、ポリシー JSON に必要なエスケープ文字を示しています。

エスケープ文字

説明

\/

スラッシュ

\\

バックスラッシュ

\

二重引用符

\$

ドル記号

\b

スペース

\f

改ページ

\n

改行

\r

キャリッジリターン

\t

水平タブ

\uxxxx

Unicode 文字

署名計算プロセス

  1. UTF-8 エンコードされたポリシーを作成します。

  2. 署名対象文字列を構築します。

    ポリシーを Base64 エンコードして安全な文字列を生成します。このエンコードされた文字列が署名対象文字列です。

  3. 署名キーを計算します。

    ご利用の AccessKey Secret を使用して一連の HMAC-SHA256 ハッシュ化操作を実行します。このプロセスにより、日付、リージョン、サービスに固有の最終的な派生キー(署名キー)が作成されます。

  4. 署名を計算します。

    HMAC-SHA256 アルゴリズムを使用して、署名対象文字列を署名キーで署名します。結果として得られるバイナリハッシュを 16 進数文字列に変換します。この 16 進数文字列が、リクエストの整合性と真正性を検証するための最終的な署名です。

POST 署名計算の例

  1. 以下の Java コードは、前述のポリシーに対する POST 署名の計算方法を示す完全な例です。

    AccessKey 認証情報

    import com.aliyun.oss.common.utils.BinaryUtil;
    import org.apache.commons.codec.binary.Base64;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import com.fasterxml.jackson.databind.ObjectMapper; 
    import java.util.Map; 
    import java.util.HashMap; 
    import java.util.List; 
    import java.util.ArrayList; 
    import java.util.Arrays; 
    
    public class Demo {
        public static void main(String[] args) throws Exception {
            // このコードを実行する前に、OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
            String accesskeyid =  System.getenv().get("OSS_ACCESS_KEY_ID");
            String accesskeysecret =  System.getenv().get("OSS_ACCESS_KEY_SECRET");
    
            // ステップ 1:ポリシーを作成します。
            ObjectMapper mapper = new ObjectMapper();
    
            Map<String, Object> policy = new HashMap<>();
            policy.put("expiration", "2024-12-03T13:00:00.000Z");
    
            List<Object> conditions = new ArrayList<>();
    
            Map<String, String> bucketCondition = new HashMap<>();
            bucketCondition.put("bucket", "examplebucket");
            conditions.add(bucketCondition);
    
            Map<String, String> signatureVersionCondition = new HashMap<>();
            signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
            conditions.add(signatureVersionCondition);
    
            Map<String, String> credentialCondition = new HashMap<>();
            credentialCondition.put("x-oss-credential", accesskeyid + "/20241203/cn-hangzhou/oss/aliyun_v4_request");
            conditions.add(credentialCondition);
    
            Map<String, String> dateCondition = new HashMap<>();
            dateCondition.put("x-oss-date", "20241203T121212Z");
            conditions.add(dateCondition);
    
            conditions.add(Arrays.asList("content-length-range", 1, 10));
            conditions.add(Arrays.asList("eq", "$success_action_status", "201"));
            conditions.add(Arrays.asList("starts-with", "$key", "user/eric/"));
            conditions.add(Arrays.asList("in", "$content-type", Arrays.asList("image/jpg", "image/png")));
            conditions.add(Arrays.asList("not-in", "$cache-control", Arrays.asList("no-cache")));
    
            policy.put("conditions", conditions);
    
            String jsonPolicy = mapper.writeValueAsString(policy);
            // ステップ 2:署名対象文字列を構築します。
            String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
            System.out.println(stringToSign);
            
            // ステップ 3:署名キーを計算します。
            byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), "20241203");
            byte[] dateRegionKey = hmacsha256(dateKey, "cn-hangzhou");
            byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
            byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
    
            // ステップ 4:署名を計算します。
            byte[] result = hmacsha256(signingKey, stringToSign);
            String signature = BinaryUtil.toHex(result);
            System.out.println("signature:" + signature);
    
        }
    
        public static byte[] hmacsha256(byte[] key, String data) {
            try {
                // HMAC キースペックを初期化し、アルゴリズムとして HmacSHA256 を指定し、提供されたキーを使用します。
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
    
                // Mac インスタンスを取得し、アルゴリズムとして HmacSHA256 を指定します。
                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("HMAC-SHA256 の計算に失敗しました", e);
            }
        }
    }

    以下の結果が返されます。

    signature:3908473f7dbfb79a102eaaa44ca1edec8d7058ce3bd1c624d59eb437463bd5d6

    一時的なアクセス認証情報

    STS からの一時的なアクセス認証情報を使用して POST 署名を生成する場合は、STS の AssumeRole 操作を呼び出してセキュリティトークンを取得する必要があります。

    import com.aliyun.oss.common.utils.BinaryUtil;
    import org.apache.commons.codec.binary.Base64;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import com.aliyun.sts20150401.models.AssumeRoleResponse;
    import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
    import com.aliyun.tea.TeaException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.util.*;
    public class Demo {
    
        // STS クライアントを初期化します。
        public static com.aliyun.sts20150401.Client createStsClient() throws Exception {
            com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                    // 必須。OSS_ACCESS_KEY_ID 環境変数が設定されていることを確認してください。
                    .setAccessKeyId(System.getenv("OSS_ACCESS_KEY_ID"))
                    // 必須。OSS_ACCESS_KEY_SECRET 環境変数が設定されていることを確認してください。
                    .setAccessKeySecret(System.getenv("OSS_ACCESS_KEY_SECRET"));
            // エンドポイント 
            config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
            return new com.aliyun.sts20150401.Client(config);
        }
    
        // STS から一時的なアクセス認証情報を取得します。
        public static AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials getCredential() throws Exception {
            com.aliyun.sts20150401.Client client = Demo.createStsClient();
            com.aliyun.sts20150401.models.AssumeRoleRequest assumeRoleRequest = new com.aliyun.sts20150401.models.AssumeRoleRequest()
                    // 必須。OSS_STS_ROLE_ARN 環境変数が設定されていることを確認してください。
                    .setRoleArn(System.getenv("OSS_STS_ROLE_ARN"))
                    .setRoleSessionName("role_session_name");// カスタムセッション名。
            com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
            try {
                // このコードをコピーする場合は、API 応答を自分で出力してください。
                AssumeRoleResponse response = client.assumeRoleWithOptions(assumeRoleRequest, runtime);
                // credentials オブジェクトには、後続の操作に必要な AccessKeyId、AccessKeySecret、SecurityToken が含まれています。
                return response.body.credentials;
            } catch (TeaException error) {
                // これはデモ用です。本番環境では例外を慎重に処理し、無視しないでください。
                // エラーメッセージ
                System.out.println(error.getMessage());
                // トラブルシューティング URL
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
            } catch (Exception _error) {
                TeaException error = new TeaException(_error.getMessage(), _error);
                // これはデモ用です。本番環境では例外を慎重に処理し、無視しないでください。
                // エラーメッセージ
                System.out.println(error.getMessage());
                // トラブルシューティング URL
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
            }
            return null;
        }
    
        public static void main(String[] args) throws Exception {
    
            AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials sts_data = getCredential();
            String accesskeyid = sts_data.accessKeyId;
            String accesskeysecret = sts_data.accessKeySecret;
            String securitytoken = sts_data.securityToken;
            // ステップ 1:ポリシーを作成します。
            ObjectMapper mapper = new ObjectMapper();
    
            Map<String, Object> policy = new HashMap<>();
            policy.put("expiration", "2024-12-03T13:00:00.000Z");
    
            List<Object> conditions = new ArrayList<>();
    
            Map<String, String> bucketCondition = new HashMap<>();
            bucketCondition.put("bucket", "examplebucket");
            conditions.add(bucketCondition);
    
            Map<String, String> signatureVersionCondition = new HashMap<>();
            signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
            conditions.add(signatureVersionCondition);
            
            Map<String, String> securityTokenCondition = new HashMap<>();
            securityTokenCondition.put("x-oss-security-token", securitytoken);
            conditions.add(securityTokenCondition);
            
            Map<String, String> credentialCondition = new HashMap<>();
            credentialCondition.put("x-oss-credential", accesskeyid + "/20241203/cn-hangzhou/oss/aliyun_v4_request");
            conditions.add(credentialCondition);
    
            Map<String, String> dateCondition = new HashMap<>();
            dateCondition.put("x-oss-date", "20241203T121212Z");
            conditions.add(dateCondition);
    
            conditions.add(Arrays.asList("content-length-range", 1, 10));
            conditions.add(Arrays.asList("eq", "$success_action_status", "201"));
            conditions.add(Arrays.asList("starts-with", "$key", "user/eric/"));
            conditions.add(Arrays.asList("in", "$content-type", Arrays.asList("image/jpg", "image/png")));
            conditions.add(Arrays.asList("not-in", "$cache-control", Arrays.asList("no-cache")));
    
            policy.put("conditions", conditions);
    
            String jsonPolicy = mapper.writeValueAsString(policy);
            // ステップ 2:署名対象文字列を構築します。
            String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
    
            // ステップ 3:署名キーを計算します。
            byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), "20241203");
            byte[] dateRegionKey = hmacsha256(dateKey, "cn-hangzhou");
            byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
            byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
    
            // ステップ 4:署名を計算します。
            byte[] result = hmacsha256(signingKey, stringToSign);
            String signature = BinaryUtil.toHex(result);
            System.out.println("signature:" + signature);
    
        }
    
        public static byte[] hmacsha256(byte[] key, String data) {
            try {
                // HMAC キースペックを初期化し、アルゴリズムとして HmacSHA256 を指定し、提供されたキーを使用します。
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
    
                // Mac インスタンスを取得し、アルゴリズムとして HmacSHA256 を指定します。
                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("HMAC-SHA256 の計算に失敗しました", e);
            }
        }
    }

    以下の結果が返されます。

    signature:1e09438f7ad01af6b3e144b42c98929c68f8d090ce07f4c277b18d8b62d0aa02
  2. 以下の Python コードは、POST 署名の計算方法を示す完全な例です。

    import base64
    import hmac
    import hashlib
    import os
    
    def hmac_sha256(key, data):
        return hmac.new(key, data.encode('utf-8'), hashlib.sha256).digest()
    
    # 環境変数から AccessKey ID および AccessKey Secret を読み取ります。
    accesskeyid = os.getenv('OSS_ACCESS_KEY_ID')
    accesskeysecret = os.getenv('OSS_ACCESS_KEY_SECRET')
    
    # AccessKey ID を出力します。
    print(accesskeyid)
    
    # 環境変数が正常に取得されたかを確認します。
    if not accesskeyid or not accesskeysecret:
        raise ValueError("必要な環境変数が見つかりません: OSS_ACCESS_KEY_ID または OSS_ACCESS_KEY_SECRET")
    
    # ポリシーを作成します。
    policy = f'''{{
      "expiration": "2025-01-01T00:00:00.000Z",
      "conditions": [
        {{"x-oss-signature-version": "OSS4-HMAC-SHA256"}},
        {{"x-oss-credential": "{accesskeyid}/20241105/cn-hangzhou/oss/aliyun_v4_request"}},
        {{"x-oss-date": "20241105T065000Z"}}
      ]
    }}'''
    
    # ポリシーを出力します。
    print(policy)
    
    # 署名対象文字列を計算します。
    string_to_sign = base64.b64encode(policy.encode('utf-8')).decode('utf-8')
    print(string_to_sign)
    
    # 署名キーを計算します。
    date_key = hmac_sha256(f"aliyun_v4{accesskeysecret}".encode('utf-8'), "20241105")
    date_region_key = hmac_sha256(date_key, "cn-hangzhou")
    date_region_service_key = hmac_sha256(date_region_key, "oss")
    signing_key = hmac_sha256(date_region_service_key, "aliyun_v4_request")
    
    # 署名を計算します。
    result = hmac_sha256(signing_key, string_to_sign)
    signature = result.hex()
    print("signature:", signature)

    以下の結果が返されます。

    signature:9e85d56429245283b1aca5bc2dc31e0020b95ac2de9e9b81b496994db602ba1e