Tạo yêu cầu API kiểu cũ

Nếu chỉ định đưa ra các yêu cầu API thông thường phù hợp với phần lớn nhà phát triển, thì bạn có thể bỏ qua kết quả về tính toàn vẹn. Trang này mô tả cách thực hiện các yêu cầu API kiểu cũ cho kết quả về tính toàn vẹn được hỗ trợ trên Android 4.4 (API cấp 19) trở lên.

Những yếu tố nên cân nhắc

So sánh yêu cầu thông thường và yêu cầu kiểu cũ

Bạn có thể thực hiện các yêu cầu thông thường, yêu cầu kiểu cũ hoặc kết hợp cả 2 tuỳ thuộc vào nhu cầu của ứng dụng về bảo mật và chống hành vi sử dụng sai mục đích. Các yêu cầu thông thường phù hợp với tất cả các ứng dụng và trò chơi, cũng như có thể dùng để kiểm tra nhằm đảm bảo rằng một hành động hoặc lệnh gọi máy chủ bất kỳ là thật, đồng thời uỷ quyền cho Google Play thực hiện một số biện pháp ngăn chặn hành vi phát lại và đánh cắp. Việc thực hiện các yêu cầu kiểu cũ sẽ tốn kém hơn và bạn có trách nhiệm triển khai chúng một cách chính xác để ngăn chặn hành vi đánh cắp cũng như một số loại tấn công. Bạn nên thực hiện các yêu cầu kiểu cũ với tần suất ít hơn so với các yêu cầu thông thường, chẳng hạn như thỉnh thoảng thực hiện một lần để kiểm tra xem một hành động nhạy cảm hoặc có giá trị cao có phải là thật hay không.

Bảng sau đây nêu bật các điểm khác biệt chính giữa 2 loại yêu cầu này:

Yêu cầu API thông thường Yêu cầu API kiểu cũ
Điều kiện tiên quyết
Cần có phiên bản SDK Android tối thiểu Android 5.0 (API cấp 21) trở lên Android 4.4 (API cấp 19) trở lên
Yêu cầu của Google Play Cửa hàng Google Play và Dịch vụ Google Play Cửa hàng Google Play và Dịch vụ Google Play
Thông tin chi tiết về việc tích hợp
Cần phải khởi động API ✔️ (vài giây)
Độ trễ thông thường của yêu cầu Vài trăm mili giây Vài giây
Tần suất yêu cầu tiềm năng Thường xuyên (kiểm tra theo yêu cầu đối với bất kỳ hành động hoặc yêu cầu nào) Không thường xuyên (kiểm tra một lần đối với các hành động có giá trị cao nhất hoặc các yêu cầu mang tính nhạy cảm nhất)
Số lần hết thời gian chờ Hầu hết các lần khởi động có thời gian dưới 10 giây nhưng chúng liên quan đến một lệnh gọi máy chủ, vì vậy, bạn nên thiết lập thời gian chờ dài (chẳng hạn như 1 phút). Yêu cầu kết quả xảy ra ở phía máy khách Hầu hết các yêu cầu có thời gian dưới 10 giây nhưng chúng liên quan đến một lệnh gọi máy chủ, vì vậy, bạn nên thiết lập thời gian chờ dài (ví dụ: 1 phút)
Mã thông báo kết quả về tính toàn vẹn
Chứa thông tin chi tiết về thiết bị, ứng dụng và tài khoản ✔️ ✔️
Lưu mã thông báo vào bộ nhớ đệm Lưu vào bộ nhớ đệm trên thiết bị được Google Play bảo vệ Không nên dùng
Giải mã và xác minh mã thông báo qua máy chủ của Google Play ✔️ ✔️
Độ trễ thông thường của yêu cầu giải mã từ máy chủ đến máy chủ Hàng chục mili giây với thời gian hoạt động từ 3 đến 9 giây Hàng chục mili giây với thời gian hoạt động từ 3 đến 9 giây
Giải mã và xác minh mã thông báo trên máy trong môi trường máy chủ bảo mật ✔️
Giải mã và xác minh mã thông báo ở phía máy khách
Độ mới của kết quả về tính toàn vẹn Một số kết quả tự động được Google Play làm mới và lưu vào bộ nhớ đệm Tất cả các kết quả được tính toán lại cho mỗi yêu cầu
Giới hạn
Số yêu cầu trên mỗi ứng dụng mỗi ngày 10.000 theo mặc định (có thể yêu cầu tăng mức này lên) 10.000 theo mặc định (có thể yêu cầu tăng mức này lên)
Số yêu cầu mỗi phút trên mỗi phiên bản ứng dụng Khởi động: 5 yêu cầu mỗi phút
Mã thông báo về tính toàn vẹn: Không có giới hạn công khai*
Mã thông báo về tính toàn vẹn: 5 mã mỗi phút
Bảo vệ
Giảm thiểu hành vi can thiệp và các cuộc tấn công tương tự Dùng trường requestHash Sử dụng trường nonce với liên kết nội dung dựa trên dữ liệu yêu cầu
Giảm thiểu cuộc tấn công phát lại và các cuộc tấn công tương tự Tự động giảm thiểu nhờ Google Play Sử dụng trường nonce với logic phía máy chủ

