All Products
Search
Document Center

Object Storage Service:Tanda tangan POST V4

Last Updated:May 01, 2026

Untuk mengunggah objek menggunakan operasi PostObject, Object Storage Service (OSS) memerlukan tanda tangan V4 dalam setiap permintaan guna menjamin keamanan. Server aplikasi Anda menghitung tanda tangan ini dengan mengenkripsi serangkaian parameter permintaan—termasuk policy dan waktu kedaluwarsanya—menggunakan AccessKey Secret Anda. Klien kemudian menggunakan informasi tersebut untuk menyusun permintaan unggah. Saat OSS menerima permintaan unggah, OSS memverifikasi tanda tangan tersebut dan hanya memproses permintaan jika tanda tangan valid; permintaan lainnya ditolak.

Cara kerja tanda tangan POST

Permintaan HTTP POST menggunakan algoritma tanda tangan V4 untuk meningkatkan keamanan. Formulir dan policy sangat penting untuk menjamin keaslian dan keamanan permintaan unggah.

Elemen formulir

Formulir adalah kumpulan bidang dalam permintaan POST yang mentransmisikan file beserta metadata terkaitnya. Tabel berikut menjelaskan elemen formulir yang spesifik untuk tanda tangan V4. Untuk informasi mengenai elemen formulir umum, lihat Elemen formulir PostObject.

Parameter

Tipe

Wajib

Deskripsi

x-oss-signature-version

String

Ya

Versi dan algoritma tanda tangan. Nilainya tetap OSS4-HMAC-SHA256.

x-oss-credential

String

Ya

Menentukan kumpulan parameter untuk kunci turunan. Formatnya sebagai berikut:

<AccessKeyId>/<date>/<region>/oss/aliyun_v4_request
  • AccessKeyId: ID AccessKey Anda.

  • date: Tanggal permintaan dalam format YYYYMMDD. Contoh: 20231203.

  • region: ID wilayah Alibaba Cloud umum. Contoh: cn-hangzhou.

  • oss: Nama layanan yang diminta. Nilainya tetap oss.

  • aliyun_v4_request: Versi permintaan. Nilainya tetap aliyun_v4_request.

x-oss-date

String

Ya

Waktu permintaan dalam format ISO 8601. Contoh: 20231203T121212Z.

  • Diperbolehkan offset mundur hingga 15 menit. Artinya, server dapat menerima permintaan hingga 15 menit setelah waktu yang ditentukan dalam bidang x-oss-date. Desain ini mengakomodasi penundaan transmisi jaringan dan kesalahan sinkronisasi waktu antara klien dan server, sehingga permintaan tetap diproses meskipun jam klien sedikit tidak akurat.

  • Permintaan berlaku maksimal selama 7 hari sejak waktu yang ditentukan dalam x-oss-date. Jika nilai x-oss-date dalam permintaan dari klien menunjukkan bahwa permintaan telah kedaluwarsa (lebih dari 7 hari), server OSS akan menolak permintaan tersebut dan mengembalikan pesan error. Hal ini menjamin ketepatan waktu dan keamanan permintaan serta mencegah pengiriman permintaan lama yang telah ditandatangani secara jahat.

  • Waktu yang ditentukan oleh x-oss-date digunakan sebagai TimeStamp dalam StringToSign, dan nilainya harus berada pada hari yang sama dengan Date yang digunakan untuk membuat signing key serta identik dengan nilai x-oss-date dalam policy.

x-oss-signature

String

Ya

Tanda tangan yang digunakan untuk otentikasi. Nilainya dihitung dengan melakukan hashing terhadap string policy yang telah diencode Base64 menggunakan algoritma HMAC-SHA256, lalu mengonversi hasilnya menjadi string heksadesimal.

Policy

Bidang formulir policy mendefinisikan izin dan batasan untuk unggahan file ke OSS melalui formulir HTML. Ditulis dalam format JSON, policy membatasi unggahan dengan menentukan parameter seperti nama bucket yang diizinkan, awalan objek, waktu kedaluwarsa, metode HTTP yang diizinkan, batas ukuran konten, dan batas tipe konten.

