Bỏ qua để đến nội dung

Google Play App Signing

Ai làm Android lâu năm chắc đều từng nghe hoặc trải qua chuyện “mất keystore là mất app”: không thể phát hành bản cập nhật, phải đăng app mới với package name mới và xin người dùng cài lại. Play App Signing ra đời phần lớn là để giải quyết chuyện đó.

Mỗi APK cài lên thiết bị Android đều phải được ký số. Android dùng chữ ký để đảm bảo bản cập nhật đến từ đúng chủ app: bản cập nhật phải được ký bằng cùng key (hoặc key đã được xoay vòng hợp lệ) với bản đang cài, nếu không hệ thống sẽ từ chối.

Trước đây, developer tự giữ key này và tự ký APK trước khi upload. Với Play App Signing, Google giữ key ký app (app signing key) trong hạ tầng quản lý key của họ (Google Cloud KMS). Developer chỉ ký file upload bằng một key khác gọi là upload key. Luồng hoạt động:

Developer Google Play Thiết bị
───────── ─────────── ────────
Build .aab
Ký bằng UPLOAD KEY ──upload──▶ Kiểm tra chữ ký upload key
(xác minh đúng là bạn)
Tách .aab thành các APK tối ưu
Ký lại bằng APP SIGNING KEY ──▶ Cài đặt, kiểm tra chữ ký

Upload key chỉ dùng để Google xác minh người upload. Thứ người dùng thật sự nhận được là APK ký bằng app signing key do Google giữ.

Từ tháng 8/2021, app mới trên Google Play bắt buộc phải phát hành bằng Android App Bundle (.aab), đồng nghĩa với việc bắt buộc dùng Play App Signing, vì Google cần key ký app để tự sinh và ký APK từ bundle. App cũ (đăng trước thời điểm đó, vẫn tự ký APK) có thể chuyển sang Play App Signing bất kỳ lúc nào.

1. Mất upload key vẫn cứu được. Đây là lợi ích lớn nhất. Nếu bạn tự giữ app signing key và làm mất, app đó coi như không thể cập nhật nữa. Với Play App Signing, thứ bạn giữ chỉ là upload key, và mất upload key thì có thể yêu cầu Google reset (xem phần lưu ý bên dưới).

2. Key được bảo vệ tốt hơn. Thực tế nhiều team để keystore trong máy cá nhân, trong repo, gửi qua chat… App signing key nằm trong KMS của Google thì giảm hẳn rủi ro lộ key. Nếu upload key bị lộ, kẻ xấu cũng không thể tự ký bản cập nhật cài thẳng lên máy người dùng, vì họ không có app signing key.

3. Là điều kiện để dùng App Bundle. Vì Google tự sinh APK theo từng cấu hình thiết bị (ngôn ngữ, mật độ màn hình, ABI), Google phải là bên ký các APK đó. Nhờ vậy mới có các tính năng như APK nhỏ hơn cho từng thiết bị, Play Feature Delivery, Play Asset Delivery, automatic protection…

4. Có thể nâng cấp key. Google hỗ trợ đổi sang key mạnh hơn (key upgrade) khi key cũ yếu hoặc nghi bị lộ, dựa trên cơ chế xoay vòng key của APK Signature Scheme v3.1. Khi tự quản lý key, việc này rất khó làm.

5. Chuẩn bị cho thời kỳ máy tính lượng tử. Vì Google giữ key, họ có thể bổ sung chữ ký hậu lượng tử (post-quantum) cho app mà developer gần như không phải làm gì (xem phần quantum-ready key).

  • Ai giữ: developer.
  • Lưu ở đâu: file Java keystore (.jks hoặc .keystore).
  • Thuật toán: RSA, tối thiểu 2048-bit.
  • Dùng để: ký file .aab trước khi upload, để Google xác minh bạn là chủ app.
  • Mất thì sao: yêu cầu Google reset được.
  • Ai giữ: Google.
  • Thuật toán: key do Google sinh là RSA 4096-bit. Nếu bạn tự cung cấp key (ví dụ khi chuyển app cũ sang), key phải là RSA tối thiểu 2048-bit.
  • Dùng để: ký các APK thật sự được cài lên thiết bị.
  • Bạn có thể tải về: chứng chỉ công khai (public certificate) và các fingerprint SHA-1/SHA-256 trong Play Console, không tải được private key.

Google khuyến nghị upload key và app signing key phải khác nhau. Nếu dùng chung, lộ upload key cũng là lộ app signing key, mất ý nghĩa của mô hình hai key.

Đây là thay đổi mới đi kèm Android 17.

Bối cảnh: chữ ký RSA và EC đang dùng hiện nay về lý thuyết có thể bị máy tính lượng tử đủ mạnh phá được. Khi đó, kẻ tấn công có thể giả mạo chữ ký app và phát tán bản “cập nhật” độc hại mà thiết bị vẫn chấp nhận. Máy tính như vậy chưa tồn tại, nhưng chuyển đổi hạ tầng ký số mất nhiều năm nên Google bắt đầu từ bây giờ.

Cách Google làm: dùng chữ ký lai (hybrid), kết hợp một key cổ điển và một key hậu lượng tử:

  • Key cổ điển: RSA 4096-bit.
  • Key hậu lượng tử: ML-DSA-65. ML-DSA (Module-Lattice-Based Digital Signature Algorithm) là chuẩn chữ ký số hậu lượng tử của NIST (FIPS 204), số 65 chỉ mức tham số (mức bảo mật).

Android 17 thêm APK Signature Scheme v3.2 để chứa chữ ký lai này. Vì thiết bị cũ không hiểu v3.2, APK vẫn mang thêm khối chữ ký cổ điển như trước:

Thiết bịKiểm tra bằng
Android 17 trở lênChữ ký lai (APK Signature Scheme v3.2)
Android 16 trở xuốngKhối chữ ký cổ điển như trước giờ

Hệ quả: theo tài liệu Play Console, một app dùng quantum-ready signing có ba key:

  1. Key cổ điển dùng cho thiết bị Android 16 trở xuống.
  2. Key cổ điển RSA 4096-bit riêng cho chữ ký lai (Google sinh mới, không dùng lại key ở mục 1).
  3. Key ML-DSA-65 cho chữ ký lai.

Ai được dùng:

  • App mới: tự động được đăng ký quantum-ready signing với key do Google sinh, bất kể target SDK.
  • App đang có: có thể chọn tham gia (opt-in) thông qua nâng cấp key trong Play Console. Google cho biết về sau sẽ cho phép developer tự mang key cổ điển và ML-DSA của mình rồi uỷ quyền cho Play, và sẽ nhắc developer nâng cấp key định kỳ, ít nhất 2 năm một lần.

SHA-1/SHA-256 đăng ký với Firebase, Google Sign-In, Maps… phải là của app signing key

Phần tiêu đề “SHA-1/SHA-256 đăng ký với Firebase, Google Sign-In, Maps… phải là của app signing key”

Đây là lỗi kinh điển: chạy debug hoặc cài file tự build thì đăng nhập Google chạy ngon, lên bản tải từ Play Store thì lỗi (thường là ApiException: 10 / DEVELOPER_ERROR). Nguyên nhân là chỉ khai báo SHA-1 của debug key hoặc upload key, trong khi APK trên Play Store được ký bằng app signing key.

Cách làm: vào trang Play app signing của app trong Play Console (theo tài liệu hiện tại: Protected with Play → Play Store protection → Manage Play app signing; tên menu thỉnh thoảng được Google đổi), copy fingerprint ở mục App signing key certificate và thêm vào Firebase / Google Cloud Console / Facebook… Nên khai báo đủ cả ba loại: debug key, upload key (cho bản build nội bộ) và app signing key.

Với app dùng quantum-ready signing, Google yêu cầu đăng ký cả ba fingerprint với các nhà cung cấp API.