* Tất cả yêu cầu, kể cả những yêu cầu không có giới hạn công khai, sẽ tuân theo các giới hạn phòng vệ không công khai khi ở giá trị cao

Tạo yêu cầu kiểu cũ với tần suất không thường xuyên

Việc tạo mã thông báo về tính toàn vẹn sẽ tốn thời gian, dữ liệu và pin, đồng thời mỗi ứng dụng sẽ có một mức tối đa cho số lượng yêu cầu kiểu cũ có thể thực hiện mỗi ngày. Do đó, bạn chỉ nên thực hiện các yêu cầu kiểu cũ để kiểm tra nhằm đảm bảo các hành động có giá trị cao nhất hoặc mang tính nhạy cảm nhất là thật khi muốn có sự chắc chắn hơn nữa so với yêu cầu thông thường. Bạn không nên thực hiện các yêu cầu kiểu cũ cho những hành động có tần suất cao hoặc có giá trị thấp. Không thực hiện các yêu cầu kiểu cũ mỗi khi ứng dụng chuyển sang nền trước hoặc cứ vài phút một lần trong nền, đồng thời tránh gọi từ nhiều thiết bị cùng lúc. Một ứng dụng thực hiện quá nhiều lệnh gọi yêu cầu kiểu cũ có thể bị hạn chế để bảo vệ người dùng khỏi các phương thức triển khai không chính xác.

Tránh lưu kết quả vào bộ nhớ đệm

Việc lưu kết quả vào bộ nhớ đệm sẽ làm tăng nguy cơ bị tấn công (chẳng hạn như đánh cắp và phát lại), trong đó một kết quả tốt được sử dụng lại trong một môi trường không đáng tin cậy. Nếu đang cân nhắc tạo một yêu cầu kiểu cũ rồi lưu vào bộ nhớ đệm để sử dụng sau này, thì thay vào đó, bạn nên thực hiện một yêu cầu thông thường khi có nhu cầu. Các yêu cầu thông thường có liên quan đến một số hoạt động lưu vào bộ nhớ đệm trên thiết bị nhưng Google Play sử dụng thêm các kỹ thuật bảo vệ để giảm thiểu nguy cơ bị tấn công phát lại và đánh cắp dữ liệu.

Sử dụng trường số chỉ dùng một lần để bảo vệ yêu cầu kiểu cũ

API Tính toàn vẹn của Play cung cấp một trường có tên là nonce để giúp ứng dụng chống lại một số cuộc tấn công nhất định, chẳng hạn như cuộc tấn công phát lại và can thiệp. API Tính toàn vẹn của Play sẽ trả về giá trị bạn đặt trong trường này, bên trong phản hồi về tính toàn vẹn đã ký. Hãy cẩn thận làm theo hướng dẫn về cách tạo số chỉ dùng một lần để giúp ứng dụng chống lại các cuộc tấn công.

Thử lại yêu cầu kiểu cũ bằng thuật toán thời gian đợi luỹ thừa

Các tình trạng môi trường, chẳng hạn như kết nối Internet không ổn định hoặc thiết bị quá tải có thể khiến việc kiểm tra tính toàn vẹn của thiết bị thất bại. Điều này có thể dẫn đến việc không tạo nhãn cho một thiết bị không đáng tin cậy. Để giảm thiểu các trường hợp này, hãy bao gồm lựa chọn thử lại bằng thuật toán thời gian đợi luỹ thừa.

Tổng quan

Sơ đồ trình tự thể hiện thiết kế cấp cao của API Tính toàn vẹn của Play

