Processo de assinatura V4 com seu próprio programa

Nesta página, você verá um algoritmo para implementar o processo de assinatura da V4, para que você possa criar URLs assinados com chave RSA do Cloud Storage no fluxo de trabalho usando uma linguagem de programação de sua escolha. Os URLs assinados fornecem acesso de leitura ou gravação por tempo limitado a um recurso específico do Cloud Storage. Qualquer pessoa que tiver o URL assinado poderá usá-lo enquanto ele estiver ativo, mesmo sem uma conta do Google.

Para saber como usar as ferramentas do Cloud Storage para criar mais facilmente URLs assinados com chave RSA do Cloud Storage, consulte Processo de assinatura da V4 com as Ferramentas do Cloud Storage. Para saber mais sobre URLs assinados, consulte a Visão geral de URLs assinados.

Pré-requisitos

Antes de criar um programa que implemente o processo de assinatura da V4, você precisa:

  1. Gerar uma nova chave privada ou ter uma chave privada atual para uma conta de serviço. A chave pode estar no formato JSON ou PKCS12.

    Para mais informações sobre chaves privadas e contas de serviço, consulte Contas de serviço.

  2. Conceda à conta de serviço permissão suficiente para que ela possa executar a solicitação que o URL assinado fará.

    Por exemplo, se seu URL assinado permitir que um usuário faça o download de um objeto, a conta de serviço precisará ter a permissão storage.objects.get no objeto.

Algoritmo para assinatura de URLs

Seu programa precisa incluir os seguintes passos:

  1. Construa o pedido canônico como uma string. A solicitação canônica define elementos que os usuários precisam incluir na solicitação quando usam seu URL assinado.

    A solicitação canônica precisa ter a seguinte estrutura, incluindo o uso de novas linhas entre cada elemento:

    HTTP_VERB
    PATH_TO_RESOURCE
    CANONICAL_QUERY_STRING
    CANONICAL_HEADERS
    
    SIGNED_HEADERS
    UNSIGNED-PAYLOAD

    A solicitação canônica tem os seguintes componentes:

    • HTTP_VERB: o verbo HTTP a ser usado com o URL assinado.

      Para uma lista de valores permitidos, consulte verbos HTTP.

    • PATH_TO_RESOURCE: o caminho para o recurso, começando após o nome do host. Ao definir o caminho para o recurso, codifique por cento os seguintes caracteres reservados: ?=!#$&'()*+,:;@[]."

    • CANONICAL_QUERY_STRING: os parâmetros da string de consulta que precisam estar inclusos nas solicitações que usam o URL assinado. Adicione os parâmetros da string de consulta em ordem alfabética e separe cada um por &.

    • CANONICAL_HEADERS: os pares de name:value para os cabeçalhos de solicitação que precisam ser incluídos nas solicitações que utilizam o URL assinado, incluindo cabeçalhos de extensão. Adicione cabeçalhos em ordem alfabética e separe cada um por \n.

    • SIGNED_HEADERS: os nomes dos cabeçalhos CANONICAL_HEADERS separados por ;.

    • UNSIGNED-PAYLOAD: essa string precisa aparecer na última linha da solicitação canônica.

  2. Use uma função hash SHA-256 para criar um valor de hash da solicitação canônica codificado em hexadecimal.

    Sua linguagem de programação precisa ter uma biblioteca para criar hashes SHA-256. Um exemplo de valor de hash é semelhante a este:

    436b7ce722d03b17d3f790255dd57904f7ed61c02ac5127a0ca8063877e4e42c
  3. Construa a string a ser assinada:

    A string a ser assinada precisa ter a seguinte estrutura, incluindo o uso de novas linhas entre cada elemento:

    SIGNING_ALGORITHM
    CURRENT_DATETIME
    CREDENTIAL_SCOPE
    HASHED_CANONICAL_REQUEST

    A string a ser assinada tem os seguintes componentes:

    • SIGNING_ALGORITHM: precisa ser GOOG4-RSA-SHA256.

    • CURRENT_DATETIME: a data e hora atuais, no formato básico ISO 8601 YYYYMMDD'T'HHMMSS'Z'.

    • CREDENTIAL SCOPE: o escopo da credencial da solicitação de assinatura da string para assinar.

    • HASHED_CANONICAL_REQUEST: o hash SHA-256 da solicitação canônica codificado em hexadecimal que você criou na etapa anterior.

  4. Assine a string usando uma assinatura RSA com SHA-256. O resultado dessa assinatura é sua assinatura de solicitação.

    A linguagem de programação precisa de uma biblioteca para executar assinaturas RSA. Em um aplicativo do Google App Engine, use o serviço de identidade de aplicativo do App Engine para assinar sua string.

  5. Construa o URL assinado usando a seguinte concatenação:

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

    O URL assinado tem os seguintes componentes:

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

    • PATH_TO_RESOURCE: precisa corresponder ao valor usado na construção da solicitação canônica.

    • CANONICAL_QUERY_STRING: precisa corresponder aos valores usados na construção da solicitação canônica.

    • REQUEST_SIGNATURE: é a saída do uso de uma assinatura RSA na etapa anterior.

    Veja um exemplo de URL completo:

    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 amostra em Python

A amostra a seguir mostra uma implementação do algoritmo para assinatura de URLs. O exemplo usa a linguagem de programação 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
Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.