Penting

Policy harus mencakup bidang expiration dan conditions. Bidang conditions pada contoh berikut mencakup parameter opsional x-oss-security-token. Parameter ini hanya diperlukan saat Anda menggunakan kredensial akses temporary dari STS untuk menghasilkan tanda tangan POST. Jika Anda menggunakan AccessKey jangka panjang untuk menghasilkan tanda tangan POST, jangan sertakan bidang 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"]]
  ]
}

Parameter policy dijelaskan sebagai berikut:

  • expiration

    Menentukan waktu kedaluwarsa policy dalam format ISO 8601 GMT. Misalnya, nilai 2023-12-03T13:00:00.000Z berarti permintaan POST harus diajukan sebelum pukul 13.00 pada 3 Desember 2023.

  • conditions

    Menentukan kondisi untuk bidang formulir dalam permintaan POST.

    Parameter

    Jenis

    Wajib

    Deskripsi

    Jenis pencocokan

    bucket

    String

    Tidak

    Nama bucket.

    bucket

    x-oss-signature-version

    String

    Ya

    Versi dan algoritma tanda tangan. Nilainya tetap OSS4-HMAC-SHA256.

    x-oss-signature-version

    x-oss-credential

    String

    Ya

    Menentukan kumpulan parameter untuk kunci turunan. Formatnya sebagai berikut:

    <AccessKeyId>/<date>/<region>/oss/aliyun_v4_request
    • AccessKeyId: ID AccessKey Anda.

    • date: Tanggal permintaan.

    • region: ID wilayah Alibaba Cloud umum. Contoh: cn-hangzhou.

    • oss: Nama layanan yang diminta. Nilainya tetap oss.

    • aliyun_v4_request: Versi permintaan. Nilainya tetap aliyun_v4_request.

    x-oss-credential

    x-oss-security-token

    String

    Tidak

    Hanya diperlukan saat Anda menggunakan kredensial akses temporary dari STS untuk menghasilkan tanda tangan POST. Anda dapat memperoleh token keamanan dengan memanggil operasi AssumeRole dari STS.

    x-oss-security-token

    x-oss-date

    String

    Ya

    Waktu permintaan dalam format ISO 8601. Contoh: 20231203T121212Z.

    • Diperbolehkan offset mundur hingga 15 menit. Artinya, waktu aktual saat server menerima permintaan dapat hingga 15 menit lebih lambat daripada waktu yang ditentukan dalam header x-oss-date. Desain ini terutama mengakomodasi penundaan transmisi jaringan dan potensi kesalahan sinkronisasi waktu antara klien dan server, sehingga permintaan tetap dapat diproses secara normal dalam rentang tertentu meskipun waktu klien sedikit tidak akurat.

    • Permintaan berlaku maksimal selama 7 hari sejak waktu yang ditentukan dalam header x-oss-date. Jika nilai x-oss-date dalam permintaan menunjukkan bahwa permintaan telah kedaluwarsa (lebih dari 7 hari), server OSS akan menolak permintaan tersebut dan mengembalikan pesan error. Desain ini menjamin ketepatan waktu dan keamanan permintaan serta mencegah pengiriman permintaan lama yang telah ditandatangani secara jahat.

    • Bidang x-oss-date menentukan waktu yang digunakan sebagai TimeStamp dalam StringToSign. Nilai bidang ini harus berada pada hari yang sama dengan Date yang digunakan untuk membuat signing key dan harus sama dengan nilai bidang x-oss-date dalam formulir.

    x-oss-date

    content-length-range

    String

    Tidak

    Ukuran minimum dan maksimum objek yang diizinkan untuk diunggah, dalam byte.

    content-length-range

    success_action_status

    String

    Tidak

    Kode status HTTP yang dikembalikan setelah unggahan berhasil.

    eq, eq-ci, starts-with, starts-with-ci, in, in-ci, not-in, not-in-ci

    key

    String

    Tidak

    Nama objek yang akan diunggah.

    eq, eq-ci, starts-with, starts-with-ci, in, in-ci, not-in, not-in-ci

    content-type

    String

    Tidak

    Membatasi tipe file unggahan.

    eq, eq-ci, starts-with, starts-with-ci, in, in-ci, not-in, not-in-ci

    cache-control

    String

    Tidak

    Menentukan perilaku caching objek.

    eq, eq-ci, starts-with, starts-with-ci, in, in-ci, not-in, not-in-ci

