Android Keystore là cơ chế hệ thống trong Android để lưu trữ an toàn các khóa mật mã trong cách ly phần cứng. Hệ thống sử dụng Trusted Execution Environment (TEE) trên các thiết bị có ARM TrustZone hoặc Secure Element chuyên dụng để bảo vệ khóa ở cấp độ chip. Theo Android Open Source Project, Keystore hỗ trợ các thuật toán RSA, EC, AES và HMAC với việc tạo khóa trực tiếp trong môi trường an toàn.
Những điểm chính
Android Keystore là nhà cung cấp mật mã được triển khai trong Android từ API 1 (Android 1.0), nhưng hỗ trợ phần cứng đầy đủ xuất hiện từ Android 4.3 (API 18). Keystore giải quyết vấn đề lưu trữ an toàn các khóa riêng tư để ngay cả khi hệ điều hành bị xâm phạm, kẻ tấn công không thể trích xuất khóa ở dạng văn bản thuần.
Kiến trúc Android Keystore bao gồm ba lớp: API ứng dụng (java.security.KeyStore), dịch vụ hệ thống (keystore daemon) và lớp phần cứng (Keymaster HAL). Ứng dụng truy cập qua API Java Cryptography Architecture (JCA) tiêu chuẩn, và dịch vụ hệ thống định tuyến yêu cầu đến Keymaster chạy trong TEE.
Tất cả các thao tác mật mã với khóa (ký, giải mã) được thực hiện bên trong TEE hoặc Secure Element. Các khóa không bao giờ rời khỏi môi trường an toàn — ứng dụng chỉ nhận được một handle (bí danh) để tham chiếu đến khóa. Đây là sự khác biệt cơ bản so với KeyStore phần mềm, nơi các khóa có thể truy cập trong bộ nhớ tiến trình.
JKS (Java KeyStore) tiêu chuẩn hoặc BKS (Bouncy Castle) lưu trữ khóa trong các tệp được bảo vệ bằng mật khẩu. Android Keystore lưu trữ khóa trong cách ly phần cứng, nơi chúng được bảo vệ ngay cả khỏi người dùng root. JKS dễ bị tổn thương khi truy cập trực tiếp vào hệ thống tệp; Android Keystore thì không.
Một điểm khác biệt nữa: trong Android Keystore, các khóa có các tham số sử dụng nghiêm ngặt (purpose — chỉ sign/verify/encrypt/decrypt) được chỉ định khi tạo. Chúng không thể thay đổi sau đó, ngăn chặn việc sử dụng sai khóa.
Khi tạo khóa mới, ứng dụng gọi KeyPairGenerator hoặc KeyGenerator với KeyGenParameterSpec, chứa tất cả các tham số của khóa tương lai. Hệ thống chuyển yêu cầu đến Keymaster HAL, tạo khóa bên trong TEE và trả về một handle.
Phương thức KeyGenParameterSpec.Builder chấp nhận các tham số bắt buộc: tên khóa trong Keystore, mục đích (PURPOSE_SIGN, PURPOSE_ENCRYPT), thuật toán (RSA, EC, AES). Bổ sung: digest (SHA-256), padding (PKCS7), userAuthenticationRequired (sinh trắc học), keyValidityStart/End (giới hạn thời gian).
Sau khi đặt tham số, KeyPairGenerator.generateKeyPair() trả về KeyPair, trong đó PrivateKey là đối tượng ủy thác thao tác cho Keymaster. Khóa công khai có thể được trích xuất, khóa riêng tư thì không. Nó chỉ tồn tại bên trong TEE.
import java.security.KeyPairGenerator
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
fun generateKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGN_PADDING_RSA_PKCS1)
.setUserAuthenticationRequired(true)
.build()
val kpGen = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
kpGen.initialize(spec)
kpGen.generateKeyPair()
}
Signature cho ECDSA hoặc RSA-PSS được tạo qua API tiêu chuẩn: Signature.getInstance(algorithm).initSign(privateKey). Thao tác ký được thực hiện trong TEE: ứng dụng gửi dữ liệu, Keymaster ký bằng phần cứng và trả về chữ ký. Khóa và dữ liệu không trộn lẫn trong bộ nhớ dùng chung.
Để bảo vệ sinh trắc học, người dùng phải xác thực qua BiometricPrompt trước khi ký. Nếu không xác thực thành công, Keymaster không thực hiện thao tác và trả về CryptoAuthenticationException.
import java.security.KeyStore
import java.security.Signature
import androidx.biometric.BiometricPrompt
fun signWithBiometric(alias: String) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val entry = ks.getEntry(alias, null) as KeyStore.PrivateKeyEntry
val signature = Signature.getInstance("SHA256withRSA")
signature.initSign(entry.privateKey)
// BiometricPrompt với CryptoObject(signature) yêu cầu FaceID/PIN
}
Android hỗ trợ hai chế độ lưu trữ khóa: phần mềm (trên thiết bị không có TEE) và phần cứng (trên thiết bị có TEE hoặc Secure Element). Chế độ phụ thuộc vào khả năng của SoC và phiên bản Android.
Trên thiết bị không có Trusted Execution Environment (trước Android 4.3 hoặc SoC giá rẻ), các khóa được lưu trữ mã hóa bằng khóa chủ derived từ mật khẩu màn hình khóa. Chế độ này kém an toàn hơn — các khóa có thể truy cập trong bộ nhớ tiến trình trong các thao tác mật mã.
Mức bảo vệ dựa trên mã hóa tệp KeyStore bằng AES-256-GCM. Khóa mã hóa được tạo từ mật khẩu hoặc mã PIN của người dùng qua Scrypt (PBKDF2 với số lần lặp cao).
Trên thiết bị hiện đại, Keymaster 4.x được sử dụng trong TEE (ARM TrustZone). Các khóa được tạo, lưu trữ và sử dụng độc quyền bên trong TrustZone. Ngay cả nhân Linux cũng không có quyền truy cập vào khóa riêng tư — chỉ Keymaster HAL mới có thể thực hiện thao tác.
Secure Element (ví dụ: eSE trong Samsung Knox hoặc StrongBox trong Google Pixel 3+) là chip riêng biệt với bộ xử lý và bộ nhớ riêng. Nó được chứng nhận Common Criteria EAL 4+ và cung cấp mức bảo vệ tối đa, bao gồm bảo vệ chống can thiệp vật lý.
| Loại | Vị trí lưu trữ | Mức bảo vệ | Có từ API |
|---|---|---|---|
| Phần mềm | Tệp /data/misc/keystore | Trung bình (AES-256) | API 1+ |
| Keymaster 3 | TEE (TrustZone) | Cao | API 23+ |
| Keymaster 4 | TEE + Secure I/O | Rất cao | API 28+ |
| StrongBox | Secure Element phần cứng | Tối đa | API 28+, tùy chọn |
Android Keystore được tích hợp vào Java Cryptography Architecture (JCA). Để truy cập nhà cung cấp, sử dụng KeyStore.getInstance("AndroidKeyStore"). API có sẵn từ API 18.
Phương thức KeyStore.load(null) tải vùng chứa KeyStore của ứng dụng. Không cần mật khẩu — Android sử dụng ngữ cảnh ứng dụng và UID của nó để kiểm soát truy cập. Mỗi ứng dụng chỉ thấy các mục nhập của riêng mình trừ khi sử dụng UID dùng chung.
Các phương thức setEntry và getEntry làm việc với KeyStore.PrivateKeyEntry, SecretKeyEntry hoặc TrustedCertificateEntry. Tham số ProtectionParameter luôn là null cho Android Keystore (bảo vệ được thực hiện ở cấp hệ thống).
import java.security.KeyStore
import java.security.cert.Certificate
import android.security.keystore.KeyProtection
fun storeSecretKey(alias: String, key: SecretKey) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val prot = KeyProtection.Builder(
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.build()
ks.setEntry(alias, KeyStore.SecretKeyEntry(key), prot)
}
Sử dụng KeyCharacteristics, bạn có thể xác định môi trường lưu trữ khóa: KeyStore phần mềm, TEE hoặc StrongBox. Phương thức getKeyCharacteristics() trả về một tập cờ: FLAG_HARDWARE (keymaster), FLAG_SECURE_ELEMENT (StrongBox), FLAG_TRUSTED_USER_PRESENCE_REQUIRED (sinh trắc học).
Android Keystore hỗ trợ nhiều loại thuật toán mật mã được chia thành ba loại: bất đối xứng, đối xứng và MAC. Hỗ trợ thuật toán cụ thể phụ thuộc vào phiên bản Keymaster HAL.
RSA (1024–4096 bit) — để ký (PKCS1, PSS) và mã hóa (OAEP, PKCS1). EC (P-224, P-256, P-384, P-521) — để ký ECDSA và thỏa thuận khóa ECDH. AES (128, 256 bit) — để mã hóa đối xứng ở chế độ CBC, CTR, GCM. HMAC (SHA1, SHA256, SHA512) — để xác thực tin nhắn.
Cho mỗi khóa, setPurposes được chỉ định để hạn chế các thao tác có thể. Khóa RSA với PURPOSE_SIGN không thể được sử dụng để mã hóa, ngay cả khi kẻ tấn công có quyền truy cập API. Đây là việc thực thi sử dụng khóa ở cấp phần cứng.
Keymaster bao gồm bộ đếm số lần xác thực sinh trắc học thất bại. Sau một số lần thất bại nhất định (có thể cấu hình qua setInvalidatedByBiometricEnrollment), khóa trở nên không khả dụng và cần xóa/tạo lại. Khi tất cả các mẫu sinh trắc học bị xóa, tất cả khóa có userAuthenticationRequired=true tự động bị vô hiệu hóa.
Key Attestation (Android 8.1+) cũng được hỗ trợ: theo yêu cầu của ứng dụng, Keymaster ký một chứng chỉ với thông tin về đặc tính của khóa (phần cứng/phần mềm, thuật toán, mục đích). Máy chủ có thể xác minh chứng chỉ này để xác nhận rằng khóa được tạo trong môi trường đáng tin cậy.
Câu hỏi thường gặp
Java KeyStore lưu trữ khóa trong tệp được bảo vệ bằng mật khẩu (JKS, BKS). Android Keystore sử dụng cách ly phần cứng qua TEE hoặc Secure Element. Java KeyStore dễ bị tổn thương với truy cập root; Android Keystore thì không, vì các khóa riêng tư không bao giờ rời khỏi môi trường an toàn.
Có, qua KeyStore.setEntry với KeyProtection. Tuy nhiên, khóa được nhập sẽ không có bảo vệ phần cứng — nó sẽ được lưu trữ trong Keystore phần mềm, được mã hóa bằng khóa chủ. Để bảo mật tối đa, hãy luôn tạo khóa bên trong Keystore.
Sử dụng KeyChain.isBoundKeyAlgorithm hoặc kiểm tra KeyCharacteristics sau khi tạo khóa. Sự hiện diện của FLAG_HARDWARE trong các đặc tính có nghĩa là khóa được tạo trong TEE. Bạn cũng có thể kiểm tra android.security.keystore.isHardwareBacked().
Khi gỡ cài đặt ứng dụng, Android xóa tất cả khóa của nó khỏi Keystore. Dữ liệu bị mất không thể khôi phục. Khi cài đặt lại, ứng dụng phải tạo khóa mới. Sao lưu khóa qua TEE là không thể về mặt kiến trúc.
Trên thiết bị bị khóa, Keymaster không thực hiện bất kỳ thao tác nào. Khóa có userAuthenticationRequired=true yêu cầu xác nhận sinh trắc học mỗi lần. Ngay cả với quyền truy cập root, kẻ tấn công không thể gọi Keymaster trực tiếp — chỉ thông qua dịch vụ Android Keystore.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm