Guia de assinatura digital

Assinar digitalmente sua solicitação com uma chave de API

Dependendo do seu caso de uso, uma assinatura digital e uma chave de API serão necessárias para autenticar as solicitações. Consulte os seguintes artigos:

Como funcionam as assinaturas digitais

As assinaturas digitais são geradas usando uma Chave secreta de assinatura do URL, disponível no console do Google Cloud. Ela é essencialmente uma chave privada, compartilhada somente entre você e o Google, e pertence apenas ao seu projeto.

O processo de assinatura usa um algoritmo de criptografia para combinar o URL e a chave secreta compartilhada. Quando você utiliza a assinatura exclusiva, nossos servidores verificam se os sites que gerarem solicitações usando sua chave de API têm autorização para isso.

Limitar as solicitações sem assinatura

Para garantir que sua chave de API só aceite solicitações assinadas:

  1. Acesse a página Cotas – Plataforma Google Maps no console do Cloud.
  2. Clique no menu suspenso de projetos e escolha aquele que você usou ao criar a chave de API para seu aplicativo ou site.
  3. Selecione a API Maps Static ou a API Street View Static no menu suspenso de APIs.
  4. Abra a seção Solicitações sem assinatura.
  5. Na tabela Nome da cota, clique no botão de edição ao lado da cota que você quer modificar. Por exemplo, Solicitações sem assinatura por dia.
  6. Atualize a opção Limite de cota no painel Editar limite de cota.
  7. Escolha Salvar.

Assinar suas solicitações

Veja a seguir as etapas para assinar suas solicitações:

Etapa 1: gerar a Chave secreta de assinatura do URL

Para receber a Chave secreta de assinatura do URL do projeto:

  1. Acesse a página Credenciais – Plataforma Google Maps no console do Cloud.
  2. Selecione o menu suspenso de projetos e escolha aquele que você usou ao criar a chave para a API Maps Static ou a API Street View Static.
  3. Role para baixo até o card Gerador de chave secreta. O campo Chave secreta atual inclui a Chave secreta de assinatura do URL.
  4. A página também contém o widget Assinar URL agora, que permite assinar automaticamente a solicitação da API Maps Static ou da API Street View Static usando a chave secreta de assinatura atual. Role para baixo até o card Assinar URL agora se quiser acessar o widget.

Para gerar uma nova Chave secreta de assinatura do URL, selecione Gerar chave secreta novamente. A chave anterior expira 24 horas após a criação da outra. Após esse prazo, as solicitações que tiverem a chave antiga vão deixar de funcionar.

Etapa 2: criar solicitação sem assinatura

Os caracteres não listados na tabela abaixo precisam ser codificados para uso em URL:

Resumo de caracteres válidos para URLs
ConjuntoCaracteresUso em URLs
Alfanuméricos a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 Strings de texto, uso do esquema (http), porta (8080) etc.
Não reservados - _ . ~ Strings de texto
Reservados ! * ' ( ) ; : @ & = + $ , / ? % # [ ] Caracteres de controle e/ou strings de texto

O mesmo vale para todos os caracteres no conjunto Reservado, se eles forem transferidos dentro de uma string de texto. Para mais informações, consulte Caracteres especiais.

Gere o URL da solicitação sem assinatura. Para instruções, confira a seguinte documentação para desenvolvedores:

Inclua também a chave de API no parâmetro key. Exemplo:

https://maps.googleapis.com/maps/api/staticmap?center=Z%C3%BCrich&size=400x400&key=YOUR_API_KEY

Gerar a solicitação assinada

Em casos de uso únicos, como solução de problemas ou hospedagem de uma imagem simples da API Maps Static ou da API Street View Static na sua página da Web, você pode gerar uma assinatura digital automaticamente usando o widget Assinar URL agora disponível.

Para solicitações geradas dinamicamente, você precisa da assinatura do lado do servidor, que requer mais algumas etapas intermediárias.

De qualquer forma, haverá um URL de solicitação com um parâmetro signature anexado ao final. Exemplo:

https://maps.googleapis.com/maps/api/staticmap?center=Z%C3%BCrich&size=400x400&key=YOUR_API_KEY
&signature=BASE64_SIGNATURE
Usar o widget "Assinar URL agora"

