Рекламные сети, использующие теги JavaScript для заполнения объявлений через Авторизованных покупателей, имеют право получать идентификаторы рекламодателя как для устройств Android, так и для iOS. Информация отправляется через макрос %%EXTRA_TAG_DATA%% или %%ADVERTISING_IDENTIFIER%% в теге JavaScript, управляемом Авторизованными покупателями. Остальная часть этого раздела посвящена извлечению %%EXTRA_TAG_DATA%% , но подробности о зашифрованном прото-буфере %%ADVERTISING_IDENTIFIER%% MobileAdvertisingId , который можно расшифровать аналогичным образом, см. в разделе Ремаркетинг с IDFA или рекламным идентификатором.
Хронология
-  Рекламная сеть обновляет теги JavaScript в приложениях через пользовательский интерфейс Авторизованных покупателей , добавляя макрос %%EXTRA_TAG_DATA%%, как описано ниже.
- Во время показа приложение запрашивает рекламу у Авторизованных покупателей через Google Mobile Ads SDK , безопасно передавая идентификатор рекламодателя.
-  Приложение получает обратно тег JavaScript с макросом %%EXTRA_TAG_DATA%%, заполненным зашифрованным буфером протокола рекламной сети, содержащим этот идентификатор.
- Приложение запускает этот тег, вызывая рекламную сеть для поиска победившего объявления.
-  Чтобы использовать (монетизировать) эту информацию, рекламная сеть должна обработать буфер протокола:- Декодируйте строку веб-безопасности обратно в байтовую строку с помощью WebSafeBase64.
- Расшифруйте его, используя схему, изложенную ниже.
- Десериализуйте прототип и получите идентификатор рекламодателя из ExtraTagData.advertising_id или ExtraTagData.hashed_idfa.
 
Зависимости
- Кодировщик WebSafeBase64 .
- Криптобиблиотека, поддерживающая SHA-1 HMAC, например Openssl .
- Компилятор буфера протокола Google.
Декодировать строку веб-безопасности
 Поскольку информация, отправляемая через макрос %%EXTRA_TAG_DATA%% должна отправляться через URL-адрес, серверы Google кодируют ее с помощью веб-безопасного base64 ( RFC 3548 ).
Поэтому, прежде чем пытаться расшифровать, вы должны декодировать символы ASCII обратно в байтовую строку. Приведенный ниже пример кода C++ основан на BIO_f_base64() проекта OpenSSL и является частью примера кода расшифровки Google .
string AddPadding(const string& b64_string) { if (b64_string.size() % 4 == 3) { return b64_string + "="; } else if (b64_string.size() % 4 == 2) { return b64_string + "=="; } return b64_string; } // Adapted from http://www.openssl.org/docs/man1.1.0/crypto/BIO_f_base64.html // Takes a web safe base64 encoded string (RFC 3548) and decodes it. // Normally, web safe base64 strings have padding '=' replaced with '.', // but we will not pad the ciphertext. We add padding here because // openssl has trouble with unpadded strings. string B64Decode(const string& encoded) { string padded = AddPadding(encoded); // convert from web safe -> normal base64. int32 index = -1; while ((index = padded.find_first_of('-', index + 1)) != string::npos) { padded[index] = '+'; } index = -1; while ((index = padded.find_first_of('_', index + 1)) != string::npos) { padded[index] = '/'; } // base64 decode using openssl library. const int32 kOutputBufferSize = 256; char output[kOutputBufferSize]; BIO* b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO* bio = BIO_new_mem_buf(const_cast<char*>(padded.data()), padded.length()); bio = BIO_push(b64, bio); int32 out_length = BIO_read(bio, output, kOutputBufferSize); BIO_free_all(bio); return string(output, out_length); }
Структура зашифрованной байтовой строки
После того как вы декодировали символы ASCII обратно в байтовую строку, вы готовы ее расшифровать. Зашифрованная байтовая строка содержит 3 раздела:
-  initialization_vector: 16 байт.
-  ciphertext: серия 20-байтовых секций.
-  integrity_signature: 4 байта.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)} Массив байтов ciphertext разделен на несколько секций по 20 байт, за исключением того, что самая последняя секция может содержать от 1 до 20 байтов включительно. Для каждого раздела исходного byte_array соответствующий 20-байтовый ciphertext генерируется как:
<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>
 где || является конкатенация.
Определения
| Переменная | Подробности | 
|---|---|
| initialization_vector | 16 байт — уникально для впечатления. | 
| encryption_key | 32 байта — предоставляются при настройке учетной записи. | 
| integrity_key | 32 байта — предоставляются при настройке учетной записи. | 
| byte_array | Сериализованный объект ExtraTagDataв секциях по 20 байт. | 
| counter_bytes | Значение байта, показывающее порядковый номер раздела, см. ниже. | 
| final_message | Общий массив байтов, отправленный через макрос %%EXTRA_TAG_DATA%%(без кодировки WebSafeBase64). | 
| Операторы | Подробности | 
|---|---|
| hmac(key, data) | SHA-1 HMAC, использование keyдля шифрованияdata. | 
| a || b | строка aобъединена со строкойb. | 
Вычислить счетчик_байтов
 counter_bytes отмечает порядок каждой 20-байтовой секции ciphertext . Обратите внимание, что последняя секция может содержать от 1 до 20 байт включительно. Чтобы заполнить counter_bytes правильным значением при запуске функции hmac() , подсчитайте 20-байтовые секции (включая остаток) и используйте следующую справочную таблицу:
| Номер раздела | значение counter_bytes | 
|---|---|
| 0 | Никто | 
| 1 … 256 | 1 байт. Значение последовательно увеличивается от 0 до 255. | 
| 257…512 | 2 байта. Значение первого байта равно 0, значение второго байта последовательно увеличивается от 0 до 255. | 
| 513…768 | 3 байта. Значение первых двух байтов равно 0, значение последнего байта последовательно увеличивается от 0 до 255. | 
Схема шифрования
Схема шифрования основана на той же схеме, которая используется для расшифровки сигнала гиперлокального нацеливания .
- Сериализация : экземпляр объекта ExtraTagData, определенный в буфере протокола, сначала сериализуется с помощью - SerializeAsString()в массив байтов.
- Шифрование . Затем массив байтов шифруется с использованием специальной схемы шифрования, предназначенной для минимизации накладных расходов по размеру и обеспечения адекватной безопасности. Схема шифрования использует алгоритм HMAC с ключами для создания секретной панели на основе - initialization_vector, уникального для события показа.
Псевдокод шифрования
byte_array = SerializeAsString(ExtraTagData object)
pad = hmac(encryption_key, initialization_vector ||
      counter_bytes )  // for each 20-byte section of byte_array
ciphertext = pad <xor> byte_array // for each 20-byte section of byte_array
integrity_signature = hmac(integrity_key, byte_array ||
                      initialization_vector)  // first 4 bytes
final_message = initialization_vector || ciphertext || integrity_signature
Схема расшифровки
Ваш код дешифрования должен: 1) расшифровать буфер протокола с помощью ключа шифрования и 2) проверить биты целостности с помощью ключа целостности. Ключи будут предоставлены вам во время настройки учетной записи. Нет никаких ограничений на то, как вы структурируете свою реализацию. По большей части вы сможете взять пример кода и адаптировать его в соответствии со своими потребностями.
-  Создайте свой блокнот : HMAC(encryption_key, initialization_vector || counter_bytes)
-  XOR : возьмите этот результат и <xor>с зашифрованным текстом, чтобы отменить шифрование.
-  Проверьте : подпись целостности передает 4 байта HMAC(integrity_key, byte_array || initialization_vector)
Псевдокод расшифровки
// split up according to length rules
(initialization_vector, ciphertext, integrity_signature) = final_message
// for each 20-byte section of ciphertext
pad = hmac(encryption_key, initialization_vector || counter_bytes)
// for each 20-byte section of ciphertext
byte_array = ciphertext <xor> pad
confirmation_signature = hmac(integrity_key, byte_array ||
                         initialization_vector)
success = (confirmation_signature == integrity_signature)
Пример кода C++
Сюда включена ключевая функция из нашего полного примера кода расшифровки .
bool DecryptByteArray(
    const string& ciphertext, const string& encryption_key,
    const string& integrity_key, string* cleartext) {
  // Step 1. find the length of initialization vector and clear text.
  const int cleartext_length =
     ciphertext.size() - kInitializationVectorSize - kSignatureSize;
  if (cleartext_length < 0) {
    // The length cannot be correct.
    return false;
  }
  string iv(ciphertext, 0, kInitializationVectorSize);
  // Step 2. recover clear text
  cleartext->resize(cleartext_length, '\0');
  const char* ciphertext_begin = string_as_array(ciphertext) + iv.size();
  const char* const ciphertext_end = ciphertext_begin + cleartext->size();
  string::iterator cleartext_begin = cleartext->begin();
  bool add_iv_counter_byte = true;
  while (ciphertext_begin < ciphertext_end) {
    uint32 pad_size = kHashOutputSize;
    uchar encryption_pad[kHashOutputSize];
    if (!HMAC(EVP_sha1(), string_as_array(encryption_key),
              encryption_key.length(), (uchar*)string_as_array(iv),
              iv.size(), encryption_pad, &pad_size)) {
      printf("Error: encryption HMAC failed.\n");
      return false;
    }
    for (int i = 0;
         i < kBlockSize && ciphertext_begin < ciphertext_end;
         ++i, ++cleartext_begin, ++ciphertext_begin) {
      *cleartext_begin = *ciphertext_begin ^ encryption_pad[i];
    }
    if (!add_iv_counter_byte) {
      char& last_byte = *iv.rbegin();
      ++last_byte;
      if (last_byte == '\0') {
        add_iv_counter_byte = true;
      }
    }
    if (add_iv_counter_byte) {
      add_iv_counter_byte = false;
      iv.push_back('\0');
    }
  }
Получить данные из буфера протокола рекламной сети.
 После декодирования и расшифровки данных, переданных в %%EXTRA_TAG_DATA%% , вы готовы десериализовать буфер протокола и получить идентификатор рекламодателя для таргетинга.
Если вы не знакомы с буферами протоколов, начните с нашей документации .
Определение
Наш буфер протокола рекламной сети определяется следующим образом:
message ExtraTagData {
  // advertising_id can be Apple's identifier for advertising (IDFA)
  // or Android's advertising identifier. When the advertising_id is an IDFA,
  // it is the plaintext returned by iOS's [ASIdentifierManager
  // advertisingIdentifier]. For hashed_idfa, the plaintext is the MD5 hash of
  // the IDFA.  Only one of the two fields will be available, depending on the
  // version of the SDK making the request.  Later SDKs provide unhashed values.
  optional bytes advertising_id = 1;
  optional bytes hashed_idfa = 2;
} Вам нужно будет десериализовать его с помощью ParseFromString() , как описано в документации по буферу протокола C++ .
 Подробные сведения о полях advertising_id и iOS hashed_idfa см. в разделах «Расшифровка рекламного идентификатора» и «Таргетинг ресурсов мобильных приложений с помощью IDFA» .
Java-библиотека
Вместо реализации криптоалгоритмов для кодирования и декодирования идентификаторов рекламодателей для рекламных сетей вы можете использовать DoubleClickCrypto.java . Дополнительные сведения см. в разделе Криптография .