يوضّح هذا المستند الطريقة الصحيحة لاستخدام مرافق التشفير في Android ويضمّ بعض الأمثلة على استخدامها. إذا كان تطبيقك يتطلّب مزيدًا من أمان المفتاح، استخدِم نظام "متجر مفاتيح Android".
تحديد مقدّم خدمة باستخدام نظام "متجر مفاتيح Android" فقط
إذا كنت تستخدم نظام "متجر مفاتيح Android"، يجب تحديد مقدّم خدمة.
وفي حالات أخرى، لا يضمن Android استخدام مقدّم خدمة معيّن لخوارزمية معيّنة. قد يؤدي تحديد موفِّر بدون استخدام نظام ملف تخزين مفاتيح Android إلى حدوث مشاكل توافق في الإصدارات المستقبلية.
اختيار خوارزمية مقترَحة
عندما يكون لديك خيار اختيار الخوارزمية التي تريد استخدامها (مثلاً عندما لا تتطلّب التوافق مع نظام تابع لجهة خارجية)، ننصحك باستخدام الخوارزميات التالية:
الفئة | مقترَح |
---|---|
التشفير | AES في وضع CBC أو GCM باستخدام مفاتيح 256 بت (مثل AES/GCM/NoPadding ) |
ملخص الرسائل | عائلة SHA-2 (مثل SHA-256 ) |
نظام التشغيل Mac | دالة HMAC من مجموعة SHA-2 (مثل HMACSHA256 ) |
التوقيع | مجموعة SHA-2 مع ECDSA (مثل SHA256withECDSA ) |
إجراء عمليات تشفير شائعة
تتضمّن الأقسام التالية مقتطفات توضّح كيفية إكمال العمليات التشفيرية الشائعة في تطبيقك.
تشفير رسالة
Kotlin
val plaintext: ByteArray = ... val keygen = KeyGenerator.getInstance("AES") keygen.init(256) val key: SecretKey = keygen.generateKey() val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING") cipher.init(Cipher.ENCRYPT_MODE, key) val ciphertext: ByteArray = cipher.doFinal(plaintext) val iv: ByteArray = cipher.iv
Java
byte[] plaintext = ...; KeyGenerator keygen = KeyGenerator.getInstance("AES"); keygen.init(256); SecretKey key = keygen.generateKey(); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] ciphertext = cipher.doFinal(plaintext); byte[] iv = cipher.getIV();
إنشاء ملخّص رسائل
Kotlin
val message: ByteArray = ... val md = MessageDigest.getInstance("SHA-256") val digest: ByteArray = md.digest(message)
Java
byte[] message = ...; MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(message);
إنشاء توقيع رقمي
يجب أن يكون لديك عنصر PrivateKey
يحتوي على مفتاح التوقيع الذي يمكنك إنشاؤه أثناء التشغيل أو قراءته من ملف مُدمَج مع تطبيقك أو الحصول عليه من مصدر آخر بناءً على احتياجاتك.
Kotlin
val message: ByteArray = ... val key: PrivateKey = ... val s = Signature.getInstance("SHA256withECDSA") .apply { initSign(key) update(message) } val signature: ByteArray = s.sign()
Java
byte[] message = ...; PrivateKey key = ...; Signature s = Signature.getInstance("SHA256withECDSA"); s.initSign(key); s.update(message); byte[] signature = s.sign();
إثبات صحة توقيع رقمي
يجب أن يكون لديك عنصر PublicKey
يحتوي على المفتاح العام للموقِّع،
ويمكنك قراءته من ملف مرفق مع تطبيقك، أو استخراجه من
شهادة، أو
الحصول عليه من مصدر آخر حسب احتياجاتك.
Kotlin
val message: ByteArray = ... val signature: ByteArray = ... val key: PublicKey = ... val s = Signature.getInstance("SHA256withECDSA") .apply { initVerify(key) update(message) } val valid: Boolean = s.verify(signature)
Java
byte[] message = ...; byte[] signature = ...; PublicKey key = ...; Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(key); s.update(message); boolean valid = s.verify(signature);
تعقيدات التنفيذ
هناك بعض التفاصيل في عملية تنفيذ التشفير في Android التي تبدو غير عادية، ولكنّها موجودة بسبب مشاكل التوافق. يناقش هذا القسم تلك التي ستواجهها على الأرجح.
خلاصة رسالة OAEP MGF1
يتم تحديد معلمات التشفير RSA OAEP بواسطة ملخصين مختلفين للرسالة: الملخص "الرئيسي" وملخص MGF1. هناك معرّفات Cipher
تتضمّن أسماء
الملخّص، مثل Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")
،
التي تحدّد الملخّص الرئيسي وتترك ملخّص MGF1 غير محدّد. بالنسبة إلى "ملف تخزين مفاتيح Android"، يتم استخدام SHA-1 لخلاصة MGF1، في حين أنّ خلاصتَي MGF1 وSHA-1 متطابقتَين لموفّري خدمات التشفير الآخرين في Android.
لمزيد من التحكّم في الملخّصات التي يستخدمها تطبيقك، اطلب رمزًا مع إضافة OAEPP، كما هو الحال في Cipher.getInstance("RSA/ECB/OAEPPadding")
،
وقدِّم OAEPParameterSpec
إلى init()
لاختيار الملخّصَين بشكل واضح.
يظهر ذلك في الرمز البرمجي التالي:
Kotlin
val key: Key = ... val cipher = Cipher.getInstance("RSA/ECB/OAEPPadding") .apply { // To use SHA-256 the main digest and SHA-1 as the MGF1 digest init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)) // To use SHA-256 for both digests init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)) }
Java
Key key = ...; Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // To use SHA-256 the main digest and SHA-1 as the MGF1 digest cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)); // To use SHA-256 for both digests cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
الوظائف التي تم إيقافها نهائيًا
توضّح الأقسام التالية الوظائف التي سيتم إيقافها نهائيًا. لا تستخدِمه في تطبيقك.
خوارزميات Bouncy Castle
تمّت إيقاف العديد من عمليات تنفيذ الخوارزميات في Bouncy Castle. ولا يؤثر هذا إلا في الحالات التي تطلب فيها صراحةً من مقدم خدمة Bouncy Castle، كما هو مبين في المثال التالي:
Kotlin
Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC") // OR Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"))
Java
Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC"); // OR Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"));
كما هو موضّح في القسم المعنيّ بتحديد مقدّم خدمة باستخدام نظام "متجر مفاتيح Android" فقط، لا يُنصح بطلب مقدّم خدمة محدّد. إذا اتّبعت هذه الإرشادات، لن يؤثّر إيقاف هذه الميزة نهائيًا فيك.
رموز التشفير المستندة إلى كلمات المرور بدون متّجه إعداد
إنّ رموز التشفير المستندة إلى كلمة المرور (PBE) التي تتطلّب متّجه إعداد (IV) يمكنها الحصول عليه من المفتاح، إذا تم إنشاؤه بشكل مناسب، أو من متّجه إعداد تم تمريره صراحةً. في حال تم تمرير مفتاح PBE لا يحتوي على مُوجِّه إعداد ولا تتم إرسال قيمة مُوجِّه إعداد صريحة، تفترض رموز التشفير PBE على Android حاليًا أنّ قيمة مُوجِّه الإعداد هي صفر.
عند استخدام رموز التشفير PBE، يجب دائمًا تمرير مفتاح تشفير السلسلة العشوائية الصريح، كما هو موضّح في مقتطف الرمز البرمجي التالي:
Kotlin
val key: SecretKey = ... val cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC") val iv = ByteArray(16) SecureRandom().nextBytes(iv) cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))
Java
SecretKey key = ...; Cipher cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC"); byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
مقدّم خدمة العملات المشفّرة
اعتبارًا من Android 9 (المستوى 28 من واجهة برمجة التطبيقات)، تمت إزالة مقدّم بنية تشفير لغة البرمجة Java
(JCA). إذا طلب تطبيقك مثيلًا من
مقدّم خدمة التشفير، مثل استدعاء الطريقة التالية، يحدث
NoSuchProviderException
.
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
مكتبة تشفيرات الأمان في Jetpack
تم إيقاف مكتبة التشفير في Jetpack Security نهائيًا. لا يؤثر ذلك إلا في الحالات التي
يكون فيها لديك التبعيات التالية في ملف build.gradle
الخاص بوحدة تطبيقك:
رائع
dependencies { implementation "androidx.security:security-crypto:1.0.0" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.0.0") }
الخوارزميات المتوافقة
في ما يلي معرّفات خوارزمية JCA المتوافقة مع Android:
AlgorithmParameterGenerator
AlgorithmParameters
CertPathBuilder
CertPathValidator
CertStore
CertificateFactory
Cipher
KeyAgreement
KeyFactory
KeyGenerator
KeyManagerFactory
KeyPairGenerator
KeyStore
Mac
MessageDigest
SSLContext
SSLEngine.Supported
SSLSocket.Supported
SecretKeyFactory
SecureRandom
Signature
TrustManagerFactory