Para gerar uma assinatura digital com uma chave de API usando o widget Assinar URL agora no console do Google Cloud:

  1. Encontre o widget Assinar URL agora, como descrito na Etapa 1: gerar a Chave secreta de assinatura do URL.
  2. No campo URL, cole o URL da Etapa 2: criar solicitação sem assinatura.
  3. O URL assinado digitalmente vai aparecer no campo Seu URL assinado. Faça uma cópia desse URL.
Gerar assinaturas digitais do lado do servidor

Além das etapas do widget Assinar URL agora, você precisará realizar algumas outras ações ao gerar assinaturas digitais do lado do servidor:

  1. Remova o esquema do protocolo e as partes do host do URL, deixando apenas o caminho e a consulta:

  2. /maps/api/staticmap?center=Z%C3%BCrich&size=400x400&key=YOUR_API_KEY
    
  3. A Chave secreta de assinatura do URL exibida é codificada em um Base64 modificado para URLs.

    Como a maioria das bibliotecas criptográficas exige que a chave esteja no formato de bytes brutos, você provavelmente vai precisar decodificar a Chave secreta de assinatura do URL no formato bruto original antes de assinar.

  4. Assine a solicitação com as partes removidas acima usando HMAC-SHA1.
  5. Como a maioria das bibliotecas criptográficas gera uma assinatura no formato de byte bruto, você precisa converter a assinatura binária resultante usando o método Base64 modificado para que os URLs o convertam em algo que possa ser transmitido dentro do URL.

  6. Anexe a assinatura codificada com Base64 ao URL original da solicitação não assinada no parâmetro signature. Exemplo:

    https://maps.googleapis.com/maps/api/staticmap?center=Z%C3%BCrich&size=400x400&key=YOUR_API_KEY
    &signature=BASE64_SIGNATURE

Se quiser exemplos de como implementar a assinatura de URL usando o código do lado do servidor, consulte Exemplo de código para assinatura de URL abaixo.

Exemplo de código para assinatura de URL

As seções abaixo mostram como implementar a assinatura de URL usando o código do lado do servidor. Os URLs sempre precisam ser assinados no servidor para evitar a exposição da Chave secreta de assinatura do URL aos usuários.

Python

O exemplo abaixo usa bibliotecas Python padrão para assinar um URL. Faça o download do código (link em inglês).

#!/usr/bin/python
# -*- coding: utf-8 -*-
""" Signs a URL using a URL signing secret """

import hashlib
import hmac
import base64
import urllib.parse as urlparse


def sign_url(input_url=None, secret=None):
    """ Sign a request URL with a URL signing secret.
      Usage:
      from urlsigner import sign_url
      signed_url = sign_url(input_url=my_url, secret=SECRET)
      Args:
      input_url - The URL to sign
      secret    - Your URL signing secret
      Returns:
      The signed request URL
  """

    if not input_url or not secret:
        raise Exception("Both input_url and secret are required")

    url = urlparse.urlparse(input_url)

    # We only need to sign the path+query part of the string
    url_to_sign = url.path + "?" + url.query

    # Decode the private key into its binary format
    # We need to decode the URL-encoded private key
    decoded_key = base64.urlsafe_b64decode(secret)

    # Create a signature using the private key and the URL-encoded
    # string using HMAC SHA1. This signature will be binary.
    signature = hmac.new(decoded_key, str.encode(url_to_sign), hashlib.sha1)

    # Encode the binary signature into base64 for use within a URL
    encoded_signature = base64.urlsafe_b64encode(signature.digest())

    original_url = url.scheme + "://" + url.netloc + url.path + "?" + url.query

    # Return signed URL
    return original_url + "&signature=" + encoded_signature.decode()


if __name__ == "__main__":
    input_url = input("URL to Sign: ")
    secret = input("URL signing secret: ")
    print("Signed URL: " + sign_url(input_url, secret))

Java

