يتيح لك نظام "متجر مفاتيح 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 هي خدمة أبطأ ومحدودة الموارد (مما يعني أنّها تتيح عددًا أقل من العمليات المتزامنة) مقارنةً ببيئة ESTE، توفّر 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.