Desencriptación de los identificadores de anunciantes para las redes de publicidad

Redes de publicidad que utilizan Son aptas las etiquetas de JavaScript para rellenar anuncios mediante Authorized Buyers Recibir identificadores de anunciante para dispositivos iOS y Android La información se envía a través de %%EXTRA_TAG_DATA%%. Macro %%ADVERTISING_IDENTIFIER%% en la etiqueta de JavaScript administrada por Authorized Buyers. El resto de esta sección se centra en la extracción %%EXTRA_TAG_DATA%% pero ver Remarketing con IDFA o ID de publicidad para obtener más detalles en el búfer de proto encriptado %%ADVERTISING_IDENTIFIER%% MobileAdvertisingId que se puede desencriptar de forma análoga.

Cronograma

  1. La red de publicidad actualiza sus etiquetas de JavaScript en la aplicación a través de la IU de Authorized Buyers agregando la macro %%EXTRA_TAG_DATA%% como se explica a continuación.
  2. En el momento de la publicación, la app solicita un anuncio a Authorized Buyers a través de la SDK de anuncios de Google para dispositivos móviles, mientras que y pasar de forma segura el identificador del anunciante.
  3. La app vuelve a recibir la etiqueta de JavaScript con el %%EXTRA_TAG_DATA%%. completa completa con el búfer de protocolo de red de publicidad encriptado que contiene ese identificador.
  4. La app ejecuta esta etiqueta y llama a la red de publicidad para que el usuario gane. anuncio.
  5. Para usar (monetizar) esta información, la red de publicidad debe procesar el búfer de protocolo:
    1. Decodifica la cadena websafe en una cadena de bytes con WebSafeBase64.
    2. Para desencriptarla, usa el esquema que se indica a continuación.
    3. Deserializa el proto y obtén el ID del anunciante de ExtraTagData.advertising_id o ExtraTagData.hashed_idfa.

Dependencias

  1. El WebSafeBase64 codificador-decodificador.
  2. Una biblioteca criptográfica compatible con HMAC de SHA-1, como Openssl.
  3. El protocolo de Google compilador de búfer.

Decodifica strings de seguridad para la Web

Debido a que la información enviada a través de la macro %%EXTRA_TAG_DATA%% debe enviarse a través de una URL, los servidores de Google lo codifican con base64 segura para la Web (RFC 3548).

Antes de intentar desencriptación, debes volver a decodificar los caracteres ASCII en un de bytes. El siguiente código C++ de muestra se basa en la biblioteca OpenSSL BIO_f_base64() del proyecto y es parte de la muestra de Google de encriptación.

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);
}

Estructura de una cadena de bytes encriptada

Una vez que hayas codificado los caracteres ASCII en una cadena de bytes, estarás listo para desencriptarlo. La cadena de bytes encriptada contiene 3 secciones:

  • initialization_vector: 16 bytes
  • ciphertext: serie de secciones de 20 bytes
  • integrity_signature: 4 bytes.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}

El array de ciphertext bytes se divide en múltiples de 20 bytes secciones, con la excepción de que la última puede contener entre de 1 a 20 bytes, ambos incluidos. Para cada sección del documento byte_array, el ciphertext de 20 bytes correspondiente se genera de la siguiente manera:

<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>

En el ejemplo anterior, || es la concatenación.

Definiciones

Variable Detalles
initialization_vector 16 bytes: únicos para la impresión
encryption_key 32 bytes, se proporciona en la configuración de la cuenta.
integrity_key 32 bytes, se proporciona en la configuración de la cuenta.
byte_array Un objeto ExtraTagData serializado, en secciones de 20 bytes.
counter_bytes Valor de bytes que muestra el número ordinal de la sección, consulta a continuación.
final_message Total de array de bytes enviado a través de la macro %%EXTRA_TAG_DATA%% (menos la codificación WebSafeBase64).
Operadores Detalles
hmac(key, data) HMAC de SHA-1 que usa key para encriptar data.
a || b la cadena a concatenada con la cadena b.

Calcular count_bytes

counter_bytes marca el orden de cada sección de 20 bytes de la ciphertext Ten en cuenta que la última sección puede contener entre 1 y 20 bytes inclusive. Para completar counter_bytes con el valor correcto Cuando ejecutes la función hmac(), cuenta las secciones de 20 bytes (incluido el resto) y utiliza la siguiente tabla de referencia:

Número de sección Valor counter_bytes
0 Ninguno
1 ... 256 1 byte. El valor se incrementa de 0 a 255 de forma secuencial.
257 ... 512 2 bytes. El valor del primer byte es 0, el valor del segundo byte aumenta de 0 a 255 en secuencia.
513 ... 768 3 bytes. El valor de los dos primeros bytes es 0, el valor del último byte. aumenta de 0 a 255 en secuencia.

Volver al principio

Esquema de encriptación

El esquema de encriptación se basa en el mismo esquema utilizado para desencriptar la indicador de segmentación hiperlocal.

  1. Serialización: una instancia del objeto ExtraTagData definidos en el búfer de protocolo se serializan primero SerializeAsString() a un array de bytes.

  2. Encriptación: Luego, el array de bytes se encripta con un de encriptación personalizado diseñado para minimizar la sobrecarga de tamaño, a la vez que garantiza con una seguridad adecuada. El esquema de encriptación usa un algoritmo HMAC con clave para generar un módulo secreto basado en el initialization_vector, que es exclusivo para el evento de impresión.

Pseudocódigo de encriptación

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

Esquema de desencriptación

Tu código de desencriptación 1) debe desencriptar el búfer de protocolo con la encriptación y 2) verificar los bits de integridad con la clave de integridad. Las claves serán que se te proporcionaron durante la configuración de la cuenta. No hay restricciones en la forma estructurar tu implementación. En su mayoría, deberías poder aprovechar y adáptalo a tus necesidades.

  1. Genera tu pad: HMAC(encryption_key, initialization_vector || counter_bytes)
  2. XOR: Muestra este resultado y <xor> con el el texto cifrado para revertir la encriptación.
  3. Verificar: La firma de integridad pasa 4 bytes de HMAC(integrity_key, byte_array || initialization_vector)

Pseudocódigo de desencriptación

// 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)

Ejemplo de código de C++

Aquí se incluye una función clave de nuestra desencriptación código de ejemplo.

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');
    }
  }

Cómo obtener datos del búfer de protocolo de la red de publicidad

Una vez que decodificaste y desencriptaste los datos que se pasaron %%EXTRA_TAG_DATA%%, está todo listo para deserializar el búfer de protocolo y obtener el identificador de anunciante para la segmentación.

Si no conoces los búferes de protocolo, comienza con nuestra documentación.

Definición

Nuestro búfer de protocolo de la red de publicidad se define de la siguiente manera:

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;
}

Deberás deserializarlo con ParseFromString() como se describe en Documentación del búfer de protocolo C++.

Para obtener detalles sobre Android advertising_id y iOS, hashed_idfa campos, consulta Desencriptar ID de publicidad y Segmentación de aplicaciones para dispositivos móviles inventario con IDFA.

Biblioteca de Java

En lugar de implementar algoritmos criptográficos para codificar y decodificar los identificadores de anunciantes para redes de publicidad, puedes usar DoubleClickCrypto.java. Para obtener más información, consulta Criptografía.