デジタル署名ガイド

API キーを使用してリクエストにデジタル署名する

使用状況によっては、リクエストを認証する際に API キーに加えてデジタル署名が必要になる場合があります。以下の記事をご覧ください。

デジタル署名の仕組み

デジタル署名は、Google Cloud コンソールで取得できる URL 署名シークレットを使用して生成されます。このシークレットは基本的に秘密鍵であり、お客様と Google との間でのみ共有され、プロジェクトごとに固有の内容です。

署名プロセスでは、暗号化アルゴリズムを使用して URL と共有シークレットを結合し、一意の署名が生成されます。この一意の署名により、お客様の API キーを使用してリクエストを生成するサイトは、いずれも許可されたサイトであることがわかります。

未署名のリクエストを制限する

API キーが署名付きリクエストのみを受け入れるようにするには:

  1. Cloud コンソールで Google Maps Platform の [割り当て] ページに移動します。
  2. プロジェクト プルダウンをクリックし、アプリまたはサイトの API キーを作成したときに使用したプロジェクトを選択します。
  3. API プルダウンから [Maps Static API] または [Street View Static API] を選択します。
  4. [未署名のリクエスト] セクションを開きます。
  5. [割り当て名] テーブルで、編集する割り当て(「1 日あたりの未署名のリクエスト数」など)の横にある編集ボタンをクリックします。
  6. [割り当て上限の編集] ペインで [割り当て上限] を更新します。
  7. [保存] を選択します。

リクエストに署名する

リクエストの署名は、次の手順で構成されます。

ステップ 1: URL 署名シークレットを取得する

プロジェクトの URL 署名シークレットを取得するには:

  1. Cloud コンソールの Google Maps Platform の [認証情報] ページに移動します。
  2. プロジェクト プルダウンを選択し、Maps Static API または Street View Static API の API キーを作成したときに使用したプロジェクトを選択します。
  3. [シークレット生成ツール] カードまで下にスクロールします。[現在のシークレット] フィールドには、現在の URL 署名シークレットが含まれています。
  4. このページには、[URL に署名] ウィジェットも提供されています。この機能により、現在の署名シークレットを使用して Maps Static API または Street View Static API のリクエストに自動的に署名できます。[URL に署名] カードにアクセスするには、画面を下にスクロールしてください。

新しい URL 署名シークレットを取得するには、[シークレットを再生成] を選択します。古いシークレットは、新しいシークレットを生成してから 24 時間で期限切れになります。24 時間を過ぎると、古いシークレットを含むリクエストは機能しなくなります。

ステップ 2: 未署名のリクエストを作成する

下記の表にリストされていない文字は、URL エンコードを行う必要があります。

有効な URL 文字の概要
セット文字URL での使用法
英数字 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 テキスト文字列、スキームでの使用(http)、ポート(8080)など
未予約 - _ . ~ テキスト文字列
予約済み ! * ' ( ) ; : @ & = + $ , / ? % # [ ] 制御文字やテキスト文字列

テキスト文字列内で渡される場合、予約済みセットのすべての文字についても同様に当てはまります。詳しくは、特殊文字をご覧ください。

未署名のリクエスト URL を作成します。手順については、以下のデベロッパー向けドキュメントをご覧ください。

key パラメータには、API キーも必ず含めてください。以下に例を示します。

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

署名付きリクエストを生成する

ウェブページ上で Maps Static API または Street View Static API のシンプルな画像をホストする場合やトラブルシューティングを行う場合など、1 回限りの使用の場合は、利用可能な「URL に署名」ウィジェットを使用してデジタル署名を自動的に生成できます。

動的に生成されるリクエストでは、サーバーサイドの署名が必要です。サーバーサイドの署名にはいくつかの追加の中間手順が必要です。

どちらの場合も、末尾に signature パラメータが付加されたリクエスト URL になります。例:

 https://maps.googleapis.com/maps/api/staticmap?center=Z%C3%BCrich&size=400x400&key=YOUR_API_KEY &signature=BASE64_SIGNATURE