Jenis pencocokan untuk kondisi

Jenis pencocokan

Deskripsi

content-length-range

Menentukan ukuran minimum dan maksimum file yang diizinkan untuk diunggah. Misalnya, untuk mengizinkan ukuran file dari 1 hingga 10 byte, gunakan ["content-length-range", 1, 10].

eq

Nilai bidang formulir harus persis sesuai dengan nilai yang ditentukan. Misalnya, untuk mewajibkan bidang key bernilai tepat "a", gunakan ["eq", "$key", "a"].

starts-with

Nilai bidang formulir harus diawali dengan awalan yang ditentukan. Misalnya, untuk mewajibkan nilai key diawali dengan "user/user1", gunakan ["starts-with", "$key", "user/user1"].

in

Menentukan daftar nilai yang diizinkan untuk bidang formulir. Misalnya, untuk memverifikasi bahwa gambar yang diunggah berupa JPG atau PNG, gunakan ["in", "$content-type", ["image/jpg", "image/png"]].

not-in

Menentukan daftar nilai yang tidak diizinkan untuk bidang formulir. Misalnya, untuk melarang nilai "no-cache" pada cache-control, gunakan ["not-in", "$cache-control", ["no-cache"]].

eq-ci

Nilai bidang formulir harus persis sesuai dengan nilai yang ditentukan, tanpa membedakan huruf besar/kecil. Perbandingan dilakukan pada string huruf kecil. Misalnya, dengan ["eq-ci", "$key", "AbC"], nama objek seperti "abc", "ABC", dan "aBc" semuanya merupakan kecocokan yang valid.

starts-with-ci

Nilai bidang formulir harus diawali dengan awalan yang ditentukan, tanpa membedakan huruf besar/kecil. Perbandingan dilakukan pada string huruf kecil. Misalnya, dengan ["starts-with-ci", "$key", "User/"], nilai yang diawali dengan "user/", "USER/", atau "User/" semuanya merupakan kecocokan yang valid.

in-ci

Menentukan daftar nilai yang diizinkan, tanpa membedakan huruf besar/kecil. Perbandingan dilakukan pada string huruf kecil. Misalnya, dengan ["in-ci", "$content-type", ["IMAGE/JPG", "image/PNG"]], nilai seperti "image/jpg" dan "IMAGE/PNG" keduanya merupakan kecocokan yang valid.

not-in-ci

Menentukan daftar nilai yang tidak diizinkan, tanpa membedakan huruf besar/kecil. Perbandingan dilakukan pada string huruf kecil. Misalnya, dengan ["not-in-ci", "$cache-control", ["No-Cache"]], nilai seperti "no-cache", "NO-CACHE", dan "No-Cache" semuanya dikecualikan.

Catatan

Saat conditions dalam policy berisi beberapa pernyataan kondisional untuk bidang formulir yang sama—misalnya in dan not-in—pernyataan terakhir yang berlaku. Hindari menetapkan kondisi yang saling bertentangan secara semantik untuk bidang formulir yang sama agar tidak menghasilkan validasi yang tidak terduga.

Karakter escape untuk policy

Dalam policy, tanda dolar ($) menunjukkan variabel. Untuk menggunakan tanda dolar literal, Anda harus meng-escape-nya sebagai \$. Tabel berikut mencantumkan karakter escape yang diperlukan untuk JSON policy.

Karakter escape

Deskripsi

\/

Garis miring

\\

Garis miring terbalik

\

Tanda kutip ganda

\$

Tanda dolar

\b

Spasi

\f

Form feed

\n

