Trang này mô tả cách các SDK bên thứ ba có thể tích hợp tính năng cài đặt trong dòng, một tính năng kiểm thử mới của Google Play, trình bày thông tin chi tiết về sản phẩm ứng dụng trên Google Play trong giao diện nửa trang. Tính năng cài đặt trong dòng giúp người dùng trải nghiệm quy trình cài đặt ứng dụng liền mạch mà không cần rời khỏi ngữ cảnh của ứng dụng.
Nhà phát triển SDK bên thứ ba có thể tích hợp tính năng cài đặt trong dòng vào SDK của họ để cho phép nhà phát triển ứng dụng sử dụng những SDK đó để truy cập vào tính năng cài đặt trong dòng cho ứng dụng của họ.
Yêu cầu
Để giao diện trang bán phần cài đặt trực tiếp xuất hiện trong một ứng dụng:
- Phiên bản Google Play tối thiểu phải là 40.4.
- Cấp độ API Android phải là 23 trở lên.
Cấu trúc quy trình
Cấu trúc quy trình cài đặt trong dòng được minh hoạ trong hình sau:
- Các máy chủ Google Play tạo khoá mã hoá AEAD (Mã hoá đã xác thực với dữ liệu liên kết) và chuyển các khoá này vào một phiên bản Secret Manager của Google Cloud Platform (GCP).
- Bên tích hợp thứ ba truy xuất khoá AEAD từ GCP Secret Manager.
- Trình tích hợp bên thứ ba mã hoá dữ liệu cài đặt trực tiếp
Intent, tạo văn bản mã hoá được truyền trong đường liên kết sâu dùng để gọi ý định cài đặt trực tiếp và gửi đường liên kết sâu đến ứng dụng trong các phản hồi. - Khi người dùng truy cập vào đường liên kết sâu, ứng dụng Google Play sẽ xử lý ý định này.
Để định cấu hình một SDK của bên thứ ba sử dụng quy trình cài đặt trong dòng, hãy hoàn tất các bước sau.
Tạo tài khoản dịch vụ trong Dự án trên Google Cloud
Trong bước này, bạn thiết lập một tài khoản dịch vụ bằng Bảng điều khiển Google Cloud.
- Thiết lập một dự án trên Google Cloud:
- Tạo một tổ chức trên Google Cloud. Khi bạn tạo tài khoản Google Workspace hoặc Cloud Identity và liên kết tài khoản đó với tên miền của mình, tài nguyên tổ chức sẽ tự động được tạo. Để biết thông tin chi tiết, hãy tham khảo bài viết Tạo và quản lý tài nguyên của tổ chức.
- Đăng nhập vào Bảng điều khiển GCP bằng Tài khoản Google Cloud mà bạn đã tạo ở bước trước, sau đó tạo một dự án trên Google Cloud. Để biết thông tin chi tiết, hãy tham khảo bài viết Tạo dự án trên Google Cloud.
- Tạo một tài khoản dịch vụ trong dự án trên đám mây của Google mà bạn đã tạo. Tài khoản dịch vụ được dùng làm danh tính Google Cloud để truy cập vào khoá đối xứng thay cho các máy chủ của bạn. Để biết thông tin chi tiết, hãy tham khảo bài viết Tạo tài khoản dịch vụ.
- Sử dụng cùng một Mã khách hàng Google Workspace (GWCID) / Dasher ID đã được nhập trên biểu mẫu thể hiện sự quan tâm.
- Tạo và tải khoá riêng tư của tài khoản dịch vụ đó xuống.
- Tạo một khoá cho tài khoản dịch vụ đó. Để biết thông tin chi tiết, hãy xem bài viết Tạo khoá tài khoản dịch vụ.
- Tải khoá tài khoản dịch vụ xuống và giữ cho khoá này có thể truy cập trên máy chủ của bạn, vì khoá này được dùng để xác thực nhằm truy cập vào các tài nguyên của Google Cloud cho các khoá đối xứng. Để biết thông tin chi tiết, hãy xem bài viết Tạo một khoá tài khoản dịch vụ.
Truy xuất thông tin đăng nhập
Ở bước này, bạn sẽ truy xuất khoá đối xứng từ Secret Manager và lưu trữ khoá đó một cách an toàn (ví dụ: trong tệp JSON) trên bộ nhớ máy chủ của riêng bạn. Khoá này được dùng để tạo văn bản mã hoá dữ liệu cài đặt trong dòng.
Các giá trị secret_id/secretId đề cập đến tên bí mật trong Trình quản lý bí mật; tên này được tạo bằng cách thêm hsdp-3p-key- vào giá trị sdk_id do Play cung cấp. Ví dụ: nếu sdk_id là abc, thì tên bí mật là hsdp-3p-key-abc.
Các phiên bản bí mật được cập nhật hằng tuần vào lúc 14:00 UTC thứ Ba. Các khoá mới thứ hai vẫn tiếp tục hoạt động cho đến lần thay khoá tiếp theo và nội dung khoá phải được tìm nạp và lưu trữ mới mỗi tuần.
Ví dụ về Python
Ví dụ về mã sau đây sử dụng mã truy cập được lưu trữ trong tệp JSON để truy cập vào tài liệu khoá trong Trình quản lý bí mật của GCP và in tài liệu đó ra bảng điều khiển.
#!/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)
Ví dụ về Java
Ví dụ về mã sau đây sử dụng mã truy cập được lưu trữ trong tệp JSON để truy cập vào khoá trong Trình quản lý bí mật của GCP và ghi khoá đó vào tệp JSON.
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();
}
}
}
}
Đặt thông tin xác thực mặc định của ứng dụng
Nếu không muốn sử dụng CredentialsProvider để truyền khoá riêng tư đến một tệp JSON trong quá trình triển khai Java, bạn có thể sửa đổi quá trình triển khai bằng cách đặt Thông tin xác thực mặc định của ứng dụng (ADC):
- Cho thư viện ứng dụng biết nơi tìm khoá tài khoản dịch vụ.
- Thêm các phần phụ thuộc Maven vào dự án Java.
- Gọi
SecretManagerServiceClient.create(), hệ thống sẽ tự động nhận thông tin xác thực (do bước 1).
Các bước này sửa đổi việc triển khai Java bằng cách:
- Không cần tạo các đối tượng
CredentialsProvidervàSecretManagerServiceSettings. - Thay đổi lệnh gọi thành
SecretManagerServiceClient.create()để không có đối số.
Tạo văn bản mã hoá và tạo đường liên kết sâu
Ở bước này, bạn dùng thư viện mật mã học Tink để tạo enifd (văn bản mã hoá InlineInstallData) từ đối tượng protobuf InlineInstallData.
Nguyên mẫu InlineInstallData được xác định như sau:
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;
}
Trong bước này, bạn cũng tạo URL liên kết sâu bằng các tham số sau:
| Trường | Mô tả | Bắt buộc |
|---|---|---|
| id | Tên gói của ứng dụng sẽ được cài đặt. | Có |
| nội dòng | Đặt thành true nếu yêu cầu trang bán phần cài đặt trực tiếp; nếu false, ý định sẽ liên kết sâu đến Google Play. |
Có |
| enifd | Giá trị nhận dạng được mã hoá cho SDK của bên thứ ba. | Có |
| lft | Giá trị nhận dạng nội bộ. | Có |
| 3pAuthCallerId | Giá trị nhận dạng SDK. | Có |
| trang thông tin | Một tham số không bắt buộc để chỉ định mục tiêu cho một trang thông tin tuỳ chỉnh trên Cửa hàng Play. | Không |
| người giới thiệu | Một chuỗi theo dõi trình giới thiệu không bắt buộc. | Không |
Ví dụ về Python
Lệnh sau đây tạo mã Python từ InlineInstallData.proto:
protoc InlineInstallData.proto --python_out=.
Đoạn mã mẫu Python sau đây tạo InlineInstallData và mã hoá bằng khoá đối xứng để tạo văn bản mã hoá:
#!/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}")
Thực thi tập lệnh Python bằng cách chạy lệnh sau:
python <file_name>.py
Ví dụ về Java
Lệnh sau đây tạo mã Java từ InlineInstallData.proto:
protoc InlineInstallData.proto --java_out=.
Mã mẫu Java sau đây tạo InlineInstallData và mã hoá bằng khoá đối xứng để tạo văn bản mã hoá:
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);
}
}
Cuối cùng, hãy tạo chương trình Java thành một tệp nhị phân và gọi chương trình đó bằng mã sau:
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>
- Cờ
secret_filenamechỉ định đường dẫn đến tệp JSON chứa nội dung bí mật. - Cờ
package_namelà mã nhận dạng tài liệu của ứng dụng mục tiêu. - Cờ
third_party_idđược dùng để chỉ định mã nhận dạng uỷ quyền của bên gọi thứ ba (tức là<sdk_id>).
Khởi chạy ý định Cài đặt trực tiếp
Để kiểm thử đường liên kết sâu được tạo trong bước trước, hãy kết nối một thiết bị Android (đảm bảo bạn đã bật chế độ gỡ lỗi qua USB) với một máy trạm đã cài đặt ADB và chạy lệnh sau:
adb shell am start "<output_from_the_previous_python_or_java_code>"
Trong mã ứng dụng, hãy gửi ý định bằng một trong các phương thức sau (Kotlin hoặc 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.
}
Phụ lục
Các phần sau đây cung cấp thêm hướng dẫn về một số trường hợp sử dụng nhất định.
Chuẩn bị môi trường Python
Để chạy mã mẫu Python, hãy thiết lập môi trường Python trên máy trạm và cài đặt các phần phụ thuộc bắt buộc.
Thiết lập môi trường Python:
Cài đặt python3.11 (nếu đã cài đặt, hãy bỏ qua bước này):
sudo apt install python3.11Cài đặt pip:
sudo apt-get install pipCài đặt
virtualenv:sudo apt install python3-virtualenvTạo một môi trường ảo (bắt buộc đối với phần phụ thuộc Tink):
virtualenv inlineinstall --python=/usr/bin/python3.11
Bước vào môi trường ảo:
source inlineinstall/bin/activateCập nhật pip:
python -m pip install --upgrade pipCài đặt các phần phụ thuộc bắt buộc:
Cài đặt Tink:
pip install tinkCài đặt Google crc32c:
pip install google-crc32cCài đặt Secret Manager:
pip install google-cloud-secret-managerCài đặt trình biên dịch protobuf:
sudo apt install protobuf-compiler
Thế hệ enifd C++
Sau đây là một ví dụ về C++ mà chúng tôi đã viết và xác thực nội bộ để tạo enifd.
Bạn có thể tạo enifd bằng mã C++ như sau:
// 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;
}
Mã này được điều chỉnh từ một mẫu mà bạn có thể tìm thấy trong tài liệu Tink.