Proses penandatanganan V4 dengan program Anda sendiri

Halaman ini berisi penjelasan mengenai algoritma untuk mengimplementasikan proses penandatanganan V4 agar Anda dapat membuat URL bertanda tangan kunci RSA Cloud Storage dalam alur kerja Anda sendiri, menggunakan bahasa pemrograman pilihan Anda.

Sebelum memulai

Sebelum membuat program yang mengimplementasikan proses penandatanganan V4, Anda harus menyelesaikan langkah-langkah berikut:

  1. Membuat akun layanan. Jika sudah memiliki akun layanan, Anda dapat melewati langkah ini.

  2. Berikan izin yang cukup ke akun layanan sehingga dapat menjalankan permintaan yang akan dibuat oleh URL yang ditandatangani.

    Misalnya, jika URL yang ditandatangani akan mengizinkan pengguna membaca data objek, akun layanan itu sendiri harus memiliki izin untuk membaca data objek.

  3. Jika algoritma penandatanganan yang ingin Anda gunakan tidak terintegrasi dalam Google Cloud, buat kunci pribadi baru, atau miliki kunci pribadi untuk layanan tersebut. Kunci dapat menggunakan format JSON atau PKCS12.

Algoritma untuk menandatangani URL

Program Anda harus mencakup langkah-langkah berikut:

  1. Buat permintaan kanonis sebagai string. Permintaan kanonis menentukan elemen yang harus disertakan pengguna dalam permintaan mereka saat menggunakan URL yang ditandatangani.

    Lihat Permintaan Kanonis untuk mengetahui detail tentang bagian dan format yang diperlukan.

  2. Buat string-to-sign. String-to-sign adalah dasar pembuatan tanda tangan dan menyertakan di dalamnya nilai hash berenkode hex dari permintaan kanonis.

    Lihat Tanda tangan untuk detail tentang format string-to-sign.

  3. Tanda tangani string-to-sign menggunakan tanda tangan RSA dengan SHA-256. Hasil penandatanganan ini adalah tanda tangan permintaan Anda. Ada beberapa opsi untuk penandatanganan:

    • Anda dapat menggunakan metode signBlob IAM yang disediakan oleh Google Cloud.

    • Anda dapat menggunakan bahasa pemrograman yang memiliki library untuk menjalankan tanda tangan RSA.

  4. Buat URL bertanda tangan menggunakan penyambungan berikut:

    HOSTNAME + PATH_TO_RESOURCE + "?" + CANONICAL_QUERY_STRING + "&X-Goog-Signature=" + REQUEST_SIGNATURE

    URL yang ditandatangani memiliki komponen berikut:

    • HOSTNAME: Ini harusnya https://storage.googleapis.com.

    • PATH_TO_RESOURCE: Nilai ini harus cocok dengan nilai yang Anda gunakan dalam membuat permintaan kanonis.

    • CANONICAL_QUERY_STRING: Nilai ini harus cocok dengan nilai yang Anda gunakan dalam membuat permintaan kanonis.

    • REQUEST_SIGNATURE: Ini adalah output dari penggunaan tanda tangan RSA di langkah sebelumnya.

    Berikut contoh URL lengkap:

    https://storage.googleapis.com/example-bucket/cat.jpeg?X-Goog-Algorithm=GOOG4-
    RSA-SHA256&X-Goog-Credential=example%40example-project.iam.gserviceaccount.com
    %2F20181026%2Fus%2Fstorage%2Fgoog4_request&X-Goog-Date=20181026T211942Z&X-Goog
    -expires=3600&X-Goog-Signedheaders=host&X-Goog-Signature=2d2a6f5055eb004b8690b
    9479883292ae7450cdc15f17d7f99bc49b916f9e7429106ed7e5858ae6b4ab0bbbdb1a8ccc364d
    ad3a0da2caebd30887a70c5b2569d089ceb8afbde3eed4dff5086f0db5483998c175980991fe89
    9fbd2cd8cb813b00165e8d56e0a8aa7b3d7a12ee1baa8400611040f05b50a1a8eab5ba223fe137
    5747748de950ec7a4dc50f8382a6ffd4994ac42498d7daa703d9a414d4475154d0e7edaa92d4f2
    507d92c1f7e8efa7cab64df68b5df48575b9259d8d0bdb5dc752bdf07bd162d98ff2924f2e4a26
    fa6b3cede73ad5333c47d146a21c2ab2d97115986a12c68ff37346d6c2ca83e56b8ec8ad956327
    10b489b75c35697d781c38e

