Android Keystore هي آلية نظام في Android لتخزين المفاتيح التشفيرية بشكل آمن في عزلة الأجهزة. يستخدم النظام بيئة التنفيذ الموثوقة (TEE) على الأجهزة المزودة بـ ARM TrustZone أو عنصر آمن مخصص (Secure Element) لحماية المفاتيح على مستوى الشريحة. وفقًا لـ مشروع Android مفتوح المصدر، يدعم Keystore خوارزميات RSA و EC و AES و HMAC مع توليد المفاتيح مباشرة في البيئة الآمنة.
الملخص
Android Keystore هو مزود تشفير تم تنفيذه في Android بدءًا من API 1 (Android 1.0)، لكن الدعم الكامل للأجهزة ظهر مع Android 4.3 (API 18). يحل Keystore مشكلة التخزين الآمن للمفاتيح الخاصة بحيث حتى في حالة اختراق نظام التشغيل، لا يمكن للمهاجم استخراج المفاتيح كنص صريح.
تتكون هندسة Android Keystore من ثلاث طبقات: API التطبيق (java.security.KeyStore)، خدمة النظام (keystore daemon)، ومستوى الأجهزة (Keymaster HAL). يصل التطبيق عبر API Java Cryptography Architecture (JCA) القياسي، وتقوم خدمة النظام بتوجيه الطلبات إلى Keymaster الذي يعمل في TEE.
جميع العمليات التشفيرية مع المفاتيح (التوقيع، فك التشفير) تتم داخل TEE أو Secure Element. المفاتيح لا تغادر البيئة الآمنة أبدًا — يتلقى التطبيق فقط معرفًا (اسمًا مستعارًا) للإشارة إلى المفتاح. هذا فرق جوهري عن KeyStores البرمجية حيث المفاتيح قد تكون متاحة في ذاكرة العملية.
JKS (Java KeyStore) القياسي أو BKS (Bouncy Castle) يخزنان المفاتيح في ملفات محمية بكلمة مرور. Android Keystore يخزن المفاتيح في عزلة الأجهزة حيث تكون محمية حتى من المستخدم root. JKS عرضة للوصول المباشر لنظام الملفات؛ Android Keystore ليس كذلك.
فرق آخر: في Android Keystore، المفاتيح لها معايير استخدام صارمة (purpose — sign/verify/encrypt/decrypt فقط) تُحدد وقت التوليد. لا يمكن تغييرها لاحقًا، مما يمنع إساءة استخدام المفتاح.
عند إنشاء مفتاح جديد، يستدعي التطبيق KeyPairGenerator أو KeyGenerator مع KeyGenParameterSpec الذي يحتوي على جميع معلمات المفتاح المستقبلي. يقوم النظام بتمرير الطلب إلى Keymaster HAL، الذي يولد المفتاح داخل TEE ويعيد معرفًا.
تقبل طريقة KeyGenParameterSpec.Builder معلمات إلزامية: اسم المفتاح في Keystore، الغرض (PURPOSE_SIGN، PURPOSE_ENCRYPT)، الخوارزمية (RSA، EC، AES). بالإضافة إلى ذلك: digest (SHA-256)، padding (PKCS7)، userAuthenticationRequired (القياسات الحيوية)، keyValidityStart/End (قيود زمنية).
بعد تعيين المعلمات، يُرجع KeyPairGenerator.generateKeyPair() KeyPair، حيث PrivateKey هو كائن يفوض العمليات إلى Keymaster. يمكن استخراج المفتاح العام، أما المفتاح الخاص فلا. إنه موجود فقط داخل 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 لـ ECDSA أو RSA-PSS عبر API القياسي: Signature.getInstance(algorithm).initSign(privateKey). تتم عملية التوقيع في TEE: يمرر التطبيق البيانات، يوقعها Keymaster في الأجهزة ويعيد التوقيع. المفتاح والبيانات لا يختلطان في الذاكرة المشتركة.
للحماية بالقياسات الحيوية، يجب مصادقة المستخدم عبر BiometricPrompt قبل التوقيع. بدون مصادقة ناجحة، لا ينفذ Keymaster العملية ويعيد 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 مع CryptoObject(signature) يطلب FaceID/PIN
}
يدعم Android وضعين لتخزين المفاتيح: برمجي (على الأجهزة بدون TEE) و جهازي (على الأجهزة المزودة بـ TEE أو Secure Element). يعتمد الوضع على قدرات SoC وإصدار Android.
على الأجهزة بدون بيئة التنفيذ الموثوقة (قبل Android 4.3 أو SoCs منخفضة التكلفة)، تُخزن المفاتيح مشفرة باستخدام مفتاح رئيسي مشتق من كلمة مرور شاشة القفل. هذا الوضع أقل أمانًا — المفاتيح متاحة في ذاكرة العملية أثناء العمليات التشفيرية.
مستوى الحماية يعتمد على تشفير ملف KeyStore باستخدام AES-256-GCM. يتم توليد مفتاح التشفير من كلمة مرور المستخدم أو PIN عبر Scrypt (PBKDF2 مع عدد كبير من التكرارات).
على الأجهزة الحديثة، يُستخدم Keymaster 4.x في TEE (ARM TrustZone). يتم توليد المفاتيح وتخزينها واستخدامها حصريًا داخل TrustZone. حتى نواة Linux لا تملك وصولاً إلى المفاتيح الخاصة — فقط Keymaster HAL يمكنه تنفيذ العمليات.
Secure Element (مثل eSE في Samsung Knox أو StrongBox في Google Pixel 3+) هي شريحة منفصلة بمعالج وذاكرة خاصين بها. وهي معتمدة من Common Criteria EAL 4+ وتوفر أقصى مستوى من الحماية، بما في ذلك الحماية ضد العبث المادي.
| النوع | موقع التخزين | مستوى الحماية | متاح منذ API |
|---|---|---|---|
| برمجي | ملف /data/misc/keystore | متوسط (AES-256) | API 1+ |
| Keymaster 3 | TEE (TrustZone) | عالي | API 23+ |
| Keymaster 4 | TEE + Secure I/O | عالي جدًا | API 28+ |
| StrongBox | Secure Element جهازي | أقصى | API 28+، اختياري |
Android Keystore مدمج في Java Cryptography Architecture (JCA). للوصول إلى المزود، يُستخدم KeyStore.getInstance("AndroidKeyStore"). API متاح بدءًا من API 18.
طريقة KeyStore.load(null) تحمل حاوية KeyStore للتطبيق. ليست هناك حاجة لكلمة مرور — يستخدم Android سياق التطبيق و UID للتحكم في الوصول. كل تطبيق يرى فقط إدخالاته الخاصة ما لم يتم استخدام UID مشترك.
طريقتا setEntry و getEntry تعملان مع KeyStore.PrivateKeyEntry و SecretKeyEntry و TrustedCertificateEntry. معامل ProtectionParameter دائمًا null لـ Android Keystore (الحماية منفذة على مستوى النظام).
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)
}
باستخدام KeyCharacteristics، يمكن تحديد البيئة التي يُخزن فيها المفتاح: KeyStore برمجي، TEE، أو StrongBox. تُرجع طريقة getKeyCharacteristics() مجموعة من الأعلام: FLAG_HARDWARE (keymaster)، FLAG_SECURE_ELEMENT (StrongBox)، FLAG_TRUSTED_USER_PRESENCE_REQUIRED (القياسات الحيوية).
يدعم Android Keystore مجموعة واسعة من الخوارزميات التشفيرية مقسمة إلى ثلاث فئات: غير متماثلة، متماثلة، و MAC. دعم الخوارزميات المحددة يعتمد على إصدار Keymaster HAL.
RSA (1024–4096 بت) — للتوقيع (PKCS1، PSS) والتشفير (OAEP، PKCS1). EC (P-224، P-256، P-384، P-521) — لتوقيع ECDSA واتفاق المفاتيح ECDH. AES (128، 256 بت) — للتشفير المتماثل في أوضاع CBC، CTR، GCM. HMAC (SHA1، SHA256، SHA512) — لمصادقة الرسائل.
لكل مفتاح، يُحدد setPurposes لتقييد العمليات الممكنة. مفتاح RSA مع PURPOSE_SIGN لا يمكن استخدامه للتشفير، حتى لو كان المهاجم لديه وصول إلى API. هذا تطبيق لاستخدام المفتاح على مستوى الأجهزة.
يتضمن Keymaster عدادًا لمحاولات المصادقة البيومترية الفاشلة. بعد عدد محدد من حالات الفشل (قابل للتكوين عبر setInvalidatedByBiometricEnrollment)، يصبح المفتاح غير متاح ويتطلب الحذف/إعادة التوليد. عند إزالة جميع القوالب البيومترية، يتم إبطال جميع المفاتيح ذات userAuthenticationRequired=true تلقائيًا.
أيضًا، Key Attestation (Android 8.1+) مدعوم: بناءً على طلب التطبيق، يوقع Keymaster شهادة بمعلومات حول خصائص المفتاح (جهازي/برمجي، الخوارزمية، الأغراض). يمكن للخادم التحقق من هذه الشهادة لتأكيد أن المفتاح تم إنشاؤه في بيئة موثوقة.
الأسئلة الشائعة
Java KeyStore يخزن المفاتيح في ملف محمي بكلمة مرور (JKS، BKS). Android Keystore يستخدم عزلة الأجهزة عبر TEE أو Secure Element. Java KeyStore عرضة للوصول الجذر؛ Android Keystore ليس كذلك، لأن المفاتيح الخاصة لا تغادر البيئة الآمنة أبدًا.
نعم، عبر KeyStore.setEntry مع KeyProtection. لكن المفتاح المستورد لن يكون له حماية الأجهزة — سيتم تخزينه في Keystore البرمجي، مشفرًا بمفتاح رئيسي. لأقصى أمان، قم دائمًا بتوليد المفاتيح داخل Keystore.
استخدم KeyChain.isBoundKeyAlgorithm أو تحقق من KeyCharacteristics بعد توليد المفتاح. وجود FLAG_HARDWARE في الخصائص يعني أن المفتاح تم إنشاؤه في TEE. يمكنك أيضًا التحقق من android.security.keystore.isHardwareBacked().
عند إلغاء تثبيت التطبيق، يزيل Android جميع مفاتيحه من Keystore. يتم فقدان البيانات بشكل لا رجعة فيه. عند إعادة التثبيت، يجب على التطبيق توليد مفاتيح جديدة. النسخ الاحتياطي للمفاتيح عبر TEE مستحيل لأسباب معمارية.
على الجهاز المقفل، لا ينفذ Keymaster أي عمليات. المفاتيح ذات userAuthenticationRequired=true تتطلب تأكيدًا بيومتريًا في كل مرة. حتى مع الوصول الجذر، لا يمكن للمهاجم استدعاء Keymaster مباشرة — فقط من خلال خدمة Android Keystore.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا