자체 프로그램을 사용한 V4 서명 프로세스

이 페이지에서는 선택한 프로그래밍 언어를 사용하여 자체 워크플로에 Cloud Storage RSA 키 서명 URL을 만들 수 있도록 V4 서명 프로세스를 구현하는 알고리즘을 설명합니다. 서명된 URL은 제한된 시간 동안 특정 Cloud Storage 리소스에 읽기 또는 쓰기 액세스 권한을 제공합니다. 서명된 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을 사용할 때 사용자가 요청에 포함해야 하는 요소를 정의합니다.

    필요한 부분과 형식에 대한 자세한 내용은 정규 요청을 참조하세요.

  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 앱 ID 서비스를 사용하여 문자열에 서명할 수 있습니다.

  5. 다음 연결을 사용하여 서명된 URL을 구성합니다.

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

    서명된 URL에는 다음과 같은 구성요소가 있습니다.

    • HOSTNAME: 이는 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
import 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(six.ensure_binary(object_name), safe=b'/~')
    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

다음 단계

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

도움이 필요하시나요? 지원 페이지를 방문하세요.