Program contoh Python

Anda dapat menggunakan library klien Cloud Storage untuk membuat URL bertanda tangan untuk banyak bahasa pemrograman umum. Lihat proses penandatanganan V4 dengan alat Cloud Storage untuk mengetahui contohnya.

Contoh berikut menunjukkan implementasi algoritma untuk menandatangani URL yang tidak menggunakan library klien Cloud Storage. Contoh ini menggunakan bahasa pemrograman Python, tetapi dapat disesuaikan dengan bahasa pilihan Anda.

import binascii
import collections
import datetime
import hashlib
import sys
from urllib.parse import quote

# pip install google-auth
from google.oauth2 import service_account

# pip install six
import six

def generate_signed_url(
    service_account_file,
    bucket_name,
    object_name,
    subresource=None,
    expiration=604800,
    http_method="GET",
    query_parameters=None,
    headers=None,
):
    if expiration > 604800:
        print("Expiration Time can't be longer than 604800 seconds (7 days).")
        sys.exit(1)

    escaped_object_name = quote(six.ensure_binary(object_name), safe=b"/~")
    canonical_uri = f"/{escaped_object_name}"

    datetime_now = datetime.datetime.now(tz=datetime.timezone.utc)
    request_timestamp = datetime_now.strftime("%Y%m%dT%H%M%SZ")
    datestamp = datetime_now.strftime("%Y%m%d")

    google_credentials = service_account.Credentials.from_service_account_file(
        service_account_file
    )
    client_email = google_credentials.service_account_email
    credential_scope = f"{datestamp}/auto/storage/goog4_request"
    credential = f"{client_email}/{credential_scope}"

    if headers is None:
        headers = dict()
    host = f"{bucket_name}.storage.googleapis.com"
    headers["host"] = host

    canonical_headers = ""
    ordered_headers = collections.OrderedDict(sorted(headers.items()))
    for k, v in ordered_headers.items():
        lower_k = str(k).lower()
        strip_v = str(v).lower()
        canonical_headers += f"{lower_k}:{strip_v}\n"

    signed_headers = ""
    for k, _ in ordered_headers.items():
        lower_k = str(k).lower()
        signed_headers += f"{lower_k};"
    signed_headers = signed_headers[:-1]  # remove trailing ';'

    if query_parameters is None:
        query_parameters = dict()
    query_parameters["X-Goog-Algorithm"] = "GOOG4-RSA-SHA256"
    query_parameters["X-Goog-Credential"] = credential
    query_parameters["X-Goog-Date"] = request_timestamp
    query_parameters["X-Goog-Expires"] = expiration
    query_parameters["X-Goog-SignedHeaders"] = signed_headers
    if subresource:
        query_parameters[subresource] = ""

    canonical_query_string = ""
    ordered_query_parameters = collections.OrderedDict(sorted(query_parameters.items()))
    for k, v in ordered_query_parameters.items():
        encoded_k = quote(str(k), safe="")
        encoded_v = quote(str(v), safe="")
        canonical_query_string += f"{encoded_k}={encoded_v}&"
    canonical_query_string = canonical_query_string[:-1]  # remove trailing '&'

    canonical_request = "\n".join(
        [
            http_method,
            canonical_uri,
            canonical_query_string,
            canonical_headers,
            signed_headers,
            "UNSIGNED-PAYLOAD",
        ]
    )

    canonical_request_hash = hashlib.sha256(canonical_request.encode()).hexdigest()

    string_to_sign = "\n".join(
        [
            "GOOG4-RSA-SHA256",
            request_timestamp,
            credential_scope,
            canonical_request_hash,
        ]
    )

    # signer.sign() signs using RSA-SHA256 with PKCS1v15 padding
    signature = binascii.hexlify(
        google_credentials.signer.sign(string_to_sign)
    ).decode()

    scheme_and_host = "{}://{}".format("https", host)
    signed_url = "{}{}?{}&x-goog-signature={}".format(
        scheme_and_host, canonical_uri, canonical_query_string, signature
    )

    return signed_url

Langkah selanjutnya