KeyStore (Android) هو تطبيق للمزوّد المشفر لهندسة Java Cryptography Architecture (JCA) المدمج في Android للتخزين الآمن للمفاتيح مع إمكانية العزل عبر الأجهزة. منذ Android 4.3 (API 18)، يدعم KeyStore المفاتيح العتادية عبر Keymaster HAL، وابتداءً من Android 9 (API 28) يدعم StrongBox Keymaster للمفاتيح في العنصر الآمن المخصص. وفقًا لـ توثيق أمان Android، فإن المزوّد «AndroidKeyStore» يحل محل Bouncy Castle أو OpenSSL KeyStore القياسي، مما يوفر حماية على مستوى النظام ضد الاستخراج غير المصرح به للمفاتيح.
الملامح الرئيسية
KeyStore في Android ليس تطبيقًا أو ملفًا منفصلاً، بل هو مزوّد مشفر يطبق واجهة java.security.KeyStore. يوفر API موحدة لتخزين واستخدام المفاتيح الخاصة والمفاتيح المتماثلة وشهادات CA الموثوقة. يتم تسجيل المزوّد باسم «AndroidKeyStore» ويمكن الوصول إليه عبر KeyStore.getInstance() القياسي.
قبل Android 4.3، كانت العمليات المشفّرة تُنفذ عبر Bouncy Castle. قدم Android 4.3 Keymaster HAL 1.0، مما أتاح استخدام TEE على ARM TrustZone. أضاف Android 6.0 (API 23) Keymaster 2.0 مع المصادقة البيومترية العتادية. قدم Android 9 (API 28) Keymaster 4.0 وStrongBox Keymaster للعنصر الآمن المخصص.
كل إصدار من Keymaster يضيف قدرات جديدة ويحسن عزل المفاتيح. الأجهزة الحديثة (2022+) يجب أن تدعم Keymaster 4.0 للحصول على شهادة Google Mobile Services، مما يضمن توفر TEE لجميع تطبيقات Android.
يتكون Android KeyStore من ثلاث طبقات: Java API (KeyStore, KeyPairGenerator)، عملية النظام keystore (C++، تعمل كخدمة نظام)، وKeymaster HAL (مكتبة في TEE أو Secure Element). يستدعي التطبيق API، ويوجّه خدمة keystore الطلب إلى Keymaster، ويتم تنفيذ العملية في البيئة الآمنة.
جميع المفاتيح الخاصة تُخزّن في TEE ولا يمكن قراءتها من مساحة المستخدم. حتى خدمة keystore للنظام لا تملك الوصول إلى المفاتيح الخام — فقط إلى المقابض التي تشير إلى المفاتيح داخل Keymaster.
يطبق Android KeyStore واجهة مزوّد الخدمة القياسية JCA. عندما يستدعي التطبيق Cipher.getInstance(«RSA/ECB/PKCS1Padding», «AndroidKeyStore»)، يفوض مزوّد أمان Android العملية إلى Keymaster عبر السلسلة: Java → JNI → خدمة keystore → Keymaster HAL.
يتم تسجيل المزوّد AndroidKeyStore تلقائيًا عند بدء العملية. أولويته أعلى من Bouncy Castle أو Conscrypt. لذلك عند استدعاء KeyStore.getInstance() دون تحديد مزوّد، يتم إرجاع AndroidKeyStore في معظم الحالات. للاستدعاء الصريح، استخدم KeyStore.getInstance(«AndroidKeyStore»).
كل تطبيق Android لديه حاوية معزولة في KeyStore. التطبيقات التي لها نفس UID (shared userId) يمكنها مشاركة الوصول إلى مفاتيح معينة، لكن الإعداد القياسي يضمن أن التطبيق A لا يمكنه قراءة مفاتيح التطبيق B.
load(null) — تهيئة KeyStore. المعلمة دائمًا null لـ AndroidKeyStore. setEntry — يحفظ مفتاحًا مع KeyProtection المحددة (الأغراض، digest، padding). getEntry — يسترجع KeyStore.PrivateKeyEntry أو SecretKeyEntry أو TrustedCertificateEntry. containsAlias — يتحقق من وجود المفتاح. deleteEntry — يحذف المفتاح بشكل دائم.
import java.security.KeyStore
import java.security.KeyPairGenerator
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
object KeyStoreManager {
private val keyStore by lazy {
KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
}
fun createRsaKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).setKeySize(2048)
.setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGN_PADDING_RSA_PKCS1)
.build()
val kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
kpg.initialize(spec)
kpg.generateKeyPair()
}
}
يدعم Android KeyStore مجموعة واسعة من الخوارزميات المشفرة، تختلف حسب إصدار Keymaster HAL على الجهاز. يمكن للمطور الحصول على قائمة الخوارزميات المدعومة عبر KeyGenParameterSpec.Builder عند محاولة الإنشاء — المعلمات غير المتوافقة ترمي InvalidAlgorithmParameterException.
RSA (1024–4096 بت) — للتوقيع (PKCS1, PSS مع SHA-1/SHA-256/SHA-384/SHA-512) والتشفير (OAEP مع SHA-1/SHA-256). EC (P-224, P-256, P-384, P-521) — لتوقيع ECDSA واتفاق المفاتيح ECDH. X25519 وEd25519 — منذ Android 12 (API 31) للبروتوكولات المشفرة الحديثة.
للمفاتيح غير المتماثلة، أنشئ دائمًا داخل Keymaster، ولا تستورد أبدًا المفاتيح الخاصة. المفاتيح الخاصة المستوردة ليست محمية عتاديًا — يتم تخزينها في طبقة البرمجيات وتكون عرضة للخطر عند اختراق عملية التطبيق.
AES (128, 256 بت) — للتشفير المتماثل في أوضاع CBC, CTR, GCM. HMAC (SHA-1, SHA-256, SHA-512) — لمصادقة الرسائل. ChaCha20 (Android 12+) — لتشفير التدفق عالي الأداء مع مصادقة Poly1305.
| الخوارزمية | Keymaster | الغرض | API |
|---|---|---|---|
| RSA | KM 1.0+ | التوقيع، التشفير | 18+ |
| EC | KM 1.0+ | ECDSA, ECDH | 18+ |
| AES | KM 2.0+ | التشفير المتماثل | 23+ |
| HMAC | KM 2.0+ | رمز المصادقة | 23+ |
| ChaCha20 | KM 3.0+ | تشفير التدفق | 31+ |
| X25519/Ed25519 | KM 3.0+ | تبادل المفاتيح | 31+ |
KeyStore.PrivateKeyEntry — يحتوي على مفتاح خاص (غير قابل للتصدير) وسلسلة شهادات. KeyStore.SecretKeyEntry — للمفاتيح المتماثلة. KeyStore.TrustedCertificateEntry — لشهادات CA الموثوقة. يمكن تصدير المفاتيح العامة عبر keyStore.getCertificate(alias).publicKey.
لننظر إلى سيناريو كامل: إنشاء مفتاح AES لتشفير البيانات وإنشاء مفتاح EC للتوقيع مع حماية بيومترية. يتم إنشاء كلا المفتاحين داخل Android KeyStore مع دعم عتادي.
يتم إنشاء مفتاح AES عبر KeyGenerator مع KeyGenParameterSpec. المعلمات: PURPOSE_ENCRYPT + PURPOSE_DECRYPT, BLOCK_MODE_GCM (الوضع الموصى به مع المصادقة), ENCRYPTION_PADDING_NONE (لا حاجة للpadding لـ GCM).
import javax.crypto.KeyGenerator
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
fun generateAndEncrypt(alias: String, plainText: ByteArray): ByteArray {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(256)
.build()
val kg = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
kg.initialize(spec)
kg.generateKey()
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, getKeyFromStore(alias))
return cipher.doFinal(plainText)
}
مفتاح EC مع userAuthenticationRequired=true يتطلب مصادقة المستخدم قبل كل عملية توقيع. يُستخدم BiometricPrompt مع CryptoObject الذي يحتوي على كائن Signature لهذا الغرض. بعد التحقق البيومتري الناجح، يسمح Keymaster بالعملية.
fun createBiometricSignKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN
).setAlgorithmParameterSpec(
ECGenParameterSpec("secp256r1")
).setDigests(KeyProperties.DIGEST_SHA256)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true)
.build()
val kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC,
"AndroidKeyStore"
)
kpg.initialize(spec)
kpg.generateKeyPair()
}
يوفر Android KeyStore ضمانات أمان على مستوى العتاد لا تستطيع KeyStore البرمجية (JKS, BKS) توفيرها. المفاتيح محمية على مستوى SoC، وحتى السيطرة الكاملة على مساحة مستخدم Android لا تسمح باستخراج المفتاح الخاص.
Key Attestation هي آلية تسمح للتطبيق (والخادم) بالتحقق من البيئة التي تم إنشاء المفتاح فيها. يوقع Android Keystore شهادة تحتوي على قائمة خصائص المفتاح: الخوارزمية، الحجم، الأغراض، العتادي (True/False)، المنشأ (GENERATED, IMPORTED). يتحقق الخادم من سلسلة الشهادات حتى الشهادة الجذرية لـ Google.
هذا أمر بالغ الأهمية للتطبيقات المالية: يمكن للخادم أن يطلب أن يكون المفتاح قد تم إنشاؤه في بيئة عتادية (Hardware-Backed = True) ويرفض المفاتيح المنشأة في Keystore برمجي. تمنع Key Attestation الهجمات التي يستبدل فيها المهاجم Keystore بمحاكي.
setInvalidatedByBiometricEnrollment(true) يعني أن Keymaster سيحذف المفتاح تلقائيًا عند تغيير أو إزالة القوالب البيومترية للمستخدم. هذه حماية من الهجمات التي يضيف فيها المهاجم بصمة إصبعه إلى حساب موجود. بعد إضافة بصمة جديدة، تصبح المفاتيح القديمة غير قابلة للوصول.
عداد محاولات المصادقة البيومترية الفاشلة يُدار أيضًا بواسطة Keymaster. بعد maxBiometricAttempt (قابل للتعديل من قبل الشركة المصنعة، عادةً 5)، يحجب Keymaster جميع العمليات باستخدام المفاتيح البيومترية لمدة 30 ثانية. بعد 10 محاولات فاشلة — حتى إدخال كلمة مرور الجهاز (PIN السري).
الأسئلة الشائعة
Bouncy Castle (BKS) هو KeyStore برمجي يخزن المفاتيح في ملف محمي بكلمة مرور. Android KeyStore يستخدم العزل العتادي TEE/StrongBox. يمكن استخراج مفاتيح BKS مع الوصول الجذري، أما مفاتيح Android KeyStore فلا يمكن. BKS مناسب لشهادات CA، Android KeyStore للمفاتيح الخاصة.
نعم، إذا حددت PURPOSE_ENCRYPT أو PURPOSE_DECRYPT أو PURPOSE_SIGN أو PURPOSE_VERIFY عند الإنشاء. لكن أفضل الممارسات هي إنشاء مفاتيح منفصلة لعمليات مختلفة. هذا يحد من الضرر إذا تم اختراق أحد المفاتيح ويتوافق مع مبدأ الامتياز الأقل.
استخدم KeyStore.getKeyCharacteristics(alias) المتاح عبر android.security.keystore. تعيد الطريقة مجموعة من الأعلام: FLAG_HARDWARE — مفتاح في TEE، FLAG_SECURE_ELEMENT — مفتاح في StrongBox. إذا لم تكن هناك أعلام، فالمفتاح برمجي فقط.
جميع المفاتيح التي تم إنشاؤها مع setInvalidatedByBiometricEnrollment(true) سيتم إبطالها تلقائيًا بواسطة Keymaster. عند محاولة استخدامها، سيتلقى التطبيق KeyPermanentlyInvalidatedException. البيانات المشفرة بهذه المفاتيح ستفقد بشكل دائم.
المفاتيح العتادية (في TEE/StrongBox) لا تدعم النسخ الاحتياطي — فهي مرتبطة بجهاز معين. المفاتيح البرمجية يمكن تضمينها في نسخة Google Drive الاحتياطية. لنقل البيانات بين الأجهزة، قم بتشفير البيانات على الخادم وفك تشفيرها على الجهاز الجديد.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا