Proceso de firma V4 con tu propio programa

En esta página, se describe un algoritmo para implementar el proceso de firma V4 a fin de que puedas crear URL firmadas con clave RSA de Cloud Storage en tu propio flujo de trabajo con el lenguaje de programación que elijas. Las URL firmadas otorgan acceso de lectura o escritura durante un tiempo limitado a un recurso específico de Cloud Storage. Cualquier persona que cuente con la URL firmada la puede usar mientras está activa, sin importar si tiene una Cuenta de Google.

Si deseas aprender a usar las herramientas de Cloud Storage para crear URL firmadas con clave RSA de Cloud Storage con mayor facilidad, consulta el proceso de firma V4 con las herramientas de Cloud Storage. Para obtener más información sobre las URL firmadas, consulta la descripción general de las URL firmadas.

Requisitos previos

Antes de crear un programa que implemente el proceso de firma V4, debes hacer lo siguiente:

  1. Genera una nueva clave privada o consigue una clave privada existente para una cuenta de servicio. La clave puede estar en formato JSON o PKCS12.

    Para obtener más información sobre las claves privadas y cuentas de servicio, consulta Cuentas de servicio.

  2. Otorga suficientes permisos a la cuenta de servicio para que pueda llevar a cabo la solicitud que realizará la URL firmada.

    Por ejemplo, si tu URL firmada permitirá que un usuario descargue un objeto, la cuenta de servicio debe tener el permiso storage.objects.get sobre el objeto.

Algoritmo para firmar URL

Tu programa debe incluir los siguientes pasos:

  1. Construir la solicitud canónica como una string. La solicitud canónica define elementos que los usuarios deben incluir en su solicitud cuando usan tu URL firmada.

    Consulta las solicitudes canónicas para obtener detalles sobre las piezas y el formato requeridos.

  2. Usa una función de hash SHA-256 para crear un valor de hash codificado en hexadecimal de la solicitud canónica.

    Tu lenguaje de programación debe tener una biblioteca para crear hashes SHA-256. Un valor de hash de ejemplo se ve así:

    436b7ce722d03b17d3f790255dd57904f7ed61c02ac5127a0ca8063877e4e42c
  3. Construir la string para firmar.

    La string para firmar debe tener la siguiente estructura, incluido el uso de caracteres de línea nueva entre cada elemento:

    SIGNING_ALGORITHM
    CURRENT_DATETIME
    CREDENTIAL_SCOPE
    HASHED_CANONICAL_REQUEST

    La string para firmar tiene los siguientes componentes:

    • SIGNING_ALGORITHM: Esto debería ser GOOG4-RSA-SHA256.

    • CURRENT_DATETIME: la fecha y la hora actuales, en el formato básico YYYYMMDD'T'HHMMSS'Z' de ISO 8601.

    • CREDENTIAL SCOPE: El alcance de credencial de la solicitud que se usará con la string para firmar.

    • HASHED_CANONICAL_REQUEST: El hash SHA-256 codificado en hexadecimal de la solicitud canónica que creaste en el paso anterior.

  4. Usa una firma RSA con SHA-256 en la string para firmar. El resultado de esta firma es tu firma de solicitud.

    Tu lenguaje de programación debe tener una biblioteca para realizar firmas RSA. En una aplicación de Google App Engine, puedes usar el servicio de identidades de apps de App Engine para firmar la string.

  5. Construye la URL firmada con la siguiente concatenación:

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

    La URL firmada tiene los siguientes componentes:

    • HOSTNAME: Debe ser https://storage.googleapis.com.

    • PATH_TO_RESOURCE: Debe coincidir con el valor que usaste para construir la solicitud canónica.

    • CANONICAL_QUERY_STRING: Debe coincidir con los valores que usaste para construir la solicitud canónica.

    • REQUEST_SIGNATURE: Este es el resultado del uso de una firma RSA en el paso anterior.

    Aquí hay un ejemplo de URL completada:

    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

Programa de ejemplo de Python

En el siguiente ejemplo, se muestra una implementación del algoritmo para firmar URL. El ejemplo usa el lenguaje de programación 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

Próximos pasos

¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Si necesitas ayuda, visita nuestra página de asistencia.