O exemplo abaixo usa a classe java.util.Base64 disponível desde o JDK 1.8. Talvez as versões mais antigas precisem usar o Apache Commons ou similar. Faça o download do código (link em inglês).

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;  // JDK 1.8 only - older versions may need to use Apache Commons or similar.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class UrlSigner {

  // Note: Generally, you should store your private key someplace safe
  // and read them into your code

  private static String keyString = "YOUR_PRIVATE_KEY";
  
  // The URL shown in these examples is a static URL which should already
  // be URL-encoded. In practice, you will likely have code
  // which assembles your URL from user or web service input
  // and plugs those values into its parameters.
  private static String urlString = "YOUR_URL_TO_SIGN";

  // This variable stores the binary key, which is computed from the string (Base64) key
  private static byte[] key;
  
  public static void main(String[] args) throws IOException,
    InvalidKeyException, NoSuchAlgorithmException, URISyntaxException {
    
    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    
    String inputUrl, inputKey = null;

    // For testing purposes, allow user input for the URL.
    // If no input is entered, use the static URL defined above.    
    System.out.println("Enter the URL (must be URL-encoded) to sign: ");
    inputUrl = input.readLine();
    if (inputUrl.equals("")) {
      inputUrl = urlString;
    }
    
    // Convert the string to a URL so we can parse it
    URL url = new URL(inputUrl);
 
    // For testing purposes, allow user input for the private key.
    // If no input is entered, use the static key defined above.   
    System.out.println("Enter the Private key to sign the URL: ");
    inputKey = input.readLine();
    if (inputKey.equals("")) {
      inputKey = keyString;
    }
    
    UrlSigner signer = new UrlSigner(inputKey);
    String request = signer.signRequest(url.getPath(),url.getQuery());
    
    System.out.println("Signed URL :" + url.getProtocol() + "://" + url.getHost() + request);
  }
  
  public UrlSigner(String keyString) throws IOException {
    // Convert the key from 'web safe' base 64 to binary
    keyString = keyString.replace('-', '+');
    keyString = keyString.replace('_', '/');
    System.out.println("Key: " + keyString);
    // Base64 is JDK 1.8 only - older versions may need to use Apache Commons or similar.
    this.key = Base64.getDecoder().decode(keyString);
  }

  public String signRequest(String path, String query) throws NoSuchAlgorithmException,
    InvalidKeyException, UnsupportedEncodingException, URISyntaxException {
    
    // Retrieve the proper URL components to sign
    String resource = path + '?' + query;
    
    // Get an HMAC-SHA1 signing key from the raw key bytes
    SecretKeySpec sha1Key = new SecretKeySpec(key, "HmacSHA1");

    // Get an HMAC-SHA1 Mac instance and initialize it with the HMAC-SHA1 key
    Mac mac = Mac.getInstance("HmacSHA1");
    mac.init(sha1Key);

    // compute the binary signature for the request
    byte[] sigBytes = mac.doFinal(resource.getBytes());

    // base 64 encode the binary signature
    // Base64 is JDK 1.8 only - older versions may need to use Apache Commons or similar.
    String signature = Base64.getEncoder().encodeToString(sigBytes);
    
    // convert the signature to 'web safe' base 64
    signature = signature.replace('+', '-');
    signature = signature.replace('/', '_');
    
    return resource + "&signature=" + signature;
  }
}

Node JS

O exemplo abaixo usa módulos nativos do Node para assinar um URL. Faça o download do código (link em inglês).

'use strict'

const crypto = require('crypto');
const url = require('url');

/**
 * Convert from 'web safe' base64 to true base64.
 *
 * @param  {string} safeEncodedString The code you want to translate
 *                                    from a web safe form.
 * @return {string}
 */
function removeWebSafe(safeEncodedString) {
  return safeEncodedString.replace(/-/g, '+').replace(/_/g, '/');
}

/**
 * Convert from true base64 to 'web safe' base64
 *
 * @param  {string} encodedString The code you want to translate to a
 *                                web safe form.
 * @return {string}
 */
