独自のプログラムを使用した V4 署名プロセス

は、

このページでは、選択したプログラミング言語を使用して、独自のワークフローで Cloud Storage RSA 鍵署名付き URL を作成するための、V4 署名プロセスを実装するアルゴリズムについて説明します。署名付き URL は、Cloud Storage の特定のリソースに対する期限付きの読み取りまたは書き込みアクセスを提供します。署名付き URL は、その URL を所有する全員が、Google アカウントを持っているかどうかにかかわらず、URL がアクティブである限り使用できます。

Cloud Storage のツールを使用して、Cloud Storage RSA 鍵署名付き URL をより簡単に作成する方法については、Cloud Storage ツールを使用した V4 署名プロセスをご覧ください。署名付き URL について詳しくは、署名付き URL の概要をご覧ください。

前提条件

V4 署名プロセスを実装するプログラムを作成する前に、次の手順を行ってください。

  1. 新しい秘密鍵を生成するかまたは、サービス アカウント用の既存の秘密鍵を準備してください。鍵は、JSON 形式か PKCS12 形式のものを使用できます。

    秘密鍵とサービス アカウントについて詳しくは、サービス アカウントをご覧ください。

  2. 署名付き URL によるリクエストを実行できるように、サービス アカウントに十分な権限を付与してください。

    たとえば、署名付き URL でユーザーがオブジェクトをダウンロードできるようにするには、サービス アカウントにそのオブジェクトに対する storage.objects.get 権限が必要です。

URL に署名するアルゴリズム

プログラムには次の手順が含まれている必要があります。

  1. 正規リクエストを文字列として作成します。正規リクエストにより、署名付き URL を使用するときにユーザーがリクエストに含める必要がある要素を定義します。

    正規リクエストの構造は、各要素間の改行の使用を含めて、次のようにする必要があります。

    HTTP_VERB
    PATH_TO_RESOURCE
    CANONICAL_QUERY_STRING
    CANONICAL_HEADERS
    
    SIGNED_HEADERS
    UNSIGNED-PAYLOAD

    正規リクエストには次のコンポーネントが含まれます。

    • HTTP_VERB: 署名付き URL とともに使用される HTTP 動詞。

      使用できる値については、HTTP 動詞をご覧ください。

    • PATH_TO_RESOURCE: ホスト名の後に配置されるリソースへのパス。リソースへのパスを定義する際に、次のような予約済み文字をパーセント エンコードする必要があります。?=!#$&'()*+,:;@[]."

    • CANONICAL_QUERY_STRING: クエリ文字列パラメータ。署名付き URL を使用するリクエストには必ず含める必要があります。クエリ文字列パラメータを、アルファベット順に並べ替えそれぞれ & で区切って追加します。

    • CANONICAL_HEADERS: リクエスト ヘッダーを構成する「名前 : 値」のペア。署名付き URL を使用したリクエストには必ず含まれている必要があります(拡張ヘッダーなど)。ヘッダーをアルファベット順に並べ替えてそれぞれ \n で区切って追加します。

    • SIGNED_HEADERS: CANONICAL_HEADERS のヘッダー名。; で区切って示します。

    • UNSIGNED-PAYLOAD: この文字列を正規リクエストの最後の行に配置します。

  2. SHA-256 ハッシュ関数を使用して、正規リクエストの 16 進エンコード ハッシュ値を作成します。

    使用するプログラミング言語には、SHA-256 ハッシュを作成するためのライブラリが提供されていなければなりません。たとえば、ハッシュ値は次のようになります。

    436b7ce722d03b17d3f790255dd57904f7ed61c02ac5127a0ca8063877e4e42c
  3. 署名対象文字列を作成します。

    各要素間の改行の使用も含めて、署名対象文字列の構造は次のようになります。

    SIGNING_ALGORITHM
    CURRENT_DATETIME
    CREDENTIAL_SCOPE
    HASHED_CANONICAL_REQUEST

    署名対象文字列には次のコンポーネントが含まれます。

    • SIGNING_ALGORITHM: GOOG4-RSA-SHA256 である必要があります。

    • CURRENT_DATETIME: ISO 8601 の基本形式 YYYYMMDD'T'HHMMSS'Z' で表した現在の日付と時刻。

    • CREDENTIAL SCOPE: 署名対象文字列に署名するためのリクエストの認証情報スコープ

    • HASHED_CANONICAL_REQUEST: 前のステップで作成した正規リクエストの 16 進エンコードの SHA-256 ハッシュ。

  4. SHA-256 の RSA 署名を使用して署名対象文字列に署名します。この署名の結果がリクエストの署名になります。

    使用するプログラミング言語には、RSA 署名を実行するためのライブラリが提供されていなければなりません。Google App Engine アプリケーション内で、App Engine App Identity サービスを使って文字列を署名できます。

  5. 次の文字列結合を使用して署名付き URL を作成します。

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

    署名付き URL には次のコンポーネントが含まれます。

    • ホスト名: https://storage.googleapis.com である必要があります。

    • PATH_TO_RESOURCE: 正規リクエストの作成に使用した値と一致している必要があります。

    • CANONICAL_QUERY_STRING: 正規リクエストの作成に使用した値と一致している必要があります。

    • REQUEST_SIGNATURE: 前のステップで RSA 署名を使用して得られた出力です。

    完成した URL の例は次のとおりです。

    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

Python のサンプル プログラム

次のサンプルは、URL に署名するアルゴリズムの実装を示しています。このサンプルは Python プログラミング言語を使用しています。

import binascii
import collections
import datetime
import hashlib
import sys

# pip install six
from six.moves.urllib.parse import quote

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

def generate_signed_url(service_account_file, bucket_name, object_name,
                        expiration, 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(object_name, safe='')
    canonical_uri = '/{}/{}'.format(bucket_name, escaped_object_name)

    datetime_now = datetime.datetime.utcnow()
    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 = '{}/auto/storage/goog4_request'.format(datestamp)
    credential = '{}/{}'.format(client_email, credential_scope)

    if headers is None:
        headers = dict()
    headers['host'] = 'storage.googleapis.com'

    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 += '{}:{}\n'.format(lower_k, strip_v)

    signed_headers = ''
    for k, _ in ordered_headers.items():
        lower_k = str(k).lower()
        signed_headers += '{};'.format(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

    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 += '{}={}&'.format(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])

    signature = binascii.hexlify(
        google_credentials.signer.sign(string_to_sign)
    ).decode()

    host_name = 'https://storage.googleapis.com'
    signed_url = '{}{}?{}&X-Goog-Signature={}'.format(host_name, canonical_uri,
                                                      canonical_query_string,
                                                      signature)
    return signed_url
このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。