Google Play Inline-Installationen (SDKs)

Auf dieser Seite wird beschrieben, wie Drittanbieter-SDKs die Inline-Installation einbinden können. Das ist eine neue Testfunktion für Google Play, bei der Produktdetails von Google Play-Apps in einer Half-Sheet-Oberfläche angezeigt werden. Mit der Inline-Installation können Nutzer Apps nahtlos installieren, ohne die App zu verlassen.

Drittanbieter-SDK-Entwickler können die Inline-Installation in ihre SDKs einbinden, damit App-Entwickler, die diese SDKs verwenden, Inline-Installationen für ihre Apps nutzen können.

Voraussetzungen

Damit das Inline-Installations-Halbsheet in einer App angezeigt wird, müssen folgende Voraussetzungen erfüllt sein:

  • Die Mindestversion von Google Play muss 40.4 sein.
  • Das Android-API‑Level muss 23 oder höher sein.

Prozessarchitektur

Die Architektur des Inline-Installationsprozesses ist in der folgenden Abbildung dargestellt:

Abbildung 1:Übersicht über die Architektur des Inline-Installationsprozesses.
  1. Google Play-Server generieren AEAD-Verschlüsselungsschlüssel (Authenticated Encryption with Associated Data) und übertragen die Schlüssel in eine Secret Manager-Instanz in der Google Cloud Platform (GCP).
  2. Der Drittanbieter ruft den AEAD-Schlüssel aus Google Cloud Secret Manager ab.
  3. Der Drittanbieter verschlüsselt die Inline-Installationsdaten Intent, generiert den Chiffretext, der im Deeplink übergeben wird, mit dem die Inline-Installationsabsicht aufgerufen wird, und sendet Deeplinks in Antworten an den Client.
  4. Wenn dem Deeplink gefolgt wird, verarbeitet die Google Play App den Intent.

So konfigurieren Sie ein Drittanbieter-SDK für die Verwendung des Inline-Installationsprozesses:

Dienstkonten in Google Cloud-Projekt erstellen

In diesem Schritt richten Sie ein Dienstkonto mit der Google Cloud Console ein.

  1. Google Cloud-Projekt einrichten:
    • Erstellen Sie eine Google Cloud-Organisation. Wenn Sie ein Google Workspace- oder Cloud Identity-Konto erstellen und es mit Ihrem Domainnamen verknüpfen, wird die Organisationsressource automatisch erstellt. Weitere Informationen finden Sie unter Organisationsressourcen erstellen und verwalten.
    • Melden Sie sich mit dem im vorherigen Schritt erstellten Google Cloud-Konto in der GCP Console an und erstellen Sie dann ein Google Cloud-Projekt. Weitere Informationen finden Sie unter Google Cloud-Projekt erstellen.
  2. Erstellen Sie ein Dienstkonto im erstellten Google Cloud-Projekt. Das Dienstkonto wird als Google Cloud-Identität verwendet, um im Namen Ihrer Server auf den symmetrischen Schlüssel zuzugreifen. Weitere Informationen finden Sie unter Dienstkonto erstellen.
  3. Verwenden Sie dieselbe Google Workspace-Kundennummer (GWCID) / Dasher-ID, die im Interessenbekundungsformular eingegeben wurde.
  4. Erstellen Sie den privaten Schlüssel für dieses Dienstkonto und laden Sie ihn herunter.
  5. Erstellen Sie einen Schlüssel für dieses Dienstkonto. Weitere Informationen finden Sie unter Dienstkontoschlüssel erstellen.
  6. Laden Sie den Dienstkontoschlüssel herunter und halten Sie ihn auf Ihrem Server zugänglich, da er für die Authentifizierung für den Zugriff auf Google Cloud-Ressourcen für die symmetrischen Schlüssel verwendet wird. Weitere Informationen finden Sie unter Dienstkontoschlüssel abrufen.

Anmeldedaten abrufen

In diesem Schritt rufen Sie den symmetrischen Schlüssel aus Secret Manager ab und speichern ihn sicher (z. B. in einer JSON-Datei) auf Ihrem eigenen Server. Dieser Schlüssel wird verwendet, um den Geheimtext der Inline-Installationsdaten zu generieren.

Die secret_id/secretId-Werte beziehen sich auf den Secret-Namen in Secret Manager. Dieser Name wird generiert, indem dem von Google Play bereitgestellten Wert sdk_id hsdp-3p-key- vorangestellt wird. Wenn sdk_id beispielsweise abc ist, lautet der Secret-Name hsdp-3p-key-abc.

Geheime Versionen werden wöchentlich am Dienstag um 14:00 Uhr UTC aktualisiert. Die zweitneuesten Schlüssel funktionieren bis zur nächsten Rotation. Schlüsselmaterial sollte wöchentlich neu abgerufen und gespeichert werden.

Beispiel für Python

Im folgenden Codebeispiel wird ein in einer JSON-Datei gespeichertes Zugriffstoken verwendet, um auf Schlüsselmaterial in GCP Secret Manager zuzugreifen und es in der Konsole auszugeben.

#!/usr/bin/env python3
# Import the Secret Manager client library.
from google.cloud import secretmanager
from google.oauth2 import service_account
import google_crc32c

# Create a service account key file.
service_account_key_file = "<json key file of the service account>"
credentials = service_account.Credentials.from_service_account_file(service_account_key_file)

# Create the Secret Manager client.
client = secretmanager.SecretManagerServiceClient(
  credentials=credentials
)

# Build the resource name of the secret version.
name = f"projects/prod-play-hsdp-3p-caller-auth/secrets/<secret_id>/versions/latest"

# Access the secret version.
response = client.access_secret_version(request={"name": name})

# Verify payload checksum.
crc32c = google_crc32c.Checksum()
crc32c.update(response.payload.data)
if response.payload.data_crc32c != int(crc32c.hexdigest(), 16):
    print("Data corruption detected.")

# A keyset created with "tinkey create-keyset --key-template=AES256_GCM". Note
# that this keyset has the secret key information in cleartext.
keyset = response.payload.data.decode("UTF-8")

# WARNING: Do not print the secret in a production environment. Please store it
# in a secure storage.
with open('<key file name>', 'w') as f:
    f.write(keyset)

Beispiel für Java

Im folgenden Codebeispiel wird ein in einer JSON-Datei gespeichertes Zugriffstoken verwendet, um auf Schlüsselmaterial in GCP Secret Manager zuzugreifen und es in eine JSON-Datei zu schreiben.

import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.api.gax.core.CredentialsProvider;
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.secretmanager.v1.AccessSecretVersionResponse;
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
import com.google.cloud.secretmanager.v1.SecretManagerServiceSettings;
import com.google.cloud.secretmanager.v1.SecretVersionName;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.zip.CRC32C;
import java.util.zip.Checksum;

/** */
final class ThirdPartySecretAccessGuide {

  private ThirdPartySecretAccessGuide() {}

  public static void main(String[] args) throws IOException {
    accessSecretVersion();
  }