Android App Links xác minh app qua fingerprint SHA-256 trong file /.well-known/assetlinks.json trên domain. Fingerprint này cũng phải là của app signing key, không phải upload key. Mỗi lần nâng cấp key hay bật quantum-ready signing thì nhớ bổ sung fingerprint mới vào file này, nếu không link sẽ mở trình duyệt thay vì mở app.

  • Không commit keystore và mật khẩu lên repo. Với Flutter, file android/key.properties và file .jks nên nằm trong .gitignore; trên CI thì lưu keystore dạng base64 trong secret rồi giải mã lúc build.
  • Backup keystore và mật khẩu ở nơi an toàn mà cả team truy cập được (password manager của công ty), không phụ thuộc máy của một người.
  • Reset upload key được nhưng không tức thì: Google cần thời gian xử lý, và trong thời gian đó bạn không phát hành được bản mới. Hotfix gấp mà mất key thì rất mệt.

Cách reset upload key khi bị mất hoặc lộ:

Terminal window
# 1. Tạo upload key mới
keytool -genkeypair -v -keystore upload-keystore.jks -keyalg RSA \
-keysize 2048 -validity 10000 -alias upload
# 2. Xuất chứng chỉ ra file PEM
keytool -export -rfc -keystore upload-keystore.jks -alias upload \
-file upload_certificate.pem

Sau đó vào trang Play app signing, ở mục Upload key certificate chọn Request upload key reset, nhập lý do và upload file upload_certificate.pem.

Nếu app còn phát hành trên store khác (Huawei AppGallery, Samsung Galaxy Store, trang web riêng…), APK ở các nơi đó phải ký cùng key với bản trên Google Play, nếu không người dùng sẽ không cập nhật chéo được giữa các nguồn. Hai cách:

  • Tải APK universal đã được Google ký từ Play Console (App bundle explorer) để phân phối nơi khác.
  • Dùng app signing key của chính bạn thay vì để Google sinh: tự tạo key, rồi mã hoá và gửi cho Google bằng công cụ PEPK trong Play Console. Như vậy bạn vẫn giữ một bản app signing key để tự ký cho store khác (và phải tự bảo vệ bản đó).

Nên quyết định việc này trước khi phát hành bản đầu tiên. Với app mới, Play Console cho đổi app signing key trước khi phát hành open testing hoặc production; sau đó thì không đổi tuỳ ý được nữa.

Test bản ký bởi Google trước khi phát hành

Phần tiêu đề “Test bản ký bởi Google trước khi phát hành”

Bản bạn build local (ký bằng upload key) không giống hoàn toàn bản người dùng nhận. Để kiểm tra các tính năng phụ thuộc chữ ký (Google Sign-In, App Links, Play Integrity…) nên cài bản do Google ký qua Internal app sharing hoặc internal testing track.

Khi nâng cấp app signing key, mức độ áp dụng khác nhau theo phiên bản Android:

Phiên bảnCách áp dụng key mới
Android 17+Bắt buộc theo key quantum-ready mới (v3.2)
Android 13 đến 16Bắt buộc theo key cổ điển mới nhất (v3.1)
Android 7 đến 12Hệ điều hành không bắt buộc; Google Play Protect kiểm tra thay (nếu người dùng không tắt)

Nếu nhiều app của bạn dùng chung key để chia sẻ dữ liệu (custom permission với protectionLevel="signature", sharedUserId…), lưu ý trên Android 12 trở xuống các app này có thể chỉ nhận ra key cũ. Cần kiểm tra kỹ trước khi nâng cấp key cho một app trong nhóm.

Google tự thêm chữ ký v4 (dùng cho cài đặt tăng dần, incremental install, trên Android 11+) cho các app phù hợp. App dùng quantum-ready signing hiện không có v4 vì hai cơ chế chưa tương thích với nhau. Đa số app không bị ảnh hưởng gì, chỉ cần biết để không bất ngờ khi kiểm tra chữ ký APK.