Processo di firma V4 con il tuo programma

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Questa pagina descrive un algoritmo per l'implementazione del processo di firma V4 in modo da poter creare gli URL firmati con la chiave RSA di Cloud Storage nel tuo flusso di lavoro utilizzando un linguaggio di programmazione a tua scelta.

Prerequisiti

Prima di creare un programma che implementa la procedura di firma V4, devi:

  1. Genera una nuova chiave privata o usa una chiave privata esistente per un account di servizio. La chiave può essere in formato JSON o PKCS12.

    Per ulteriori informazioni sulle chiavi private e sugli account di servizio, consulta Account di servizio.

  2. Concedi all'account di servizio un'autorizzazione sufficiente in modo che possa eseguire la richiesta effettuata dall'URL firmato.

    Ad esempio, se l'URL firmato consente a un utente di scaricare un oggetto, l'account di servizio deve disporre dell'autorizzazione storage.objects.get per l'oggetto.

Algoritmo per la firma degli URL

Il tuo programma deve includere i seguenti passaggi:

  1. Crea la richiesta canonica come stringa. La richiesta canonica definisce gli elementi che gli utenti devono includere nella loro richiesta quando utilizzano l'URL firmato.

    Consulta la sezione Richieste canoniche per conoscere i dettagli sulle parti e sul formato richiesti.

  2. Crea la stringa da firmare. La stringa da firmare è la base per creare una firma e include al suo interno il valore hash con codifica esadecimale della richiesta canonica.

    Consulta la sezione Firme per informazioni dettagliate sul formato della stringa da firmare.

  3. Firma la stringa da firmare utilizzando una firma RSA con SHA-256. Il risultato di questa firma è la tua firma di richiesta.

    Il tuo linguaggio di programmazione deve avere una libreria per eseguire le firme RSA. In alternativa, puoi utilizzare il metodo IAM signBlob fornito da Google Cloud se la tua scadenza è di massimo 12 ore.

  4. Crea l'URL firmato utilizzando la seguente concatenazione:

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

    L'URL firmato ha i seguenti componenti:

    • HOSTNAME: dovrebbe essere https://storage.googleapis.com.

    • PATH_TO_RESOURCE: dovrebbe corrispondere al valore utilizzato per creare la richiesta canonica.

    • CANONICAL_QUERY_STRING: dovrebbe corrispondere ai valori che hai utilizzato per creare la richiesta canonica.

    • REQUEST_SIGNATURE: si tratta dell'output di una firma RSA nel passaggio precedente.

    Di seguito è riportato un esempio di URL completato:

    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

Programma di esempio Python

Puoi utilizzare le librerie client di Cloud Storage per creare URL firmati per molti linguaggi di programmazione comuni. Consulta Processo di firma V4 con strumenti di Cloud Storage per esempi.

L'esempio seguente mostra un'implementazione dell'algoritmo per la firma degli URL che non utilizzano le librerie client di Cloud Storage. L'esempio utilizza il linguaggio di programmazione Python, ma può essere adattato al linguaggio di tua scelta.

import binascii
import collections
import datetime
import hashlib
import sys

# pip install google-auth
from google.oauth2 import service_account
# pip install six
import six
from six.moves.urllib.parse import quote

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

    if headers is None:
        headers = dict()
    host = '{}.storage.googleapis.com'.format(bucket_name)
    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 += '{}:{}\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
    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 += '{}={}&'.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])

    # 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

Passaggi successivi