重要事項:Google 地圖平台付費方案不再開放註冊或提供給新客戶。
數位簽章
數位簽章的運作方式
請使用 Google Cloud 控制台提供的「網址簽署密鑰」(亦即加密編譯金鑰),產生數位簽章。這個密鑰基本上是一種私密金鑰,只限您與 Google 共用,且專屬於您的用戶端 ID。
簽署程序會使用加密演算法,將網址與共用密鑰結合。我們的伺服器會根據產生的專屬簽章進行驗證,確認使用您用戶端 ID 產生要求的所有網站都已獲得授權。
簽署要求
簽署要求包含下列步驟:
步驟 1:取得網址簽署密鑰
如何取得專案網址簽署密鑰:
- 前往 Cloud 控制台的「用戶端 ID」頁面。
- 「Key」(金鑰) 欄位包含您目前的用戶端 ID 網址簽署密鑰。
如果您需要重新產生用戶端 ID 網址簽署密鑰,請聯絡支援團隊。
步驟 2:建立未簽署的要求
「未」列於下表中的字元必須經過網址編碼處理:
字元集 | 字元 | 網址使用情況 |
---|---|---|
英數字元 | 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 ) 等。 |
非預留 | - _ . ~ | 文字字串 |
預留 | ! * ' ( ) ; : @ & = + $ , / ? % # [ ] | 控制字元和 (或) 文字字串 |
如果「預留」字元集內的字元是在文字字串內傳遞,一樣適用。詳情請參閱「特殊字元」說明。
建立不含簽章的未簽署要求網址。
請確保也在 client
參數中加入用戶端 ID。例如:
https://maps.googleapis.com/maps/api/staticmap?center=Z%C3%BCrich&size=400x400&client=YOUR_CLIENT_ID
產生已簽署的要求
如果要排解問題,可以使用我們提供的「立即簽署網址」小工具,自動產生數位簽章。
如果是動態產生的要求,會需要進行伺服器端簽署,中間須執行一些額外步驟。
不論是哪一種方式,最後都需要建立結尾附有 signature
參數的要求網址。例如:
https://maps.googleapis.com/maps/api/staticmap?center=Z%C3%BCrich&size=400x400&client=YOUR_CLIENT_ID &signature=BASE64_SIGNATURE
-
去除網址的通訊協定架構和主機部分,僅保留路徑和查詢:
-
畫面上顯示的網址簽署密鑰會以網址適用的修改版 Base64 進行編碼。
大部分的加密編譯程式庫都要求金鑰採用原始位元組格式,因此在簽署前,您可能需要將網址簽署密鑰解碼為最初的原始格式。
- 使用 HMAC-SHA1 簽署上述調整過的要求。
-
大部分的加密編譯程式庫會以原始位元組格式產生簽章,因此請務必使用修改後的網址 Base64,將產生的二進位簽章轉換成可在網址內傳送的內容。
-
將經過 Base64 編碼的簽章附加到
signature
參數中未簽署的原始要求網址。舉例來說:https://maps.googleapis.com/maps/api/staticmap?center=Z%C3%BCrich&size=400x400&client=YOUR_CLIENT_ID &signature=BASE64_SIGNATURE
/maps/api/staticmap?center=Z%C3%BCrich&size=400x400&client=YOUR_CLIENT_ID
如需使用伺服器端程式碼導入網址簽署的做法範例,請參閱下方的「網址簽署程式碼範例」一節。
網址簽署程式碼範例
後續章節會說明使用伺服器端程式碼導入網址簽署的方式。網址一律應在伺服器端簽署,以避免向使用者暴露您的網址簽署密鑰。
Python
以下範例使用標準 Python 程式庫簽署網址 (下載程式碼)。
#!/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 模組簽署網址 (下載程式碼)。
'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
程式庫簽署網址要求。請注意,我們必須轉換預設的 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)); } } }
其他程式語言範例
如需其他程式語言範例,請參閱網址簽署專案。
疑難排解
如果要求包含無效簽章,API 就會傳回 HTTP 403 (Forbidden)
錯誤。使用的簽署密鑰未連結已傳遞的用戶端 ID,或者非 ASCII 輸入內容在簽署「之前」未經網址編碼,最有可能導致這個錯誤。
如要排解這個問題,請複製要求網址,然後去除 signature
查詢參數,再按照下方操作說明重新產生有效的簽章。
如何透過下方的「立即簽署網址」小工具,使用用戶端 ID 產生數位簽章:
- 擷取用戶端 ID 網址簽署密鑰,方法如「步驟 1:取得網址簽署密鑰」所述。
- 在「URL」(網址) 欄位中,貼上「步驟 2:建立未簽署的要求」的未簽署要求網址。
- 在「URL Signing Secret」(網址簽署密鑰) 欄位中,貼上步驟 2 的網址簽署密鑰。
系統會根據未簽署的要求網址和簽署密鑰產生數位簽章,並附加到原始網址。 - 隨後顯示的「Your Signed URL」(已簽署的網址) 欄位就會包含經過數位簽署的網址。