  public static void accessSecretVersion() throws IOException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "projectId";
    String secretId = "secretId";
    String versionId = "versionId";
    String accessTokenPrivateKeyPath = "path/to/credentials.json";
    String secretMaterialOutputPath = "path/to/secret.json";
    accessSecretVersion(
        projectId, secretId, versionId, accessTokenPrivateKeyPath, secretMaterialOutputPath);
  }

  // Access the payload for the given secret version if one exists. The version
  // can be a version number as a string (e.g. "5") or an alias (e.g. "latest").
  public static void accessSecretVersion(
      String projectId,
      String secretId,
      String versionId,
      String accessTokenPrivateKeyPath,
      String secretMaterialOutputPath)
      throws IOException {

    // We can explicitly instantiate the SecretManagerServiceClient (below) from a json file if we:
    // 1. Create a CredentialsProvider from a FileInputStream of the JSON file,
    CredentialsProvider credentialsProvider =
        FixedCredentialsProvider.create(
            ServiceAccountCredentials.fromStream(new FileInputStream(accessTokenPrivateKeyPath)));

    // 2. Build a SecretManagerService Settings object from that credentials provider, and
    SecretManagerServiceSettings secretManagerServiceSettings =
        SecretManagerServiceSettings.newBuilder()
            .setCredentialsProvider(credentialsProvider)
            .build();

    // 3. Initialize client that will be used to send requests by passing the settings object to
    // create(). This client only needs to be created once, and can be reused for multiple requests.
    // After completing all of your requests, call the "close" method on the client to safely clean
    // up any remaining background resources.
    try (SecretManagerServiceClient client =
        SecretManagerServiceClient.create(secretManagerServiceSettings)) {
      SecretVersionName secretVersionName = SecretVersionName.of(projectId, secretId, versionId);

      // Access the secret version.
      AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName);

      // Verify checksum. The used library is available in Java 9+.
      // If using Java 8, you may use the following:
      // https://github.com/google/guava/blob/e62d6a0456420d295089a9c319b7593a3eae4a83/guava/src/com/google/common/hash/Hashing.java#L395
      byte[] data = response.getPayload().getData().toByteArray();
      Checksum checksum = new CRC32C();
      checksum.update(data, 0, data.length);
      if (response.getPayload().getDataCrc32C() != checksum.getValue()) {
        System.out.printf("Data corruption detected.");
        return;
      }

      String payload = response.getPayload().getData().toStringUtf8();
      // Print the secret payload.
      //
      // WARNING: Do not print the secret in a production environment - this
      // snippet is showing how to access the secret material.
      System.out.printf("Plaintext: %s\n", payload);

      // Write the JSON secret material payload to a json file
      try (PrintWriter out =
          new PrintWriter(Files.newBufferedWriter(Paths.get(secretMaterialOutputPath), UTF_8))) {
        out.write(payload);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}

Standardanmeldedaten für Anwendungen festlegen

Wenn Sie CredentialsProvider nicht verwenden möchten, um den privaten Schlüssel in der Java-Implementierung an eine JSON-Datei zu übergeben, können Sie die Implementierung ändern, indem Sie Standardanmeldedaten für Anwendungen (ADC) festlegen:

  1. Teilen Sie den Clientbibliotheken mit, wo der Dienstkontoschlüssel zu finden ist.
  2. Maven-Abhängigkeiten zum Java-Projekt hinzufügen.
  3. Rufen Sie SecretManagerServiceClient.create() auf. Die Authentifizierung wird automatisch übernommen (aufgrund von Schritt 1).

Mit diesen Schritten wird die Java-Implementierung geändert, indem:

  • Die CredentialsProvider- und SecretManagerServiceSettings-Objekte müssen nicht mehr erstellt werden.
  • Ändern Sie den Aufruf von SecretManagerServiceClient.create() so, dass er keine Argumente enthält.

Chiffretext erstellen und Deeplink generieren

In diesem Schritt verwenden Sie die Tink-Kryptografiebibliothek, um das enifd-Objekt (InlineInstallData-Chiffretext) aus dem InlineInstallData-Protobuf-Objekt zu erstellen. Das InlineInstallData-Proto ist so definiert:

syntax = "proto2";
package hsdpexperiments;
option java_package = "com.google.hsdpexperiments";
option java_multiple_files = true;

// InlineInstallData is used by 3p auth callers to generate "encrypted inline
// flow data" (enifd) which is decrypted in PGS to verify authenticity and
// freshness.
message InlineInstallData {
  // The timestamp which indicates the time encrypted data is generated.
  // Used to validate freshness (i.e. generation time in past 4 hours).
  // Required.
  optional int64 timestamp_ms = 1;

  // The docid of the app that we want to open inline install page for.
  // This is the package name.
  // Required.
  optional string target_package_name = 2;

  // This is the name of the app requesting the ad from Google Ad Serving
  // system.
  // Required.
  optional string caller_package_name = 3;

  // This is the advertising id that will be collected by 3P Ad SDKs.
  // Optional.
  optional string advertising_id = 4;

  // This is used to indicate the network from where the inline install was
  // requested.
  // Required.
  optional string ad_network_id = 5;
}

In diesem Schritt erstellen Sie auch die Deeplink-URL mit diesen Parametern:

Felder Beschreibung Erforderlich
id Der Paketname der App, die installiert werden soll. Ja
Inline Auf true setzen, wenn das Half-Sheet für die Inline-Installation angefordert wird. Bei false wird der Intent per Deeplink zu Google Play weitergeleitet. Ja
enifd Die verschlüsselte Kennung für Drittanbieter-SDKs. Ja
lft Eine interne Kennung. Ja
3pAuthCallerId Die SDK-Kennung. Ja
Eintrag Ein optionaler Parameter zur Angabe des Ziels für einen benutzerdefinierten Store-Eintrag. Nein
referrer Ein optionaler Referrer-Tracking-String. Nein

Beispiel für Python

Mit dem folgenden Befehl wird Python-Code aus InlineInstallData.proto generiert:

protoc InlineInstallData.proto --python_out=.

Im folgenden Python-Beispielcode wird InlineInstallData erstellt und mit dem symmetrischen Schlüssel verschlüsselt, um den Chiffretext zu erstellen:

#!/usr/bin/env python3

# Import the Secret Manager client library.
import base64
import time
import inline_install_data_pb2 as InlineInstallData
import tink
from tink import aead
from tink import cleartext_keyset_handle

# Read the stored symmetric key.
with open("example3psecret.json", "r") as f:
  keyset = f.read()

"""Encrypt and decrypt using AEAD."""
# Register the AEAD key managers. This is needed to create an Aead primitive later.
aead.register()

# Create a keyset handle from the cleartext keyset in the previous
# step. The keyset handle provides abstract access to the underlying keyset to
# limit access of the raw key material. WARNING: In practice, it is unlikely
# you will want to use a cleartext_keyset_handle, as it implies that your key
# material is passed in cleartext, which is a security risk.
keyset_handle = cleartext_keyset_handle.read(tink.JsonKeysetReader(keyset))

# Retrieve the Aead primitive we want to use from the keyset handle.
primitive = keyset_handle.primitive(aead.Aead)

inlineInstallData = InlineInstallData.InlineInstallData()
inlineInstallData.timestamp_ms = int(time.time() * 1000)
inlineInstallData.target_package_name = "x.y.z"
inlineInstallData.caller_package_name = "a.b.c"
inlineInstallData.ad_network_id = "<sdk_id>"

# Use the primitive to encrypt a message. In this case the primary key of the
# keyset will be used (which is also the only key in this example).
ciphertext = primitive.encrypt(inlineInstallData.SerializeToString(), b'<sdk_id>')
print(f"InlineInstallData Ciphertext: {ciphertext}")

# Base64 Encoded InlineInstallData Ciphertext
enifd = base64.urlsafe_b64encode(ciphertext).decode('utf-8')
print(enifd)

# Deeplink
print(f"https://play.google.com/d?id={inlineInstallData.target_package_name}\&inline=true\&enifd={enifd}\&lft=1\&3pAuthCallerId={inlineInstallData.ad_network_id}")

Führen Sie das Python-Skript mit dem folgenden Befehl aus:

python <file_name>.py

Beispiel für Java

Mit dem folgenden Befehl wird Java-Code aus InlineInstallData.proto generiert:

protoc InlineInstallData.proto --java_out=.

Im folgenden Java-Beispielcode wird InlineInstallData erstellt und mit dem symmetrischen Schlüssel verschlüsselt, um den Chiffretext zu erstellen:

package com.google.hsdpexperiments;

import static com.google.common.io.BaseEncoding.base64Url;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.flags.Flag;
import com.google.common.flags.FlagSpec;
import com.google.common.flags.Flags;
import com.google.crypto.tink.Aead;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.TinkJsonProtoKeysetFormat;
import com.google.crypto.tink.aead.AeadConfig;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.Security;
import java.time.Duration;
import org.conscrypt.Conscrypt;

/** info on encryption in https://github.com/google/tink#learn-more */
final class ThirdPartyEnifdGuide {

  @FlagSpec(
      name = "third_party_id",
      help = "the identifier associated with the 3p for which to generate the enifd")
  private static final Flag<String> thirdPartyAuthCallerId = Flag.value("");

  @FlagSpec(name = "package_name", help = "the package name of the target app")
  private static final Flag<String> packageName = Flag.value("");


  @FlagSpec(name = "caller_package_name", help = "the package name of the caller app")
  private static final Flag<String> callerPackageName = Flag.value("");

  @FlagSpec(name = "secret_filename", help = "the path to the json file with the secret material")
  private static final Flag<String> secretFilename = Flag.value("");

  private ThirdPartyEnifdGuide() {}

  public static void main(String[] args) throws Exception {
    // parse flags
    Flags.parse(args);

    // File keyFile = new File(args[0]);
    Path keyFile = Paths.get(secretFilename.get());

    // Create structured inline flow data
    InlineInstallData idrp =
        InlineInstallData.newBuilder()
            .setTargetPackageName(packageName.get())
            .setCallerPackageName(callerPackageName.get())
            .setTimestampMs(System.currentTimeMillis())
            .setAdNetworkId(thirdPartyAuthCallerId.get())
            .build();

    // we can print this out here to make sure it's well formatted, this will help debug
    System.out.println(idrp.toString());

    // Register all AEAD key types with the Tink runtime.
    Conscrypt.checkAvailability();
    Security.addProvider(Conscrypt.newProvider());
    AeadConfig.register();

    // Read AEAD key downloaded from secretmanager into keysethandle
    KeysetHandle handle =
        TinkJsonProtoKeysetFormat.parseKeyset(
            new String(Files.readAllBytes(keyFile), UTF_8), InsecureSecretKeyAccess.get());

    // Generate enifd using tink library
    Aead aead = handle.getPrimitive(Aead.class);
    byte[] plaintext = idrp.toByteArray();
    byte[] ciphertext = aead.encrypt(plaintext, thirdPartyAuthCallerId.get().getBytes(UTF_8));
    String enifd = base64Url().omitPadding().encode(ciphertext);

    // Build deeplink, escaping ampersands (TODO: verify this is necessary while testing e2e)
    String deeplink =
        "https://play.google.com/d?id="
            + packageName.get()
            + "\\&inline=true\\&enifd="
            + enifd
            + "\\&lft=1\\&3pAuthCallerId="
            + thirdPartyAuthCallerId.get();

    System.out.println(deeplink);
  }
}

Erstellen Sie schließlich das Java-Programm als Binärdatei und rufen Sie es mit dem folgenden Code auf:

path/to/binary/ThirdPartyEnifdGuide --secret_filename=path/to/jsonfile/example3psecret.json --package_name=<package_name_of_target_app> --third_party_id=<3p_caller_auth_id>
  • Das Flag secret_filename gibt den Pfad zur JSON-Datei an, die das vertrauliche Material enthält.
  • Das Flag package_name ist die Dokument-ID der Ziel-App.
  • Das Flag third_party_id wird verwendet, um die Auth-ID des Drittanbieteraufrufers (d. h. die <sdk_id>) anzugeben.

Intent für Inline-Installation starten

Um den im vorherigen Schritt generierten Deeplink zu testen, verbinden Sie ein Android-Gerät (USB-Debugging muss aktiviert sein) mit einer Workstation, auf der ADB installiert ist, und führen Sie den folgenden Befehl aus:

adb shell am start "<output_from_the_previous_python_or_java_code>"
enifd

Senden Sie im Clientcode den Intent mit einer der folgenden Methoden (Kotlin oder Java).

Kotlin

val intent = Intent(Intent.ACTION_VIEW)
val deepLinkUrl = "<output_from_the_previous_python_or_java_code>"
intent.setPackage("com.android.vending")
intent.data = Uri.parse(deepLinkUrl)
val packageManager = context.getPackageManager()
if (intent.resolveActivity(packageManager) != null) {
  startActivityForResult(intent, 0)
} else {
  // Fallback to deep linking to full Play Store.
}

Java

Intent intent = new Intent(Intent.ACTION_VIEW);
String id = "exampleAppToBeInstalledId";
String deepLinkUrl = "<output_from_the_previous_python_or_java_code>";
intent.setPackage("com.android.vending");
intent.setData(Uri.parse(deepLinkUrl));
PackageManager packageManager = context.getPackageManager();
if (intent.resolveActivity(packageManager) != null) {
  startActivityForResult(intent, 0);
} else {
  // Fallback to deep linking to full Play Store.
}

Anhang

In den folgenden Abschnitten finden Sie weitere Informationen zu bestimmten Anwendungsfällen.

Python-Umgebung vorbereiten

Wenn Sie den Python-Beispielcode ausführen möchten, richten Sie die Python-Umgebung auf Ihrer Workstation ein und installieren Sie die erforderlichen Abhängigkeiten.

  1. Richten Sie die Python-Umgebung ein:

    1. Installieren Sie python3.11 (wenn bereits installiert, überspringen Sie diesen Schritt):

      sudo apt install python3.11
      
    2. Installieren Sie pip:

      sudo apt-get install pip
      
    3. Installieren Sie virtualenv:

      sudo apt install python3-virtualenv
      
    4. Erstellen Sie eine virtuelle Umgebung (erforderlich für die Tink-Abhängigkeit):

      virtualenv inlineinstall --python=/usr/bin/python3.11
      
  2. Rufen Sie die virtuelle Umgebung auf:

    source inlineinstall/bin/activate
    
  3. pip aktualisieren:

    python -m pip install --upgrade pip
    
  4. Installieren Sie die erforderlichen Abhängigkeiten:

    1. Installieren Sie Tink:

      pip install tink
      
    2. Installieren Sie Google crc32c:

      pip install google-crc32c
      
    3. Secret Manager installieren:

      pip install google-cloud-secret-manager
      
    4. Installieren Sie den protobuf-Compiler:

      sudo apt install protobuf-compiler
      

C++-enifd-Generierung

Das folgende C++-Beispiel wurde von uns intern geschrieben und validiert, um die enifd zu generieren.

Die Generierung von enifd kann mit C++-Code wie folgt erfolgen:

// A command-line example for using Tink AEAD w/ key template aes128gcmsiv to
// encrypt an InlineInstallData proto.
#include <chrono>
#include <iostream>
#include <memory>
#include <string>

#include "<path_to_protoc_output>/inline_install_data.proto.h"
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "tink/cc/aead.h"
#include "tink/cc/aead_config.h"
#include "tink/cc/aead_key_templates.h"
#include "tink/cc/config/global_registry.h"
#include "tink/cc/examples/util/util.h"
#include "tink/cc/keyset_handle.h"
#include "tink/cc/util/status.h"
#include "tink/cc/util/statusor.h"

ABSL_FLAG(std::string, keyset_filename, "",
          "Keyset file (downloaded from secretmanager) in JSON format");
ABSL_FLAG(std::string, associated_data, "",
          "Associated data for AEAD (default: empty");

namespace {

using ::crypto::tink::Aead;
using ::crypto::tink::AeadConfig;
using ::crypto::tink::KeysetHandle;
using ::crypto::tink::util::Status;
using ::crypto::tink::util::StatusOr;

}  // namespace

namespace tink_cc_examples {

// AEAD example CLI implementation.
void AeadCli(const std::string& keyset_filename,
             absl::string_view associated_data) {
  Status result = AeadConfig::Register();
  if (!result.ok()) {
    std::clog << "Failed to register AeadConfig";
    return;
  }

  // Read the keyset from file.
  StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
      ReadJsonCleartextKeyset(keyset_filename);
  if (!keyset_handle.ok()) {
    std::clog << "Failed to read json keyset";
    return;
  }

  // Get the primitive.
  StatusOr<std::unique_ptr<Aead>> aead =
      (*keyset_handle)
          ->GetPrimitive<crypto::tink::Aead>(
              crypto::tink::ConfigGlobalRegistry());
  if (!aead.ok()) {
    std::clog << "Failed to get primitive";
    return;
  }

  // Instantiate the enifd.
  hsdpexperiments::InlineInstallData iid;

  iid.set_timestamp_ms(std::chrono::duration_cast<std::chrono::milliseconds>(
                           std::chrono::system_clock::now().time_since_epoch())
                           .count());
  iid.set_target_package_name("<TARGET_PACKAGE_NAME>");
  iid.set_caller_package_name("<CALLER_PACKAGE_NAME>");
  iid.set_ad_network_id("<SDK_ID>");

  // Compute the output.
  StatusOr<std::string> encrypt_result =
      (*aead)->Encrypt(iid.SerializeAsString(), associated_data);
  if (!encrypt_result.ok()) {
    std::clog << "Failed to encrypt Inline Install Data";
    return;
  }
  const std::string& output = encrypt_result.value();

  std::string enifd;
  absl::WebSafeBase64Escape(output, &enifd);

  std::clog << "enifd: " << enifd << '\n';
}

}  // namespace tink_cc_examples

int main(int argc, char** argv) {
  absl::ParseCommandLine(argc, argv);

  std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename);
  std::string associated_data = absl::GetFlag(FLAGS_associated_data);

  std::clog << "Using keyset from file " << keyset_filename
            << " to AEAD-encrypt inline install data with associated data '"
            << associated_data << "'." << '\n';

  tink_cc_examples::AeadCli(keyset_filename, associated_data);
  return 0;
}

Dieser Code wurde aus einem Beispiel in der Tink-Dokumentation übernommen.