解密價格確認

當您的廣告素材贏得競價時,如果定義廣告素材的 HTML 程式碼片段或 VAST 網址包含 WINNING_PRICE 巨集,Google 將通知您得標的價格。Google 會以加密形式傳回勝出的價格。下列主題會說明應用程式如何解密勝出的價格資訊。

您可以在廣告素材中加入 WINNING_PRICE 巨集,例如,隱藏像素請求會當做廣告的一部分顯示:

<div>
  <script language='JavaScript1.1' src='https://example.com?creativeID=5837243'/>
  <img src='https://example.com/t.gif?price=%%WINNING_PRICE%%' width='1' height='1'/>
</div>

影片廣告素材的 VAST 網址也可包含 WINNING_PRICE 巨集 (但不包含在 VAST 中的曝光網址):

https://example.com/vast/v?price=%%WINNING_PRICE%%

情境

  1. 您的應用程式會在傳送給 Google 的 HTML 程式碼片段或 VAST 網址中加入 WINNING_PRICE 巨集。
  2. Google 使用無填湊的網路安全 base64 編碼方式 (RFC 3548),將巨集替換成得標的價格。
  3. 程式碼片段會以您選擇的格式傳送確認訊息。舉例來說,確認可以傳遞做為廣告內隱藏像素請求的網址。
  4. 在伺服器上,應用程式安全的 base64 會將得標價格資訊解碼,並解密結果。

依附元件

您需要支援 SHA-1 HMAC 的密碼編譯程式庫,例如 Openssl。

程式碼範例

程式碼範例提供 Java 和 C++,可從 privatedatacommunicationprotocol 專案下載。

  • Java 程式碼範例會使用 Apache Commons 專案的 base64 解碼器。您不需要下載 Apache Commons 程式碼,因為參考實作包含必要部分,因此是獨立的。

  • C++ 程式碼範例會使用 OpenSSL base64 BIO 方法。並擷取以網路安全 Base64 編碼的字串 (RFC 3548) 並進行解碼。 一般而言,網路安全的 Base64 字串會將「=」邊框間距替換成「.」(請注意,系統會加入引號以方便閱讀,且不會包含在通訊協定中),但替代巨集不會填入加密的價格。這項參考實作會加入邊框間距,因為 OpenSSL 無法處理未填充的字串。

編碼

得標價格加密和解密需要兩組密鑰,但共用金鑰。完整性金鑰和加密金鑰分別稱為 i_keye_key。在帳戶設定時,這兩個金鑰都是以網路安全 Base64 字串的形式提供,並可在 Authorized Buyers 頁面的「出價工具設定」>「RTB 設定」>「加密金鑰」下方找到。

完整性和加密金鑰範例:

skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=  // Encryption key (e_key)
arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=  // Integrity key (i_key)

金鑰應以網路安全解碼,然後由應用程式解碼:Base64:

e_key = WebSafeBase64Decode('skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=')
i_key = WebSafeBase64Decode('arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=')

加密配置

價格是以自訂加密配置加密,這種配置旨在將大小負擔降到最低,同時確保安全性。這項加密配置採用金鑰化 HMAC 演算法,根據不重複的曝光事件 ID 產生密鑰填充。

加密價格的固定長度為 28 個位元組。當中包含 16 位元組初始化向量、8 位元組的密文,以及 4 位元組的完整性簽章。根據 RFC 3548 的規定,加密的價格採用網路安全 Base64 編碼,但會省略填補字元。因此,無論支付的價格為何,28 位元組的加密價格都會編碼為 38 個字元的網路安全 Base-64 字串。

加密價格範例:

YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCce_6msaw  // 100 CPI micros
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCAWJRxOgA  // 1900 CPI micros
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw  // 2700 CPI micros

加密格式為:

{initialization_vector (16 bytes)}{encrypted_price (8 bytes)}
{integrity (4 bytes)}

價格會加密為 <price xor HMAC(encryption_key, initialization_vector)>,因此解密會以加密價格計算 HMAC(encryption_key,initialization_vector) 和 xor,以反轉加密。完整性階段會取用 <HMAC(integrity_key, price||initialization_vector)> 的 4 個位元組,其中 || 為串連。

輸入
iv 初始化向量 (16 位元組,為曝光專屬)
e_key 加密金鑰 (32 位元組,在設定帳戶時提供)
i_key 完整性金鑰 (32 位元組,在設定帳戶時提供)
price (8 位元組 - 以帳戶貨幣的百萬分之一表示)
Notation
hmac(k, d) 資料 d 的 SHA-1 HMAC,使用金鑰 k
a || b 字串「a」與字串 b 串連
虛擬程式碼
pad = hmac(e_key, iv)  // first 8 bytes
enc_price = pad <xor> price
signature = hmac(i_key, price || iv)  // first 4 bytes

final_message = WebSafeBase64Encode( iv || enc_price || signature )

解密機制

您的解密程式碼必須使用加密金鑰將價格解密,並使用完整性金鑰驗證完整性位元。系統會在設定期間提供金鑰。實作的建構方式沒有任何限制。在大部分的情況下,您應能擷取程式碼範例,然後視需要調整。

輸入
e_key 加密金鑰 (32 位元組,在建立帳戶時即提供)
i_key 完整性金鑰 (32 位元組,在建立帳戶時即提供給使用者)
final_message 使用網路安全 Base64 編碼的 38 個字元
虛擬程式碼
// Base64 padding characters are omitted.
// Add any required base64 padding (= or ==).
final_message_valid_base64 = AddBase64Padding(final_message)

// Web-safe decode, then base64 decode.
enc_price = WebSafeBase64Decode(final_message_valid_base64)

// Message is decoded but remains encrypted.
(iv, p, sig) = enc_price // Split up according to fixed lengths.
price_pad = hmac(e_key, iv)
price = p <xor> price_pad

conf_sig = hmac(i_key, price || iv)
success = (conf_sig == sig)

偵測過時回應攻擊

如要偵測過時的回應或重播,建議使用時間戳記篩選回應,與系統時間有明顯差異,再考量時區差異後。

初始化向量的前 8 個位元組包含時間戳記。可透過下列 C++ 函式讀取:

void GetTime(const char* iv, struct timeval* tv) {
    uint32 val;
    memcpy(&val, iv, sizeof(val));
    tv->tv_sec = htonl(val);
    memcpy(&val, iv+sizeof(val), sizeof(val));
    tv->tv_usec = htonl(val)
}

您可以使用下列 C++ 程式碼,將時間戳記轉換為使用者可理解的格式:

struct tm tm;
localtime_r(&tv->tv_sec, &tm);

printf("%04d-%02d-%02d|%02d:%02d:%02d.%06ld",
       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
       tm.tm_hour, tm.tm_min, tm.tm_sec,
       tv_.tv_usec);

Java 程式庫

您可以使用 DoubleClickCrypto.java 來編碼並解碼勝出的價格,不必實作加密演算法。詳情請參閱「密碼編譯」。