Khi người dùng thực hiện một hành động có giá trị cao trong ứng dụng và bạn muốn bảo vệ bằng quy trình kiểm tra tính toàn vẹn, hãy hoàn thành các bước sau:

  1. Phần phụ trợ phía máy chủ của ứng dụng sẽ tạo và gửi một giá trị duy nhất đến logic phía máy khách. Các bước còn lại gọi logic này là "ứng dụng".
  2. Ứng dụng của bạn tạo ra nonce từ giá trị duy nhất và nội dung của hành động có giá trị cao. Sau đó, ứng dụng sẽ gọi API Tính toàn vẹn của Play, truyền vào giá trị nonce.
  3. Ứng dụng sẽ nhận kết quả đã ký và mã hoá từ API Tính toàn vẹn của Play.
  4. Ứng dụng chuyển kết quả đã ký và mã hoá cho phần phụ trợ của ứng dụng.
  5. Phần phụ trợ của ứng dụng sẽ gửi kết quả đến máy chủ Google Play. Máy chủ Google Play giải mã và xác minh kết quả, trả về kết quả cho phần phụ trợ của ứng dụng.
  6. Phần phụ trợ của ứng dụng sẽ quyết định cách tiến hành, dựa trên các tín hiệu có trong phần tải trọng mã thông báo.
  7. Máy chủ phụ trợ của ứng dụng sẽ gửi kết quả về quyết định cho ứng dụng.

Tạo một số chỉ dùng một lần

Khi dùng API Tính toàn vẹn của Play để bảo vệ một hành động trong ứng dụng của mình, bạn có thể tận dụng trường nonce để giảm thiểu một số loại tấn công, chẳng hạn như tấn công can thiệp xen giữa (PITM) và tấn công phát lại (replay attack). API Tính toàn vẹn của Play sẽ trả về giá trị bạn đặt trong trường này, bên trong phản hồi về tính toàn vẹn đã ký.

Giá trị đã đặt trong trường nonce phải được định dạng chính xác:

  • String
  • URL-safe
  • Mã hoá dưới dạng Base64 và không đóng gói
  • Tối thiểu 16 ký tự
  • Tối đa 500 ký tự

Sau đây là một số cách phổ biến để sử dụng trường nonce trong API Tính toàn vẹn của Play. Để có được biện pháp bảo vệ mạnh nhất từ nonce này, bạn có thể kết hợp các phương thức dưới đây.

Thêm hàm băm yêu cầu để ngăn chặn hành vi can thiệp

Bạn có thể dùng tham số nonce trong yêu cầu API kiểu cũ tương tự như tham số requestHash trong yêu cầu API thông thường để bảo vệ nội dung của yêu cầu trước hành vi can thiệp.

Khi bạn yêu cầu kết quả về tính toàn vẹn:

  1. Tính toán chuỗi đại diện của tất cả các tham số yêu cầu quan trọng (ví dụ: SHA256 của quá trình chuyển đổi tuần tự yêu cầu có tính ổn định) từ hành động của người dùng hoặc yêu cầu tới máy chủ đang diễn ra.
  2. Dùng setNonce để đặt trường nonce thành giá trị của chuỗi đại diện đã tính toán.

Khi bạn nhận được kết quả về tính toàn vẹn:

  1. Giải mã và xác minh mã thông báo về tính toàn vẹn, đồng thời lấy chuỗi đại diện từ trường nonce.
  2. Tính toán chuỗi đại diện của yêu cầu theo cách tương tự như trong ứng dụng (ví dụ: SHA256 của quá trình chuyển đổi tuần tự yêu cầu có tính ổn định).
  3. So sánh chuỗi đại diện phía ứng dụng và phía máy chủ. Nếu chúng không khớp nhau thì yêu cầu không đáng tin cậy.

Thêm các giá trị duy nhất để ngăn chặn các cuộc tấn công phát lại

Để ngăn người dùng độc hại sử dụng lại những phản hồi trước đó từ API Tính toàn vẹn của Play, bạn có thể sử dụng trường nonce để xác định riêng biệt từng thông báo.

Khi bạn yêu cầu kết quả về tính toàn vẹn:

  1. Thu được giá trị duy nhất trên toàn hệ thống theo cách mà người dùng độc hại không thể dự đoán. Ví dụ: giá trị này có thể là một số ngẫu nhiên được bảo mật bằng mật mã do phía máy chủ tạo ra hoặc một mã nhận dạng có sẵn, chẳng hạn như mã phiên hoặc mã giao dịch. Một biến thể đơn giản và kém an toàn hơn là tạo một số ngẫu nhiên trên thiết bị. Bạn nên tạo các giá trị 128 bit trở lên.
  2. Gọi setNonce() để đặt trường nonce sang giá trị duy nhất ở bước 1.