function makeWebSafe(encodedString) {
  return encodedString.replace(/\+/g, '-').replace(/\//g, '_');
}

/**
 * Takes a base64 code and decodes it.
 *
 * @param  {string} code The encoded data.
 * @return {string}
 */
function decodeBase64Hash(code) {
  // "new Buffer(...)" is deprecated. Use Buffer.from if it exists.
  return Buffer.from ? Buffer.from(code, 'base64') : new Buffer(code, 'base64');
}

/**
 * Takes a key and signs the data with it.
 *
 * @param  {string} key  Your unique secret key.
 * @param  {string} data The url to sign.
 * @return {string}
 */
function encodeBase64Hash(key, data) {
  return crypto.createHmac('sha1', key).update(data).digest('base64');
}

/**
 * Sign a URL using a secret key.
 *
 * @param  {string} path   The url you want to sign.
 * @param  {string} secret Your unique secret key.
 * @return {string}
 */
function sign(path, secret) {
  const uri = url.parse(path);
  const safeSecret = decodeBase64Hash(removeWebSafe(secret));
  const hashedSignature = makeWebSafe(encodeBase64Hash(safeSecret, uri.path));
  return url.format(uri) + '&signature=' + hashedSignature;
}

C#

No exemplo abaixo, a biblioteca padrão System.Security.Cryptography é usada para assinar uma solicitação de URL. É preciso converter a codificação Base64 padrão para implementar uma versão compatível com URL. Faça o download do código (link em inglês).

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace SignUrl {

  public struct GoogleSignedUrl {

    public static string Sign(string url, string keyString) {
      ASCIIEncoding encoding = new ASCIIEncoding();

      // converting key to bytes will throw an exception, need to replace '-' and '_' characters first.
      string usablePrivateKey = keyString.Replace("-", "+").Replace("_", "/");
      byte[] privateKeyBytes = Convert.FromBase64String(usablePrivateKey);

      Uri uri = new Uri(url);
      byte[] encodedPathAndQueryBytes = encoding.GetBytes(uri.LocalPath + uri.Query);

      // compute the hash
      HMACSHA1 algorithm = new HMACSHA1(privateKeyBytes);
      byte[] hash = algorithm.ComputeHash(encodedPathAndQueryBytes);

      // convert the bytes to string and make url-safe by replacing '+' and '/' characters
      string signature = Convert.ToBase64String(hash).Replace("+", "-").Replace("/", "_");
            
      // Add the signature to the existing URI.
      return uri.Scheme+"://"+uri.Host+uri.LocalPath + uri.Query +"&signature=" + signature;
    }
  }

  class Program {

    static void Main() {
    
      // Note: Generally, you should store your private key someplace safe
      // and read them into your code

      const string keyString = "YOUR_PRIVATE_KEY";
  
      // The URL shown in these examples is a static URL which should already
      // be URL-encoded. In practice, you will likely have code
      // which assembles your URL from user or web service input
      // and plugs those values into its parameters.
      const  string urlString = "YOUR_URL_TO_SIGN";
      
      string inputUrl = null;
      string inputKey = null;
    
      Console.WriteLine("Enter the URL (must be URL-encoded) to sign: ");
      inputUrl = Console.ReadLine();
      if (inputUrl.Length == 0) {
        inputUrl = urlString;
      }     
    
      Console.WriteLine("Enter the Private key to sign the URL: ");
      inputKey = Console.ReadLine();
      if (inputKey.Length == 0) {
        inputKey = keyString;
      }
      
      Console.WriteLine(GoogleSignedUrl.Sign(inputUrl,inputKey));
    }
  }
}

Exemplos em outras linguagens

Confira outros exemplos de linguagens no projeto url-signing (link em inglês).

Solução de problemas

Se a solicitação inclui uma assinatura inválida, a API retorna um erro HTTP 403 (Forbidden). Isso provavelmente vai acontecer se a chave secreta de assinatura usada não estiver vinculada à chave de API transmitida ou se a entrada não ASCII não for codificada para URL antes da assinatura.

Para resolver o problema, copie o URL da solicitação, remova o parâmetro de consulta signature e gere novamente uma assinatura válida seguindo as instruções abaixo:

Para gerar uma assinatura digital com uma chave de API usando o widget Assinar URL agora no console do Google Cloud:

  1. Encontre o widget Assinar URL agora, como descrito na Etapa 1: gerar a Chave secreta de assinatura do URL.
  2. No campo URL, cole o URL da Etapa 2: criar solicitação sem assinatura.
  3. O URL assinado digitalmente vai aparecer no campo Seu URL assinado. Faça uma cópia desse URL.