Baris baru

\r

Carriage return

\t

Tab horizontal

\uxxxx

Karakter Unicode

Proses perhitungan tanda tangan

  1. Buat policy yang diencode UTF-8.

  2. Buat string yang akan ditandatangani.

    Encode policy dengan Base64 untuk menghasilkan string aman. String yang diencode ini merupakan string yang akan ditandatangani.

  3. Hitung signing key.

    Gunakan AccessKey Secret Anda untuk melakukan serangkaian operasi penghashan HMAC-SHA256. Proses ini menghasilkan kunci turunan akhir, yang dikenal sebagai signing key, yang spesifik untuk tanggal, wilayah, dan layanan.

  4. Hitung tanda tangan.

    Tandatangani string yang akan ditandatangani dengan signing key menggunakan algoritma HMAC-SHA256. Konversi hash biner yang dihasilkan menjadi string heksadesimal. String heksadesimal ini merupakan tanda tangan akhir yang memverifikasi integritas dan keaslian permintaan.

Contoh perhitungan tanda tangan POST

  1. Kode Java berikut memberikan contoh lengkap cara menghitung tanda tangan POST untuk policy yang dijelaskan sebelumnya.

    Kredensial 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 {
            // Sebelum menjalankan kode ini, pastikan variabel lingkungan OSS_ACCESS_KEY_ID dan OSS_ACCESS_KEY_SECRET telah disetel.
            String accesskeyid =  System.getenv().get("OSS_ACCESS_KEY_ID");
            String accesskeysecret =  System.getenv().get("OSS_ACCESS_KEY_SECRET");
    
            // Langkah 1: Buat policy.
            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);
            // Langkah 2: Buat string yang akan ditandatangani.
            String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
            System.out.println(stringToSign);
            
            // Langkah 3: Hitung signing key.
            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");
    
            // Langkah 4: Hitung tanda tangan.
            byte[] result = hmacsha256(signingKey, stringToSign);
            String signature = BinaryUtil.toHex(result);
            System.out.println("signature:" + signature);
    
        }
    
        public static byte[] hmacsha256(byte[] key, String data) {
            try {
                // Inisialisasi spesifikasi kunci HMAC, tentukan HmacSHA256 sebagai algoritma dan gunakan kunci yang diberikan.
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
    
                // Dapatkan instance Mac, tentukan HmacSHA256 sebagai algoritma.
                Mac mac = Mac.getInstance("HmacSHA256");
                // Inisialisasi objek Mac dengan kunci.
                mac.init(secretKeySpec);
    
                // Lakukan perhitungan HMAC. Metode doFinal mengambil data dan mengembalikan hash hasilnya sebagai array byte.
                byte[] hmacBytes = mac.doFinal(data.getBytes());
    
                return hmacBytes;
            } catch (Exception e) {
                throw new RuntimeException("Gagal menghitung HMAC-SHA256", e);
            }
        }
    }

    Hasil berikut dikembalikan:

    signature:3908473f7dbfb79a102eaaa44ca1edec8d7058ce3bd1c624d59eb437463bd5d6

    Kredensial akses temporary

    Saat Anda menggunakan kredensial akses temporary dari STS untuk menghasilkan tanda tangan POST, Anda juga harus memperoleh token keamanan dengan memanggil operasi AssumeRole dari STS.

    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 {
    
        // Inisialisasi klien STS.
        public static com.aliyun.sts20150401.Client createStsClient() throws Exception {
            com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                    // Wajib. Pastikan variabel lingkungan OSS_ACCESS_KEY_ID telah disetel.
                    .setAccessKeyId(System.getenv("OSS_ACCESS_KEY_ID"))
                    // Wajib. Pastikan variabel lingkungan OSS_ACCESS_KEY_SECRET telah disetel.
                    .setAccessKeySecret(System.getenv("OSS_ACCESS_KEY_SECRET"));
            // Titik akhir 
            config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
            return new com.aliyun.sts20150401.Client(config);
        }
    
        // Peroleh kredensial akses temporary dari 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()
                    // Wajib. Pastikan variabel lingkungan OSS_STS_ROLE_ARN telah disetel.
                    .setRoleArn(System.getenv("OSS_STS_ROLE_ARN"))
                    .setRoleSessionName("role_session_name");// Nama sesi kustom.
            com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
            try {
                // Jika Anda menyalin kode ini, cetak respons API sendiri.
                AssumeRoleResponse response = client.assumeRoleWithOptions(assumeRoleRequest, runtime);
                // Objek credentials berisi AccessKeyId, AccessKeySecret, dan SecurityToken untuk operasi selanjutnya.
                return response.body.credentials;
            } catch (TeaException error) {
                // Ini hanya untuk demonstrasi. Di lingkungan produksi, tangani exception dengan hati-hati dan jangan abaikan.
                // Pesan error
                System.out.println(error.getMessage());
                // URL pemecahan masalah
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
            } catch (Exception _error) {
                TeaException error = new TeaException(_error.getMessage(), _error);
                // Ini hanya untuk demonstrasi. Di lingkungan produksi, tangani exception dengan hati-hati dan jangan abaikan.
                // Pesan error
                System.out.println(error.getMessage());
                // URL pemecahan masalah
                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;
            // Langkah 1: Buat policy.
            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);
            // Langkah 2: Buat string yang akan ditandatangani.
            String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
    
            // Langkah 3: Hitung signing key.
            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");
    
            // Langkah 4: Hitung tanda tangan.
            byte[] result = hmacsha256(signingKey, stringToSign);
            String signature = BinaryUtil.toHex(result);
            System.out.println("signature:" + signature);
    
        }
    
        public static byte[] hmacsha256(byte[] key, String data) {
            try {
                // Inisialisasi spesifikasi kunci HMAC, tentukan HmacSHA256 sebagai algoritma dan gunakan kunci yang diberikan.
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
    
                // Dapatkan instance Mac, tentukan HmacSHA256 sebagai algoritma.
                Mac mac = Mac.getInstance("HmacSHA256");
                // Inisialisasi objek Mac dengan kunci.
                mac.init(secretKeySpec);
    
                // Lakukan perhitungan HMAC. Metode doFinal mengambil data dan mengembalikan hash hasilnya sebagai array byte.
                byte[] hmacBytes = mac.doFinal(data.getBytes());
    
                return hmacBytes;
            } catch (Exception e) {
                throw new RuntimeException("Gagal menghitung HMAC-SHA256", e);
            }
        }
    }

    Hasil berikut dikembalikan:

    signature:1e09438f7ad01af6b3e144b42c98929c68f8d090ce07f4c277b18d8b62d0aa02
  2. Kode Python berikut memberikan contoh lengkap cara menghitung tanda tangan 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()
    
    # Baca ID AccessKey dan AccessKey Secret dari variabel lingkungan.
    accesskeyid = os.getenv('OSS_ACCESS_KEY_ID')
    accesskeysecret = os.getenv('OSS_ACCESS_KEY_SECRET')
    
    # Cetak ID AccessKey.
    print(accesskeyid)
    
    # Periksa apakah variabel lingkungan berhasil diambil.
    if not accesskeyid or not accesskeysecret:
        raise ValueError("Variabel lingkungan yang diperlukan tidak ditemukan: OSS_ACCESS_KEY_ID atau OSS_ACCESS_KEY_SECRET")
    
    # Buat policy.
    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"}}
      ]
    }}'''
    
    # Cetak policy.
    print(policy)
    
    # Hitung string yang akan ditandatangani.
    string_to_sign = base64.b64encode(policy.encode('utf-8')).decode('utf-8')
    print(string_to_sign)
    
    # Hitung signing key.
    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")
    
    # Hitung tanda tangan.
    result = hmac_sha256(signing_key, string_to_sign)
    signature = result.hex()
    print("signature:", signature)

    Hasil berikut dikembalikan:

    signature:9e85d56429245283b1aca5bc2dc31e0020b95ac2de9e9b81b496994db602ba1e