Khi bạn nhận được kết quả về tính toàn vẹn:

  1. Giải mã và xác minh mã thông báo về tính toàn vẹn, đồng thời lấy giá trị duy nhất từ trường nonce.
  2. Nếu giá trị ở bước 1 được tạo trên máy chủ, hãy kiểm tra để chắc chắn rằng giá trị duy nhất nhận được là một trong các giá trị đã tạo và đây là lần đầu sử dụng giá trị này (máy chủ của bạn cần lưu giữ bản ghi các giá trị đã tạo trong khoảng thời gian phù hợp). Nếu giá trị duy nhất nhận được đã được sử dụng hoặc không xuất hiện trong bản ghi, hãy từ chối yêu cầu
  3. Ngược lại, nếu giá trị duy nhất được tạo trên thiết bị, hãy kiểm tra để đảm bảo rằng đây là lần đầu sử dụng giá trị đã nhận (máy chủ của bạn cần lưu giữ bản ghi các giá trị đã thấy trong khoảng thời gian thích hợp). Nếu giá trị duy nhất nhận được đã được sử dụng, hãy từ chối yêu cầu.

Kết hợp cả 2 biện pháp bảo vệ để ngăn chặn hành vi can thiệp và các cuộc tấn công phát lại (nên dùng)

Bạn có thể sử dụng trường nonce để cùng lúc ngăn chặn cả cuộc tấn công can thiệp lẫn cuộc tấn công phát lại. Để làm như vậy, hãy tạo giá trị duy nhất như mô tả ở trên và đưa giá trị đó vào yêu cầu. Sau đó, hãy tính toán hàm băm yêu cầu, nhớ bao gồm giá trị duy nhất trong hàm băm. Cách triển khai kết hợp cả hai phương pháp thực hiện như sau:

Khi bạn yêu cầu kết quả về tính toàn vẹn:

  1. Người dùng bắt đầu thực hiện hành động có giá trị cao.
  2. Nhận một giá trị duy nhất cho hành động này như mô tả trong phần Thêm các giá trị duy nhất để ngăn chặn các cuộc tấn công phát lại.
  3. Chuẩn bị thông điệp mà bạn muốn bảo vệ. Đưa giá trị duy nhất ở bước 2 vào thông điệp.
  4. Ứng dụng sẽ tính toán một chuỗi đại diện cho thông điệp mà ứng dụng muốn bảo vệ, như mô tả trong phần Thêm hàm băm yêu cầu để ngăn chặn hành vi can thiệp. Vì thông điệp chứa giá trị duy nhất, nên giá trị duy nhất là một phần của hàm băm.
  5. Dùng setNonce() để đặt trường nonce sang chuỗi đại diện đã tính toán ở bước trước.

Khi bạn nhận được kết quả về tính toàn vẹn:

  1. Nhận giá trị duy nhất từ yêu cầu
  2. Giải mã và xác minh mã thông báo về tính toàn vẹn, đồng thời lấy chuỗi đại diện từ trường nonce.
  3. Như đã mô tả trong phần Thêm hàm băm yêu cầu để ngăn chặn vi can thiệp, hãy tính toán lại chuỗi đại diện này ở phía máy chủ và kiểm tra để đảm bảo chuỗi đó khớp với chuỗi đại diện thu được từ mã thông báo về tính toàn vẹn.
  4. Như được mô tả trong phần Thêm các giá trị duy nhất để ngăn chặn các cuộc tấn công phát lại, hãy kiểm tra tính hợp lệ của giá trị duy nhất.

Sơ đồ trình tự sau đây minh hoạ các bước này bằng nonce phía máy chủ:

Sơ đồ trình tự cho biết cách ngăn chặn cả các cuộc tấn công can thiệp lẫn phát lại

Yêu cầu kết quả về tính toàn vẹn

Sau khi tạo nonce, bạn có thể yêu cầu Google Play cung cấp một kết quả về tính toàn vẹn. Để làm được điều này, vui lòng hoàn thành các bước sau:

  1. Tạo một IntegrityManager như trong các ví dụ sau.
  2. Tạo IntegrityTokenRequest, cung cấp nonce thông qua phương thức setNonce() trong trình tạo được liên kết. Các ứng dụng được phân phối độc quyền bên ngoài Google Play và các SDK cũng phải chỉ định số dự án Google Cloud thông qua phương thức setCloudProjectNumber(). Các ứng dụng trên Google Play được liên kết với một dự án Cloud trong Play Console mà không cần đặt số dự án Cloud trong yêu cầu.
  3. Sử dụng trình quản lý để gọi requestIntegrityToken(), cung cấp IntegrityTokenRequest.

Kotlin

// Receive the nonce from the secure server.
val nonce: String = ...

// Create an instance of a manager.
val integrityManager =
    IntegrityManagerFactory.create(applicationContext)

// Request the integrity token by providing a nonce.
val integrityTokenResponse: Task<IntegrityTokenResponse> =
    integrityManager.requestIntegrityToken(
        IntegrityTokenRequest.builder()
             .setNonce(nonce)
             .build())

Java

import com.google.android.gms.tasks.Task; ...

// Receive the nonce from the secure server.
String nonce = ...

// Create an instance of a manager.
IntegrityManager integrityManager =
    IntegrityManagerFactory.create(getApplicationContext());

// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> integrityTokenResponse =
    integrityManager
        .requestIntegrityToken(
            IntegrityTokenRequest.builder().setNonce(nonce).build());

Unity

IEnumerator RequestIntegrityTokenCoroutine() {
    // Receive the nonce from the secure server.
    var nonce = ...

    // Create an instance of a manager.
    var integrityManager = new IntegrityManager();

    // Request the integrity token by providing a nonce.
    var tokenRequest = new IntegrityTokenRequest(nonce);
    var requestIntegrityTokenOperation =
        integrityManager.RequestIntegrityToken(tokenRequest);

    // Wait for PlayAsyncOperation to complete.
    yield return requestIntegrityTokenOperation;

    // Check the resulting error code.
    if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
    {
        AppendStatusLog("IntegrityAsyncOperation failed with error: " +
                requestIntegrityTokenOperation.Error);
        yield break;
    }

    // Get the response.
    var tokenResponse = requestIntegrityTokenOperation.GetResult();
}

Mã gốc

/// Create an IntegrityTokenRequest opaque object.
const char* nonce = RequestNonceFromServer();
IntegrityTokenRequest* request;
IntegrityTokenRequest_create(&request);
IntegrityTokenRequest_setNonce(request, nonce);

/// Prepare an IntegrityTokenResponse opaque type pointer and call
/// IntegerityManager_requestIntegrityToken().
IntegrityTokenResponse* response;
IntegrityErrorCode error_code =
        IntegrityManager_requestIntegrityToken(request, &response);

/// ...
/// Proceed to polling iff error_code == INTEGRITY_NO_ERROR
if (error_code != INTEGRITY_NO_ERROR)
{
    /// Remember to call the *_destroy() functions.
    return;
}
/// ...
/// Use polling to wait for the async operation to complete.
/// Note, the polling shouldn't block the thread where the IntegrityManager
/// is running.

IntegrityResponseStatus response_status;

/// Check for error codes.
IntegrityErrorCode error_code =
        IntegrityTokenResponse_getStatus(response, &response_status);
if (error_code == INTEGRITY_NO_ERROR
    && response_status == INTEGRITY_RESPONSE_COMPLETED)
{
    const char* integrity_token = IntegrityTokenResponse_getToken(response);
    SendTokenToServer(integrity_token);
}
/// ...
/// Remember to free up resources.
IntegrityTokenRequest_destroy(request);
IntegrityTokenResponse_destroy(response);
IntegrityManager_destroy();

Giải mã và xác minh kết quả về tính toàn vẹn

Khi bạn yêu cầu một kết quả về tính toàn vẹn, API Tính toàn vẹn của Play sẽ cung cấp một mã thông báo phản hồi đã ký. nonce mà bạn đưa vào yêu cầu sẽ trở thành một phần của mã thông báo phản hồi.

Định dạng mã thông báo

Mã thông báo là một Mã thông báo web JSON (JWT) lồng ghép, đó là Mã hoá web JSON (JWE) của Chữ ký web JSON (JWS) Các thành phần JWE và JWS được biểu thị bằng quá trình chuyển đổi tuần tự nhỏ gọn.

Các thuật toán mã hoá/ký tên được hỗ trợ tốt trong nhiều cách triển khai JWT khác nhau:

  • JWE sử dụng A256KW cho alg và A256GCM cho enc

  • JWS sử dụng ES256.

Giải mã và xác minh trên các máy chủ của Google (được khuyến nghị)

API Tính toàn vẹn của Play giúp bạn giải mã và xác minh kết quả về tính toàn vẹn trên các máy chủ của Google, nhằm tăng cường bảo mật cho ứng dụng của bạn. Để làm thế, hãy hoàn thành các bước sau:

  1. Tạo một tài khoản dịch vụ trong dự án Google Cloud được liên kết với ứng dụng của bạn. Trong quá trình tạo tài khoản này, bạn cần cấp cho tài khoản dịch vụ của bạn vai trò Service Account User (Người dùng tài khoản dịch vụ) và Service Usage Consumer (Người tiêu dùng sử dụng dịch vụ).
  2. Trên máy chủ của ứng dụng, tìm nạp mã truy cập từ thông tin xác thực tài khoản dịch vụ của bạn bằng phạm vi playintegrity và thực hiện yêu cầu sau:

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
    '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. Đọc phản hồi JSON.

Giải mã và xác minh trên thiết bị

Nếu chọn quản lý và tải khoá mã hoá phản hồi xuống, thì bạn có thể giải mã và xác minh mã thông báo được trả về trong môi trường máy chủ bảo mật của riêng mình. Bạn có thể lấy mã thông báo được trả về bằng cách sử dụng phương thức IntegrityTokenResponse#token().

Ví dụ sau đây cho thấy cách giải mã khoá AES và khoá EC công khai được mã hoá bằng DER để xác minh chữ ký từ Play Console gửi tới các khoá cho ngôn ngữ cụ thể (ở trường hợp này là ngôn ngữ lập trình Java) trong máy chủ phụ trợ của ứng dụng. Lưu ý rằng các khoá này được mã hoá dưới dạng base64 bằng cách sử dụng cờ mặc định.

Kotlin

// base64OfEncodedDecryptionKey is provided through Play Console.
var decryptionKeyBytes: ByteArray =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT)

// Deserialized encryption (symmetric) key.
var decryptionKey: SecretKey = SecretKeySpec(
    decryptionKeyBytes,
    /* offset= */ 0,
    AES_KEY_SIZE_BYTES,
    AES_KEY_TYPE
)

// base64OfEncodedVerificationKey is provided through Play Console.
var encodedVerificationKey: ByteArray =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT)

// Deserialized verification (public) key.
var verificationKey: PublicKey = KeyFactory.getInstance(EC_KEY_TYPE)
    .generatePublic(X509EncodedKeySpec(encodedVerificationKey))

Java


// base64OfEncodedDecryptionKey is provided through Play Console.
byte[] decryptionKeyBytes =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT);

// Deserialized encryption (symmetric) key.
SecretKey decryptionKey =
    new SecretKeySpec(
        decryptionKeyBytes,
        /* offset= */ 0,
        AES_KEY_SIZE_BYTES,
        AES_KEY_TYPE);

// base64OfEncodedVerificationKey is provided through Play Console.
byte[] encodedVerificationKey =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT);
// Deserialized verification (public) key.
PublicKey verificationKey =
    KeyFactory.getInstance(EC_KEY_TYPE)
        .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));

Tiếp theo, dùng các khoá này để giải mã mã thông báo tính toàn vẹn (phần JWE) trước, sau đó xác minh và trích xuất phần JWS lồng ghép.

Kotlin

val jwe: JsonWebEncryption =
    JsonWebStructure.fromCompactSerialization(integrityToken) as JsonWebEncryption
jwe.setKey(decryptionKey)

// This also decrypts the JWE token.
val compactJws: String = jwe.getPayload()

val jws: JsonWebSignature =
    JsonWebStructure.fromCompactSerialization(compactJws) as JsonWebSignature
jws.setKey(verificationKey)

// This also verifies the signature.
val payload: String = jws.getPayload()

Java

JsonWebEncryption jwe =
    (JsonWebEncryption)JsonWebStructure
        .fromCompactSerialization(integrityToken);
jwe.setKey(decryptionKey);

// This also decrypts the JWE token.
String compactJws = jwe.getPayload();

JsonWebSignature jws =
    (JsonWebSignature) JsonWebStructure.fromCompactSerialization(compactJws);
jws.setKey(verificationKey);

// This also verifies the signature.
String payload = jws.getPayload();

Tải trọng phát sinh là một mã thông báo dạng văn bản thuần tuý chứa các kết quả về tính toàn vẹn.