「URL に署名」ウィジェットを使用する

API キーでデジタル署名を生成するには、Google Cloud コンソールの [URL に署名] ウィジェットを使用します。

  1. [URL に署名] ウィジェットを見つけます(ステップ 1: URL 署名シークレットを取得するの説明を参照)。
  2. [URL] フィールドに、ステップ 2: 未署名のリクエストを作成するで作成した未署名のリクエスト URL を貼り付けます。
  3. 表示される [署名付き URL] フィールドには、デジタル署名された URL が含まれます。必ずコピーを作成してください。
サーバー側でデジタル署名を生成する

[今すぐ URL に署名] ウィジェットとは異なり、サーバー側でデジタル署名を生成する場合は、追加の操作を行う必要があります。

  1. URL のプロトコル スキームとホスト部分を削除し、パスとクエリのみを残します。

  2.  /maps/api/staticmap?center=Z%C3%BCrich&size=400x400&key=YOUR_API_KEY 
  3. 表示される URL 署名シークレットは、URL 用に変更された Base64 でエンコードされます。

    ほとんどの暗号ライブラリでは、鍵が RAW バイト形式になっているため、署名する前に URL 署名シークレットを元の未加工の形式にデコードする作業が必要になる可能性があります。

  4. HMAC-SHA1 を使用して上記のリクエストに署名します。
  5. ほとんどの暗号ライブラリでは、RAW バイト形式で署名が生成されるため、URL 用に変更された Base64 を使用して生成されたバイナリ署名を、URL 内で渡すことのできるものに変換する必要があります。

  6. Base64 でエンコードされた署名を、signature パラメータの元の未署名リクエスト URL に追加します。以下にサンプルコードを示します。

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

サーバー側のコードを使用して URL 署名を実装する方法については、以下の URL 署名のサンプルコードをご覧ください。

URL 署名のサンプルコード

以下のセクションでは、サーバー側のコードを使用して URL 署名を実装する方法を説明します。URL 署名シークレットをユーザーに知られないようにするため、URL は常にサーバー側で署名してください。

Python

次の例では、標準の Python ライブラリを使用して URL に署名します(サンプルコードをダウンロード)。

#!/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

次の例では、JDK 1.8 以降で利用可能な java.util.Base64 クラスを使用します。これより古いバージョンでは、Apache Commons などを使用する必要があります(サンプルコードをダウンロード)。

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

次の例では、ネイティブの Node モジュールを使用して URL に署名します(サンプルコードをダウンロード)。

'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#

次の例では、デフォルトの System.Security.Cryptography ライブラリを使用して URL リクエストに署名します。URL セーフ バージョンを実装するには、デフォルトの Base64 エンコーディングを変換する必要があります(サンプルコードをダウンロード)。

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));     }   } } 

その他の言語のサンプルコード

その他の言語のサンプルコードは、URL 署名プロジェクトで入手できます。

トラブルシューティング

リクエストに無効な署名が含まれていると、API から HTTP 403 (Forbidden) エラーが返されます。このエラーが発生するのは主に、使用された署名シークレットと受け渡された API キーがリンク済みでない場合や、非 ASCII の入力内容が署名前に URL エンコードされていない場合です。

この問題をトラブルシューティングするには、リクエスト URL をコピーし、signature クエリ パラメータを削除した後、下記の手順に沿って、有効な署名を再生成します。

API キーでデジタル署名を生成するには、Google Cloud コンソールの [URL に署名] ウィジェットを使用します。

  1. [URL に署名] ウィジェットを見つけます(ステップ 1: URL 署名シークレットを取得するの説明を参照)。
  2. [URL] フィールドに、ステップ 2: 未署名のリクエストを作成するで作成した未署名のリクエスト URL を貼り付けます。
  3. 表示される [署名付き URL] フィールドには、デジタル署名された URL が含まれます。必ずコピーを作成してください。