يتيح لك نظام "متجر مفاتيح Android" تخزين مفاتيح التشفير في حاوية لكي يصعب استخراجها من الجهاز. بعد إضافة المفاتيح إلى ملف تخزين مفاتيح التشفير، يمكنك استخدامها لإجراء عمليات التشفير، مع إبقاء مادة المفتاح غير قابلة للتصدير. يتيح لك نظام تخزين المفاتيح أيضًا فرض قيود على استخدام المفاتيح وكيفية استخدامها، مثل طلب مصادقة المستخدم لاستخدام المفتاح أو حصر استخدام المفاتيح في أوضاع تشفير معيّنة فقط. راجِع القسم ميزات الأمان للحصول على مزيد من المعلومات.
يستخدم نظام "متجر المفاتيح" واجهة برمجة التطبيقات
KeyChain
التي تم طرحها في Android 4.0 (المستوى 14 لواجهة برمجة التطبيقات)، بالإضافة إلى ميزة موفِّر "متجر مفاتيح Android" التي تم طرحها في Android 4.3 (المستوى 18 لواجهة برمجة التطبيقات). يتناول هذا المستند
حالات استخدام نظام "متجر مفاتيح Android" وكيفية استخدامه.
ميزات الأمان
يحمي نظام "ملف تخزين مفاتيح Android" مواد المفاتيح من الاستخدام غير المصرَّح به بطريقتَين. أولاً، يقلل ذلك من خطر الاستخدام غير المصرَّح به لمادة المفتاح من خارج جهاز Android من خلال منع استخراج مادة المفتاح من عمليات التطبيق ومن جهاز Android ككل. ثانيًا، يقلل نظام "متجر المفاتيح" من خطر الاستخدام غير المصرَّح به لمواد المفاتيح داخل جهاز Android من خلال جعل التطبيقات تحديد الاستخدامات المصرَّح بها لمفاتيحها ثم فرض هذه القيود خارج عمليات التطبيقات.
منع الاستخراج
تتم حماية مادة مفاتيح "متجر مفاتيح Android" من الاستخراج باستخدام تدبيرَين للأمان:
- لا يتم إدخال المادة الرئيسية مطلقًا في عملية تقديم الطلب. عندما يُجري تطبيق عمليات التشفير باستخدام مفتاح "متجر مفاتيح Android"، يتمّ من وراء الكواليس إرسال النص العادي والنص المشفَّر والرسائل التي يجب توقيعها أو التحقّق منها إلى عملية نظام تنفِّذ العمليات التشفيرية. في حال اختراق عملية التطبيق، قد يتمكّن المهاجم من استخدام مفاتيح التطبيق ولكن لا يمكنه استخراج مادة المفتاح (على سبيل المثال، لاستخدامها خارج جهاز Android).
- يمكن ربط مادة المفتاح بالأجهزة الآمنة لجهاز Android، مثل
بيئة التنفيذ الموثوق (TEE) أو
العنصر الآمن (SE). عند تفعيل هذه الميزة لمفتاح، لا يتم أبدًا عرض مادة مفتاح الصعوبة خارج الأجهزة الآمنة. إذا تم اختراق نظام التشغيل Android أو تمكّن مهاجم من قراءة مساحة التخزين الداخلية للجهاز، قد يتمكّن المهاجم من استخدام مفاتيح "متجر مفاتيح Android" لأي تطبيق على جهاز Android، ولكن لا يمكنه استخراجها من الجهاز. لا يتم تفعيل هذه الميزة
إلا إذا كان الجهاز الآمن يتيح استخدام مجموعة معيّنة من خوارزمية المفتاح وأوضاع
الحظر ومخططات الحشو والملخّصات التي يُسمح باستخدام المفتاح معها.
للتحقّق مما إذا كانت الميزة مفعّلة لمفتاح معيّن، احصل على
KeyInfo
للمفتاح. تعتمد الخطوة التالية على إصدار حزمة SDK المستهدَف لتطبيقك:- إذا كان تطبيقك يستهدف الإصدار 10 من نظام التشغيل Android (المستوى 29 من واجهة برمجة التطبيقات) أو إصدارًا أحدث، تحقَّق من قيمة
getSecurityLevel()
المعروضة. تشير القيم المعروضة التي تتطابق معKeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT
أوKeyProperties.SecurityLevelEnum.STRONGBOX
إلى أنّ المفتاح مخزّن في جهاز آمن. - إذا كان تطبيقك يستهدف Android 9 (المستوى 28 من واجهة برمجة التطبيقات) أو إصدارًا أقدم، تحقّق من قيمة الإرجاع المَنطقي
KeyInfo.isInsideSecurityHardware()
.
- إذا كان تطبيقك يستهدف الإصدار 10 من نظام التشغيل Android (المستوى 29 من واجهة برمجة التطبيقات) أو إصدارًا أحدث، تحقَّق من قيمة
وحدة أمان الأجهزة
يمكن أن تتضمّن الأجهزة المتوافقة التي تعمل بالإصدار 9 من نظام التشغيل Android (المستوى 28 من واجهة برمجة التطبيقات) أو إصدار أحدث StrongBox Keymaster، وهو عبارة عن تنفيذ لواجهة برمجة التطبيقات Keymaster أو Keymint HAL يقع في عنصر آمن يشبه وحدة أمان الأجهزة. على الرغم من أنّ وحدات أمان الأجهزة يمكن أن تشير إلى العديد من عمليات التنفيذ المختلفة لتخزين المفاتيح التي لا يمكن أن تكشف عنها عملية اختراق لنظام التشغيل Linux، مثل TEE، يشير StrongBox بشكل صريح إلى الأجهزة، مثل العناصر الآمنة المضمّنة (eSE) أو وحدات المعالجة الآمنة (iSE) على شريحة المعالجة المُدمَجة (SoC).
تتضمّن الوحدة ما يلي:
- وحدة المعالجة المركزية الخاصة به
- مساحة تخزين آمنة
- أداة إنشاء أرقام عشوائية حقيقية
- آليات إضافية لمنع التلاعب بالحِزم وتحميل التطبيقات من مصدر غير معروف
- موقّت آمن
- دبوس إشعار إعادة التشغيل (أو ما يعادله)، مثل الإدخال/الإخراج العام (GPIO)
لدعم عمليات تنفيذ StrongBox المنخفضة الطاقة، تتوفّر مجموعة فرعية من الخوارزميات وحجم المفتاحَين:
- RSA 2048
- AES 128 و256
- ECDSA وECDH P-256
- HMAC-SHA256 (تتيح أحجام مفاتيح التشفير بين 8 و64 بايت)
- Triple DES
- رسائل APDU ذات الطول الموسّع
- مصادقة المفتاح
- إتاحة التعديل "ح" للترقية
عند إنشاء مفاتيح أو استيرادها باستخدام فئة KeyStore
، يمكنك تحديد الإعداد المفضّل لتخزين المفتاح في StrongBox Keymaster
من خلال تمرير true
إلى طريقة setIsStrongBoxBacked()
.
على الرغم من أنّ تقنية StrongBox أبطأ قليلاً وإمكانية الوصول إلى مواردها محدودة (أي أنّها تتيح عددًا أقل من العمليات المتزامنة) مقارنةً بتقنية TEE، إلا أنّ تقنية StrongBox تقدّم ضمانات أمان أفضل ضد الهجمات المادية وهجمات قناة جانبية. إذا كنت تريد منح الأولوية لضمانات أمان أعلى على كفاءة موارد التطبيق، ننصحك باستخدام StrongBox على الأجهزة التي يتوفّر فيها. في حال عدم توفّر StrongBox، يمكن لتطبيقك الاعتماد على TEE لتخزين مواد المفاتيح.
تفويضات استخدام المفاتيح
لتجنُّب الاستخدام غير المصرَّح به للمفاتيح على جهاز Android، يتيح "مفتاح تشفير Android" للتطبيقات تحديد الاستخدامات المصرَّح بها لمفاتيحها عند إنشائها أو استيرادها. بعد إنشاء مفتاح أو استيراده، لا يمكن تغيير أذوناته. بعد ذلك، يفرض "متجر مفاتيح Android" عمليات التفويض عند استخدام المفتاح. هذه ميزة أمان متقدّمة مفيدة بشكل عام، ولكن ليس إلا إذا كانت متطلباتك هي أنّ اختراق عملية تقديم طلبك بعد توليد/استيراد المفتاح (وليس قبله أو أثناءه) لا يمكن أن يؤدي إلى استخدامات غير مصرّح بها للمفتاح.
تندرج أذونات استخدام المفاتيح المسموح بها ضمن الفئات التالية:
- التشفير: لا يمكن استخدام المفتاح إلا مع خوارزميات المفاتيح أو العمليات أو الأغراض المعتمَدة (التشفير أو فك التشفير أو التوقيع أو التحقّق) أو مخططات الحشو أو أوضاع الكتل أو ملفات الترميز.
- فترة الصلاحية الزمنية: لا يُسمح باستخدام المفتاح إلا خلال فترة زمنية محدّدة.
- مصادقة المستخدم: لا يمكن استخدام المفتاح إلا إذا تم مصادقة المستخدم مؤخرًا. راجِع طلب مصادقة المستخدم لاستخدام المفتاح.
كإجراء أمان إضافي للمفاتيح التي تكون مادة المفتاح فيها داخل جهاز آمن (راجِع
KeyInfo.isInsideSecurityHardware()
أو، بالنسبة إلى التطبيقات التي تستهدف الإصدار 10 من نظام التشغيل Android (المستوى 29 من واجهة برمجة التطبيقات) أو إصدارًا أحدث،
KeyInfo.getSecurityLevel()
)،
قد تفرض الأجهزة الآمنة بعض أذونات استخدام المفتاح،
استنادًا إلى جهاز Android.
تفرض الأجهزة الآمنة عادةً أذونات التشفير ومصادقة المستخدمين. ومع ذلك، لا تفرض الأجهزة الآمنة عادةً أذونات فاصل الصلاحية الزمني، لأنّها لا تحتوي عادةً على
ساعة مستقلة وآمنة في الوقت الفعلي.
يمكنك الاستعلام عمّا إذا كان جهاز الأمان يفرض إذن مصادقة المستخدم للمفتاح باستخدام KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()
.
اختَر بين سلسلة مفاتيح وموفِّر متجر مفاتيح Android.
استخدِم واجهة برمجة التطبيقات KeyChain
عندما تريد
بيانات اعتماد على مستوى النظام. عندما يطلب أحد التطبيقات استخدام أي بيانات اعتماد
من خلال واجهة برمجة التطبيقات KeyChain
، يمكن للمستخدمين
اختيار بيانات الاعتماد المثبَّتة التي يمكن للتطبيق الوصول إليها
من خلال واجهة مستخدم يوفّرها النظام. ويسمح ذلك لعدة تطبيقات باستخدام
مجموعة بيانات الاعتماد نفسها بعد الحصول على موافقة المستخدم.
استخدِم مقدّم خدمة "متجر مفاتيح Android" للسماح لتطبيق فردي بتخزين بيانات اعتماده الخاصة، والتي لا يمكن لأحد غير هذا التطبيق الوصول إليها.
يقدّم ذلك للتطبيقات طريقة لإدارة بيانات الاعتماد التي يمكنها وحدها استخدامها، مع توفير مزايا الأمان نفسها التي تقدّمها واجهة برمجة التطبيقات
KeyChain
لبيانات الاعتماد على مستوى النظام.
لا تتطلب هذه الطريقة من المستخدم اختيار بيانات الاعتماد.
استخدام موفِّر "مفتاح Android"
لاستخدام هذه الميزة، استخدِم فئتَي KeyStore
وKeyPairGenerator
أو
KeyGenerator
العاديتَين مع موفِّر
AndroidKeyStore
الذي تم تقديمه في Android 4.3 (المستوى 18 من واجهة برمجة التطبيقات).
تم تسجيل AndroidKeyStore
كنوع KeyStore
لاستخدامه مع طريقة KeyStore.getInstance(type)
وكمقدّم خدمة لاستخدامه مع الطريقتَين KeyPairGenerator.getInstance(algorithm, provider)
وKeyGenerator.getInstance(algorithm, provider)
.
بما أنّ العمليات التشفيرية قد تستغرق وقتًا طويلاً، يجب أن تتجنّب التطبيقات استخدام
AndroidKeyStore
في سلسلة التعليمات الرئيسية لضمان بقاء واجهة مستخدم التطبيق
مستجيبة. (يمكن أن يساعدك تطبيق StrictMode
في العثور على أماكن لا ينطبق فيها ذلك).
إنشاء مفتاح خاص أو سري جديد
لإنشاء KeyPair
جديد يحتوي على
PrivateKey
،
يجب تحديد سمات X.509 الأولية للشهادة. يمكنك استخدام
KeyStore.setKeyEntry()
لاستبدال الشهادة في وقت لاحق بشهادة موقَّعة
من قِبل هيئة إصدار الشهادات (CA).
لإنشاء مفتاحَي التشفير، استخدِم KeyPairGenerator
مع KeyGenParameterSpec
:
Kotlin
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ val kpg: KeyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore" ) val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY ).run { setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) build() } kpg.initialize(parameterSpec) val kp = kpg.generateKeyPair()
Java
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ KeyPairGenerator kpg = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); kpg.initialize(new KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .build()); KeyPair kp = kpg.generateKeyPair();
استيراد مفاتيح مشفَّرة إلى جهاز آمن
يتيح لك الإصدار 9 من Android (المستوى 28 لواجهة برمجة التطبيقات) والإصدارات الأحدث استيراد مفاتيح مشفّرة بأمان إلى ملف تخزين المفاتيح باستخدام تنسيق مفتاح مُشفَّر بترميز ASN.1. بعد ذلك، يفكّ رمز مفاتيح التشفير في "Keymaster" في مخبّر المفاتيح، وبالتالي لا يظهر محتوى المفاتيح أبدًا كنص عادي في ذاكرة المضيف للجهاز. توفّر هذه العملية أمانًا إضافيًا لفك تشفير المفاتيح.
لإتاحة استيراد آمن للمفاتيح المشفَّرة إلى ملف تخزين المفاتيح، أكمِل الخطوات التالية:
أنشئ مفتاحَي تشفير يستخدمان الغرض
PURPOSE_WRAP_KEY
. ننصحك أيضًا بإضافة شهادة اعتماد إلى هذا الزوج من المفاتيح.على خادم أو جهاز تثق به، أنشئ رسالة ASN.1 لملف العميل
SecureKeyWrapper
.يحتوي الغلاف على المخطّط التالي:
KeyDescription ::= SEQUENCE { keyFormat INTEGER, authorizationList AuthorizationList } SecureKeyWrapper ::= SEQUENCE { wrapperFormatVersion INTEGER, encryptedTransportKey OCTET_STRING, initializationVector OCTET_STRING, keyDescription KeyDescription, secureKey OCTET_STRING, tag OCTET_STRING }
أنشئ عنصرًا من نوع
WrappedKeyEntry
، مع إدخال رسالة ASN.1 كصفيف بايت.نقْل عنصر
WrappedKeyEntry
هذا إلى طريقة التحميل الزائد لسمةsetEntry()
التي تقبل عنصرKeystore.Entry
.
العمل مع إدخالات ملف تخزين المفاتيح
يمكنك الوصول إلى مقدّم AndroidKeyStore
من خلال
جميع واجهات برمجة تطبيقات KeyStore
العادية.
إدخالات القائمة
يمكنك إدراج الإدخالات في ملف تخزين المفاتيح من خلال استدعاء الطريقة aliases()
:
Kotlin
/* * Load the Android KeyStore instance using the * AndroidKeyStore provider to list the currently stored entries. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val aliases: Enumeration<String> = ks.aliases()
Java
/* * Load the Android KeyStore instance using the * AndroidKeyStore provider to list the currently stored entries. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); Enumeration<String> aliases = ks.aliases();
التوقيع على البيانات والتحقّق منها
يمكنك توقيع البيانات من خلال جلب KeyStore.Entry
من ملف تخزين المفاتيح واستخدام واجهات برمجة التطبيقات
Signature
، مثل sign()
:
Kotlin
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry: KeyStore.Entry = ks.getEntry(alias, null) if (entry !is KeyStore.PrivateKeyEntry) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return null } val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run { initSign(entry.privateKey) update(data) sign() }
Java
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return null; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initSign(((PrivateKeyEntry) entry).getPrivateKey()); s.update(data); byte[] signature = s.sign();
وبالمثل، يمكنك التحقّق من البيانات باستخدام طريقة verify(byte[])
:
Kotlin
/* * Verify a signature previously made by a private key in the * KeyStore. This uses the X.509 certificate attached to the * private key in the KeyStore to validate a previously * generated signature. */ val ks = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry if (entry == null) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return false } val valid: Boolean = Signature.getInstance("SHA256withECDSA").run { initVerify(entry.certificate) update(data) verify(signature) }
Java
/* * Verify a signature previously made by a private key in the * KeyStore. This uses the X.509 certificate attached to the * private key in the KeyStore to validate a previously * generated signature. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return false; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(((PrivateKeyEntry) entry).getCertificate()); s.update(data); boolean valid = s.verify(signature);
طلب مصادقة المستخدم لاستخدام المفتاح
عند إنشاء مفتاح أو استيراده إلى AndroidKeyStore
، يمكنك تحديد أنّه ليس مسموحًا باستخدام المفتاح إلا إذا تمّت مصادقة المستخدم. تتم مصادقة المستخدم باستخدام
مجموعة فرعية من بيانات اعتماد شاشة القفل الآمنة (النقش/رقم التعريف الشخصي/كلمة المرور، بيانات الاعتماد للمقاييس الحيوية).
هذه ميزة أمان متقدّمة لا تكون مفيدة بشكل عام إلا إذا كانت متطلباتك هي أنّه ينبغي عدم السماح بتجاوز شرط مصادقة المستخدم لاستخدام المفتاح في حال اختراق عملية تقديم طلبك بعد إنشاء المفتاح أو استيراده (وليس قبل ذلك أو أثناء ذلك).
عندما يكون المفتاح مفوَّضًا للاستخدام فقط إذا تمّت مصادقة المستخدم، يمكنك الاتصال ببرمجة setUserAuthenticationParameters()
لضبطها للعمل في أحد الأوضاع التالية:
- منح الإذن لمدة زمنية
- يتم تفويض استخدام جميع المفاتيح فور مصادقة المستخدم باستخدام إحدى بيانات الاعتماد المحدّدة.
- التفويض لمدة عملية تشفير معيّنة
يجب أن يوافق المستخدم بشكلٍ فردي على كل عملية تتضمن مفتاحًا معيّنًا.
يبدأ تطبيقك هذه العملية من خلال استدعاء
authenticate()
على مثيل منBiometricPrompt
.
بالنسبة إلى كل مفتاح تنشئه، يمكنك اختيار السماح باستخدام
بيانات اعتماد قوية
مستندة إلى المقاييس الحيوية أو
بيانات اعتماد
خاصة بشاشة القفل أو كلا النوعَين من بيانات الاعتماد. لتحديد ما إذا كان المستخدم قد أعدّ بيانات الاعتماد
التي يعتمد عليها مفتاح تطبيقك، اتصل
canAuthenticate()
.
إذا كان المفتاح يتيح استخدام بيانات الاعتماد الحيوية فقط، يتم إبطال صلاحيته تلقائيًا عند إضافة عمليات تسجيل جديدة باستخدام المقاييس الحيوية. يمكنك ضبط المفتاح ليظل صالحًا عند إضافة عمليات تسجيل جديدة باستخدام ميزات قياس البصمات الحيوية. لإجراء ذلك، مرِّر false
في
setInvalidatedByBiometricEnrollment()
.
اطّلِع على مزيد من المعلومات حول كيفية إضافة ميزات المصادقة باستخدام المقاييس الحيوية إلى تطبيقك، بما في ذلك كيفية عرض مربّع حوار المصادقة باستخدام المقاييس الحيوية.
الخوارزميات المتوافقة
Cipher
KeyGenerator
KeyFactory
-
KeyStore
(تتيح أنواع المفاتيح نفسها التي تتيحهاKeyGenerator
وKeyPairGenerator
) KeyPairGenerator
Mac
Signature
SecretKeyFactory
مقالات المدونة
راجِع إدخال المدونة توحيد إمكانية الوصول إلى "متجر المفاتيح" في ICS.