يوضّح هذا المستند الطريقة الصحيحة لاستخدام أدوات التشفير في Android ويتضمّن بعض الأمثلة على استخدامها. إذا كان تطبيقك يتطلّب أمانًا أكبر للمفاتيح، استخدِم نظام Android Keystore.
تحديد مقدّم خدمة باستخدام نظام Android Keystore فقط
إذا كنت تستخدم نظام Android Keystore، عليك تحديد مقدّم خدمة.
في حالات أخرى، لا يضمن نظام التشغيل Android توفّر موفّر معيّن لخوارزمية معيّنة. قد يؤدي تحديد موفّر بدون استخدام نظام Android Keystore إلى حدوث مشاكل في التوافق في الإصدارات المستقبلية.
اختيار خوارزمية مقترَحة
عندما يكون لديك الحرية في اختيار الخوارزمية التي تريد استخدامها (مثلما يحدث عندما لا تحتاج إلى التوافق مع نظام تابع لجهة خارجية)، ننصحك باستخدام الخوارزميات التالية:
الفئة | الاقتراح |
---|---|
التشفير | تشفير AES في وضع CBC أو GCM باستخدام مفاتيح 256 بت (مثل AES/GCM/NoPadding ) |
MessageDigest | مجموعة 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 Keystore، يتم استخدام SHA-1 لتجزئة MGF1، بينما تكون التجزئتان متطابقتَين في موفّري التشفير الآخرين على Android.
للحصول على مزيد من التحكّم في الملخّصات التي يستخدمها تطبيقك، اطلب تشفيرًا باستخدام OAEPPadding، كما هو موضّح في 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 Keystore فقط، لا يُنصح بطلب موفّر محدّد. إذا كنت تتّبع هذه الإرشادات، لن يؤثر هذا الإيقاف النهائي فيك.
رموز التشفير المستندة إلى كلمات المرور بدون متّجه إعداد
يمكن أن تحصل برامج التشفير المستندة إلى كلمة المرور (PBE) التي تتطلّب متّجه إعداد (IV) على هذا المتّجه من المفتاح، إذا تم إنشاؤه بشكل مناسب، أو من متّجه إعداد تم تمريره بشكل صريح. إذا مرّرت مفتاح تشفير مستند إلى كلمة مرور لا يحتوي على متّجه إعداد ولم تمرّر متّجه إعداد صريحًا، ستفترض حاليًا برامج التشفير المستندة إلى كلمة مرور على Android أنّ متّجه الإعداد هو صفر.
عند استخدام رموز PBE، يجب دائمًا تمرير IV صريح، كما هو موضّح في مقتطف الرمز التالي:
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 security-crypto
تم إيقاف جميع واجهات برمجة التطبيقات في مكتبة
security-crypto
Jetpack نهائيًا في الإصدار الثابت من
الإصدار 1.1.0
.
لن يتم إصدار أي إصدارات لاحقة من هذه المكتبة.
تظهر التعليقات التوضيحية للإيقاف النهائي إذا كان لديك أي من التبعيات التالية في ملف build.gradle
الخاص بوحدة تطبيقك:
Groovy
dependencies { implementation "androidx.security:security-crypto:1.1.0" // or implementation "androidx.security:security-crypto-ktx:1.1.0" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.1.0") // or implementation("androidx.security:security-crypto-ktx:1.1.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