Java 샘플 코드는 Apache 공통 프로젝트의 base64 디코더를 사용합니다. 참조 구현에 필요한 부분이 포함되어 있으므로 Apache commons 코드를 다운로드할 필요가 없습니다.
C++ 샘플 코드는 OpenSSL base64 BIO 메서드를 사용합니다. 웹 보안 base64로 인코딩된 문자열 (RFC 3548)을 사용하여 디코딩합니다.
일반적으로 웹에 안전한 base64 문자열은 '=" 패딩을 '.'로 대체합니다. 따옴표는 읽기 편의를 위해 추가되며 프로토콜에는 포함되지 않습니다. 하지만 매크로 대체는 암호화된 가격을 패딩하지 않습니다. OpenSSL에는 패딩되지 않은 문자열에 문제가 있으므로 참조 구현은 패딩을 추가합니다.
인코딩
낙찰가 암호화 및 복호화에는 비공개이지만 공유된 키 2개가 필요합니다. 무결성 키와 암호화 키(각각 i_key 및 e_key라고 함) 두 키는 계정 설정 시 웹에 안전한 base64 문자열로 제공되며 Authorized Buyers 페이지의 입찰자 설정 > RTB 설정 > 암호화 키에서 확인할 수 있습니다.
가격은 크기 오버헤드를 최소화하면서 적절한 보안을 보장하도록 설계된 맞춤 암호화 스키마를 사용하여 암호화됩니다. 암호화 스키마는 키가 있는 HMAC 알고리즘을 사용하여 고유한 노출 이벤트 ID를 기반으로 보안 패드를 생성합니다.
암호화된 가격의 고정 길이는 28바이트입니다. 16바이트 초기화 벡터, 8바이트 암호문, 4바이트 무결성 서명으로 구성됩니다. 암호화된 가격은 RFC 3548에 따라 웹 보안 base64로 인코딩되며 패딩 문자는 생략됩니다. 따라서 28바이트 암호화된 가격은 지불된 낙찰가와 관계없이 38자 웹 안전 base-64 문자열로 인코딩됩니다.
가격은 <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)
키 k를 사용하여 데이터 d의 SHA-1 HMAC
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_padconf_sig=hmac(i_key,price||iv)success=(conf_sig==sig)
비활성 응답 공격 감지
오래된 응답 또는 재생 공격을 감지하려면 시간대 차이를 고려한 후 시스템 시간과 크게 다른 타임스탬프가 있는 응답을 필터링하는 것이 좋습니다.
초기화 벡터의 처음 8바이트에는 타임스탬프가 포함됩니다. 다음 C++ 함수에서 읽을 수 있습니다.
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-09-04(UTC)"],[[["\u003cp\u003eGoogle can reveal the winning price of an auction if the creative includes either the \u003ccode\u003e${AUCTION_PRICE}\u003c/code\u003e macro for OpenRTB or \u003ccode\u003e%%WINNING_PRICE%%\u003c/code\u003e for the Google RTB protocol.\u003c/p\u003e\n"],["\u003cp\u003eThe winning price, when expanded from the macro, is returned in an encrypted format and can be embedded in elements such as an invisible pixel request or a VAST URL, but not the impression URL.\u003c/p\u003e\n"],["\u003cp\u003eDecrypting the winning price requires both an integrity key (\u003ccode\u003ei_key\u003c/code\u003e) and an encryption key (\u003ccode\u003ee_key\u003c/code\u003e), provided during account setup, and a crypto library that supports SHA-1 HMAC.\u003c/p\u003e\n"],["\u003cp\u003eThe encrypted price consists of a 16-byte initialization vector, 8 bytes of ciphertext, and a 4-byte integrity signature, totaling 28 bytes, which are then encoded into a 38-character web-safe base64 string.\u003c/p\u003e\n"],["\u003cp\u003eTo guard against stale response attacks, it's recommended to filter responses based on the timestamp found in the initialization vector, comparing it against the current system time.\u003c/p\u003e\n"]]],["When a creative wins an auction, Google reveals the winning price via macros. OpenRTB uses `${AUCTION_PRICE}`, while the deprecated Google RTB uses `%%WINNING_PRICE%%`. These macros return an encrypted winning price that can be inserted into a creative's pixel request or a video's VAST URL. The encrypted price, a 38-character web-safe base64 string, is decrypted using provided encryption and integrity keys, alongside SHA-1 HMAC. The encryption includes an initialization vector, ciphertext, and integrity signature. Sample code in Java and C++ are available.\n"],null,["When your creative wins an auction, Google can inform you what the winning\nprice was if the creative includes the `${AUCTION_PRICE}` macro.\n\nWhen the macro is expanded, it returns the winning price in an encrypted\nform. It can be included in a creative, for example, with an invisible pixel\nrequest rendered as part of the ad: \n\n```genshi\n\u003cdiv\u003e\n \u003cscript language='JavaScript1.1' src='https://example.com?creativeID=5837243'/\u003e\n \u003cimg src='https://example.com/t.gif?price=${AUCTION_PRICE}' width='1' height='1'/\u003e\n\u003c/div\u003e\n```\n\nThe `${AUCTION_PRICE}` macro can also be included in the VAST URL of\na video creative, but not in the impression URL in the VAST: \n\n```genshi\nhttps://example.com/vast/v?price=${AUCTION_PRICE}\n```\n\nScenario\n\n1. Your OpenRTB bidding application includes the `${AUCTION_PRICE}` macro in the HTML snippet or VAST URL it returns to Google.\n2. Google substitutes the winning price for the macro in unpadded web-safe base64 encoding ([RFC 3548](//tools.ietf.org/html/rfc3548)).\n3. The snippet passes the confirmation in the format you have chosen. For example, the confirmation might be passed in the URL of an invisible pixel request rendered as part of the ad.\n4. On the server, your application web-safe base64 decodes the winning price information and decrypts the result.\n\nDependencies\n\nYou will need a crypto library that supports SHA-1 HMAC, such as\nOpenssl.\n\nSample code\n\nSample code is provided in Java and C++ and can be downloaded from the [privatedatacommunicationprotocol\nproject](//code.google.com/p/privatedatacommunicationprotocol).\n\n- The Java sample code uses the base64 decoder from the [Apache\n commons project](//commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html). You will not need to download the Apache commons code,\n as the reference implementation includes the necessary part and is therefore\n self-contained.\n\n- The C++ sample code uses the [OpenSSL\n base64 BIO method](//www.openssl.org/docs/man1.1.0/crypto/BIO_f_base64.html). It takes a web-safe base64 encoded string ([RFC 3548](//tools.ietf.org/html/rfc3548)) and decodes it.\n Normally, web-safe base64 strings replace \"=\" padding with \".\" (note that\n quotation marks are added for reading clarity and are not included in the\n protocol) but the macro substitution does not pad the encrypted price. The\n reference implementation adds padding because OpenSSL has trouble with\n unpadded strings.\n\nEncoding\n\nWinning price encryption and decryption requires two secret, but shared,\nkeys. An integrity key, and encryption key, referred to as `i_key`,\nand `e_key` respectively. Both keys are provided at account setup as\nweb-safe base64 strings, and can be found on the Authorized Buyers page\nunder [**Bidder\nsettings \\\u003e RTB settings \\\u003e Encryption keys**](//support.google.com/authorizedbuyers/answer/10858928).\n\nExample integrity and encryption keys: \n\n```scdoc\nskU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o= // Encryption key (e_key)\narO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo= // Integrity key (i_key)\n```\n\nKeys should be web-safe decoded and then base64 decoded by your\napplication: \n\n```scdoc\ne_key = WebSafeBase64Decode('skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=')\ni_key = WebSafeBase64Decode('arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=')\n```\n\nEncryption scheme\n\nThe price is encrypted using a custom encryption scheme that is designed to\nminimize size overhead while ensuring adequate security. The encryption scheme\nuses a keyed HMAC algorithm to generate a secret pad based on the unique\nimpression event ID.\n\nThe encrypted price has a fixed length of 28 bytes. It is comprised of a\n16-byte initialization vector, 8 bytes of ciphertext, and a 4-byte integrity\nsignature. The encrypted price is web-safe base64-encoded, according to RFC\n3548, with padding characters omitted. Thus, the 28-byte encrypted price is\nencoded as a 38 character web-safe base-64 string irrespective of the winning\nprice paid.\n\nExample encrypted prices: \n\n```scdoc\nYWJjMTIzZGVmNDU2Z2hpN7fhCuPemCce_6msaw // 100 CPI micros\nYWJjMTIzZGVmNDU2Z2hpN7fhCuPemCAWJRxOgA // 1900 CPI micros\nYWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw // 2700 CPI micros\n```\n\nThe encrypted format is: \n\n```scdoc\n{initialization_vector (16 bytes)}{encrypted_price (8 bytes)}\n{integrity (4 bytes)}\n```\n\nThe price is encrypted as `\u003cprice xor HMAC(encryption_key,\ninitialization_vector)\u003e` so decryption calculates\n`HMAC(encryption_key,initialization_vector)` and xor's with the\nencrypted price to reverse the encryption. The integrity stage takes 4 bytes of\n`\u003cHMAC(integrity_key, price||initialization_vector)\u003e` where\n`||` is concatenation.\n\n| Inputs ||\n|--------------|-------------------------------------------------------------|\n| `iv` | initialization vector (16 bytes - unique to the impression) |\n| `e_key` | encryption key (32 bytes - provided at account set up) |\n| `i_key` | integrity key (32 bytes - provided at account set up) |\n| `price` | (8 bytes - in micros of account currency) |\n| `hmac(k, d)` | SHA-1 HMAC of data `d`, using key `k` |\n| `a || b` | string `a` concatenated with string `b` |\n| ```scdoc pad = hmac(e_key, iv) // first 8 bytes enc_price = pad \u003cxor\u003e price signature = hmac(i_key, price || iv) // first 4 bytes final_message = WebSafeBase64Encode( iv || enc_price || signature ) ``` ||\n\nDecryption scheme\n\nYour decryption code must decrypt the price using the encryption key, and\nverify the integrity bits with the integrity key. The keys will be provided to\nyou during setup. There aren't any restrictions on the details of how you\nstructure your implementation. For the most part, you should be able to take\nthe sample code and adapt it according to your needs.\n\n| Inputs ||\n|-----------------|-------------------------------------------------------|\n| `e_key` | encryption key, 32 bytes - provided at account set up |\n| `i_key` | integrity key, 32 bytes - provided at account set up |\n| `final_message` | 38 characters web-safe base64 encoded |\n| ```scilab // 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 \u003cxor\u003e price_pad conf_sig = hmac(i_key, price || iv) success = (conf_sig == sig) ``` ||\n\nDetect stale response attacks\n\nTo detect stale response, or replay, attacks, it's recommended that you\nfilter responses with a timestamp that differs significantly from the system\ntime, after accounting for timezone differences.\n\nThe initialization vector contains a timestamp in the first 8 bytes. It can\nbe read by the following C++ function: \n\n```gdscript\nvoid GetTime(const char* iv, struct timeval* tv) {\n uint32 val;\n memcpy(&val, iv, sizeof(val));\n tv-\u003etv_sec = htonl(val);\n memcpy(&val, iv+sizeof(val), sizeof(val));\n tv-\u003etv_usec = htonl(val)\n}\n```\n\nThe timestamp can be converted to a human readable form using the following\nC++ code: \n\n```css+lasso\nstruct tm tm;\nlocaltime_r(&tv-\u003etv_sec, &tm);\n\nprintf(\"%04d-%02d-%02d|%02d:%02d:%02d.%06ld\",\n tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,\n tm.tm_hour, tm.tm_min, tm.tm_sec,\n tv_.tv_usec);\n```\n\nJava library\n\nInstead of implementing the crypto algorithms to encode and decode\nthe winning price, you can use\n[DoubleClickCrypto.java](//github.com/google/openrtb-doubleclick/blob/master/doubleclick-core/src/main/java/com/google/doubleclick/crypto/DoubleClickCrypto.java). For more information, see\n[Cryptography](//github.com/google/openrtb-doubleclick/wiki#cryptography)."]]