Google的激勵廣告的 Google 回調的服務器端驗證 ( SSV ) server side verifiy。Python版本的基於第三方包 ecdsa 開箱即用。python
https://www.gstatic.com/admob/reward/verifier-keys.jsonshell
注意:json
- AdMob 密鑰服務器提供的公鑰會不按期輪換。爲確保能夠繼續按預期驗證 SSV 回調,請勿使公鑰的緩存時間超過 24 小時。
- Google 預計您的服務器會針對 SSV 回調返回 HTTP 200 OK 成功狀態響應代碼。若是您的服務器沒法訪問或未提供預期的響應,Google 將從新嘗試發送 SSV 回調,每隔 1 秒發送最多 5 次。
- 用回調參數中key_id 取對應公鑰,進行簽名驗證。
$ pip install ecdsa
# codin=utf8 """ google admob server side verify pip install ecdsa success """ import sys import json import urllib.parse import urllib.request import base64 import hashlib from ecdsa.keys import VerifyingKey, BadSignatureError from ecdsa.util import sigdecode_der # AdMob密鑰服務器 VERIFIER_KEYS_URL = 'https://www.gstatic.com/admob/reward/verifier-keys.json' # request 的模擬瀏覽器信息 USER_AGENT = ''' Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36 ''' def extract_verifier_keys(): """ 從google的AdMob密鑰服務器提取用於驗證激勵視頻廣告SSV回調的公鑰列表。 公鑰列表以 JSON 格式 """ keys_dict = dict() response = urllib.request.urlopen(VERIFIER_KEYS_URL) if response.status != 200: return keys_dict json_keys = response.read().decode('utf-8') if not json_keys: return keys_dict data_keys = json.loads(json_keys) if not data_keys or 'keys' not in data_keys or not isinstance(data_keys, dict): return keys_dict for key_ids in data_keys['keys']: keys_dict[str(key_ids['keyId'])] = dict( pem=key_ids['pem'], base64=key_ids['base64'] ) return keys_dict def parse_query_string(query_string: str): """ 解析query_string,取出signature, key_id,message :param query_string: :return: """ query_dict = dict( signature='', key_id='', message='' ) if not query_string or query_string.find('&') == -1: return query_dict query_string_dict = dict([x.split('=', 1) for x in query_string.split('&')]) query_dict['signature'] = query_string_dict.get('signature').strip() or '' query_dict['message'] = query_string[:query_string.index('signature') - 1].strip() or '' query_dict['key_id'] = query_string_dict.get('key_id', '').strip() or '' return query_dict def signature_verifier(ver_message: str, ver_signature: str, public_key_pem: str): """ 校驗簽名 :param ver_message: 簽名字符串 :param ver_signature: base64編碼的簽名 :param public_key_pem: 公鑰 :return: boolean """ if not all([ver_message, ver_signature, public_key_pem]): return False public_key = VerifyingKey.from_pem(public_key_pem) # 注意這裏的 替換 和補位 ver_signature = base64.urlsafe_b64decode(str(ver_signature) + '===') ver_message = bytes(ver_message, encoding="utf8") try: return public_key.verify( ver_signature, ver_message, hashfunc=hashlib.sha256, sigdecode=sigdecode_der, ) except BadSignatureError as e: return False if __name__ == '__main__': # Google 回調參數,get傳參 query_string = """123456""" # 解析query_string query_dict = parse_query_string(query_string) signature = query_dict.get('signature').strip() # 簽名結果字符串 message = query_dict.get('message').strip() # 元數據 key_id = query_dict.get('key_id').strip() # 簽名使用的公鑰id # 獲取Google AdMob的公鑰 verifier_keys = extract_verifier_keys() if not verifier_keys or key_id not in verifier_keys: sys.exit('Fetch google admob keys failed!') # 根據google回調的key_id 取對應的公鑰 pem_keys = verifier_keys.get(str(key_id), None) if not pem_keys: sys.exit('Can not found public key by key_id!') # 執行校驗 ret_verifier = signature_verifier(message, signature, pem_keys['pem']) print(ret_verifier)
### 執行python腳本:瀏覽器
$ python depakin.py True
True 校驗成功。緩存