الإصدار 2 من مخطّط توقيع حزمة APK هو مخطّط لتوقيع الملف بالكامل يزيد من سرعة التحقّق ويعزّز ضمانات السلامة من خلال رصد أي تغييرات في الأجزاء المحمية من حزمة APK.
يؤدي التوقيع باستخدام الإصدار 2 من نظام توقيع APK إلى إدراج كتلة توقيع حِزم APK في ملف APK مباشرةً قبل قسم "دليل ZIP المركزي". داخل حزمة توقيع حزمة APK، يتم تخزين توقيعات الإصدار 2 ومعلومات هوية الموقِّع في الجزء 2 من مخطّط توقيع حِزم APK.
تم تقديم الإصدار 2 من مخطّط توقيع حزمة APK في نظام التشغيل Android 7.0 (Nougat). لجعل ملف APK قابلاً للتثبيت على أجهزة Android 6.0 (Marshmallow) والإصدارات الأقدم، يجب توقيع ملف APK باستخدام توقيع JAR قبل توقيعه باستخدام مخطّط الإصدار 2.
حظر توقيع حِزم APK
للحفاظ على التوافق مع الإصدارات القديمة من تنسيق حِزم APK، يتم تخزين توقيعات الإصدار 2 من حِزم APK والإصدارات الأحدث داخل كتلة توقيع حِزم APK، وهي حاوية جديدة تم طرحها لتوفير توافق مع الإصدار 2 من مخطّط توقيع حِزم APK. في ملف APK، تقع كتلة توقيع APK قبل الدليل المركزي لملف ZIP مباشرةً، والذي يقع في نهاية الملف.
يحتوي المربّع على أزواج من المعرّفات والقيم مُغلفة بطريقة تسهّل العثور على المربّع في حزمة APK. يتم تخزين توقيع الإصدار 2 من حزمة APK كزوج قيمة / معرّف يحمل المعرّف 0x7109871a.
التنسيق
تنسيق كتلة توقيع APK هو على النحو التالي (جميع الحقول الرقمية بالتنسيق little-endian):
-
size of block
بايت (باستثناء هذا الحقل) (uint64) - تسلسل أزواج قيمة معرّف مسبوقة بطول uint64:
ID
(uint32)value
(طول متغيّر: طول الزوج - 4 بايت)
-
size of block
بايت، وهو نفسه الحقل الأول (uint64) magic
"APK Sig Block 42" (16 بايت)
يتم تحليل حِزمة APK من خلال العثور أولاً على بداية "الدليل المركزي" لملف ZIP (من خلال
العثور على سجلّ "نهاية الدليل المركزي" لملف ZIP في نهاية الملف، ثم قراءة
الموضع النسبي لبدء "الدليل المركزي" من السجلّ). توفّر قيمة
magic
طريقة سريعة للتأكّد من أنّ ما يسبق
Central Directory هو على الأرجح مجموعة توقيع APK. تشير قيمة size of
block
بعد ذلك بفعالية إلى بداية الكتلة في
الملف.
يجب تجاهل أزواج القيمة والمعرّف التي تحتوي على معرّفات غير معروفة عند تفسير الوحدة.
حظر الإصدار 2 من مخطّط توقيع حِزم APK
يتم توقيع حِزم APK بواسطة موقِّع أو هوية واحدة أو أكثر، ويمثّل كلّ منها مفتاح توقيع. ويتم تخزين هذه المعلومات كتكتُل من الإصدار 2 من مخطّط توقيع حزمة APK. بالنسبة إلى كل ملف شخصي للموقّع، يتم تخزين المعلومات التالية:
- (خوارزمية التوقيع، الملخص، التوقيع) الصفوف. ويتم تخزين الملخّص لفصل عملية التحقّق من التوقيع عن التحقّق من سلامة محتوى حزمة APK.
- سلسلة شهادة X.509 التي تمثّل هوية الموقّع
- سمات إضافية كأزواج المفتاح/القيمة
بالنسبة إلى كل موقّع، يتم التحقّق من حزمة APK باستخدام توقيع متوافق من القائمة المقدَّمة. يتم تجاهل التوقيعات التي تتضمّن خوارزميات توقيع غير معروفة. ويعود اختيار التوقيع المراد استخدامه عند العثور على عدة توقيعات متوافقة إلى كل عملية تنفيذ. يتيح ذلك تقديم طرق توقيع أكثر أمانًا في المستقبل بطريقة متوافقة مع الإصدارات القديمة. ينُصَح بالتحقق من صحة التوقيع الأقوى.
التنسيق
يتم تخزين الإصدار 2 من مخطّط توقيع حزمة APK داخل كتلة توقيع حزمة APK ضمن المعرّف
0x7109871a
.
تنسيق حِزمة نظام توقيع APK الإصدار 2 هو كما يلي (جميع القيم الرقمية تكون بالتنسيق little-endian، وجميع الحقول التي تسبقها قيمة الطول تستخدم uint32 لتحديد الطول):
- تسلسل مسبوق بطول من
signer
مسبوق بطول:signed data
التي تحتوي على بادئة الطول:- تسلسل مسبوق بطول من
digests
مسبوق بطول:signature algorithm ID
(uint32)- (مع البادئة "الطول")
digest
—راجِع المحتوى المحمي بميزة "سلامة البيانات"
- تسلسل مسبوق بطول X.509
certificates
:certificate
X.509 مسبوقة بطولcertificate
(تنسيق ASN.1 DER)
- تسلسل مسبوق بطول من
additional attributes
مسبوق بطول:ID
(uint32)value
(طول متغيّر: طول السمة الإضافية - 4 بايت)
- تسلسل مسبوق بطول من
- تسلسل مسبوق بطول من
signatures
مسبوق بطول:signature algorithm ID
(uint32)signature
مسبوقة بطولها علىsigned data
public key
مسبوقة بالطول (SubjectPublicKeyInfo، نموذج ASN.1 DER)
أرقام تعريف خوارزميات التوقيع
- 0x0101: RSASSA-PSS مع تجزئة SHA2-256 وSHA2-256 MGF1 و32 بايت من الملح، اللاحقة: 0xbc
- 0x0102—RSASSA-PSS مع ملخص SHA2-512، SHA2-512 MGF1، 64 بايت من الملح، المقطورة: 0xbc
- 0x0103—RSASSA-PKCS1-v1_5 مع ملخص SHA2-256. يُستخدم هذا الإجراء لأنظمة الإنشاء التي تتطلّب استخدام توقيعات محدّدة.
- 0x0104—RSASSA-PKCS1-v1_5 مع ملخص SHA2-512. يُستخدم هذا الإجراء لأنظمة الإنشاء التي تتطلّب استخدام توقيعات محدّدة.
- 0x0201: ECDSA مع خلاصة SHA2-256
- 0x0202: ECDSA مع خلاصة SHA2-512
- 0x0301: توقيع رقمي متقدّم (DSA) مع خلاصة SHA2-256
جميع خوارزميات التوقيع المذكورة أعلاه متوافقة مع نظام Android الأساسي. يمكن أن تتيح أدوات التوقيع مجموعة فرعية من الخوارزميات.
أحجام المفاتيح ومنحنيات الإحالة الناجحة (EC) المتوافقة:
- RSA: 1024 و2048 و4096 و8192 و16384
- التشفير المتماثل: NIST P-256 وP-384 وP-521
- الإعلانات الديناميكية على شبكة البحث: 1024 و2048 و3072
المحتوى المحميّ بسلامة التطبيق
لأغراض حماية محتوى حِزم APK، تتألف حزمة APK من أربعة أقسام:
- محتوى إدخالات ZIP (من الموضع 0 حتى بداية مجموعة توقيع APK)
- حظر توقيع حِزم APK
- الدليل المركزي بتنسيق ZIP
- نهاية الدليل المركزي بتنسيق ZIP
يحمي الإصدار 2 من مخطّط توقيع حزمة APK سلامة الأقسام 1 و3 و4 وكتلة signed data
من الإصدار 2 من مخطّط توقيع حزمة APK المضمّنة
داخل القسم 2.
يتم حماية سلامة الأقسام 1 و3 و4 من خلال ملخّص واحد أو أكثر من
محتوياتها المخزّنة في وحدات signed data
التي يتم بدورها حمايتها من خلال توقيع واحد أو أكثر.
يتم احتساب الملخّص في الأقسام 1 و3 و4 على النحو التالي، على غرار شجرة ميركل ذات المستويَين.
يتم تقسيم كل قسم إلى أجزاء متتالية بحجم 1 ميغابايت (220 بايت). قد يكون الجزء الأخير
في كل قسم أقصر. يتم احتساب ملخّص كل قطعة من خلال
تسلسل البايت 0xa5
وطول القطعة بالبايت
(الترتيب الأقل أهمية لوحدة 32 بت) ومحتويات القطعة. يتم احتساب الملخّص على مستوى الملف بالكامل
من خلال تسلسل البايت 0x5a
وعدد الأجزاء
(بترتيب endian الأصغر) وتسلسل الملخّصات للأجزاء في
الترتيب الذي تظهر به الأجزاء في حزمة APK. يتم احتساب الملخّص بطريقة مجزّأة ل
السماح بتسريع عملية الحساب من خلال إجراءها بشكل موازٍ.
تصبح حماية القسم 4 (نهاية الدليل المركزي في ZIP) معقّدة بسبب القسم الذي يحتوي على الإزاحة للدليل المركزي في ZIP. يتغيّر البدء عند تغيُّر حجم كتلة توقيع حزمة APK، على سبيل المثال، عند إضافة توقيع جديد. وبالتالي، عند احتساب الملخّص على "نهاية دليل ZIP المركزي"، يجب التعامل مع الحقل الذي يحتوي على الإزاحة في دليل ZIP المركزي على أنّه يحتوي على الإزاحة في كتلة توقيع APK.
العودة إلى الإصدار السابق من إجراءات الحماية
قد يحاول أي مهاجم محاولة إثبات ملكية حِزمة APK موقَّعة من الإصدار v2 كحزمة APK موقَّعة بالإصدار v1 على أنظمة Android الأساسية التي تتيح التحقّق من حِزمة APK الموقَّعة بالإصدار 2. للحدّ من هذا الهجوم، يجب أن تحتوي حِزم APK الموقَّعة بالإصدار 2 والتي تم توقيعها أيضًا بالإصدار 1 على سمة X-Android-APK-Signed في القسم الرئيسي من ملفات META-INF/*.SF. قيمة سمة هي مجموعة مفصولة بفواصل من معرّفات مخطّط توقيع حزمة APK (معرّف هذا المخطّط هو 2). عند التحقّق من توقيع الإصدار 1، على أداة التحقّق من حِزم APK رفض حِزم APK التي لا تحتوي على توقيع لمخطّط توقيع حِزم APK الذي يفضّله أداة التحقّق من هذه المجموعة (مثل مخطّط الإصدار 2). تعتمد هذه الحماية على حقيقة أنّ محتوى ملفات META-INF/*.SF محمي بتوقيعات الإصدار 1. اطّلِع على القسم المعني بموضوع التحقّق من حِزم APK الموقَّعة باستخدام JAR.
يمكن للمهاجم محاولة إزالة التوقيعات الأقوى من حزمة توقيع APK
باستخدام مخطط الإصدار 2. وللحدّ من حدة هذا الهجوم، يتم تخزين قائمة بأرقام تعريف خوارزميات التوقيع
التي تم توقيع حزمة APK معها في كتلة "signed data
"
المحمية بكل توقيع.
التحقق
في الإصدار Android 7.0 والإصدارات الأحدث، يمكن التحقق من حِزم APK وفقًا للإصدار 2+ من مخطَّط توقيع حزمة APK أو توقيع JAR (مخطط الإصدار 1). تتجاهل الأنظمة الأساسية القديمة توقيعات الإصدار 2 وتتحقق من توقيعات الإصدار 1 فقط.
التحقّق من الإصدار 2 من مخطّط توقيع حِزم APK
- حدِّد موقع مجموعة توقيع APK وتأكَّد مما يلي:
- يحتوي حقلَا الحجم الخاصان بوحدة توقيع APK على القيمة نفسها.
- يتبع "الدليل المركزي" في ملف ZIP على الفور سجلّ "نهاية الدليل المركزي" في ملف ZIP.
- لا يتبع رمز ZIP الخاص بنهاية الدليل المركزي المزيد من البيانات.
- حدِّد موقع أول مجموعة من الإصدار 2 من مخطّط توقيع حزمة APK داخل مجموعة توقيع حزمة APK. في حال توفُّر كتلة الإصدار 2، انتقِل إلى الخطوة 3. وإذا لم يكن كذلك، يمكنك الرجوع للتحقق من حزمة APK باستخدام مخطط الإصدار 1.
- لكل
signer
في مجموعة الإصدار 2 من مخطّط توقيع حِزم APK:- اختَر
signature algorithm ID
الأقوى المتاح منsignatures
. يعتمد ترتيب مدى القوة على كل إصدار من إصدارات نظام التشغيل أو عملية التنفيذ. - تحقَّق من
signature
المطابق منsignatures
مقارنةً بـsigned data
باستخدامpublic key
. (يمكن الآن تحليلsigned data
بأمان). - تأكَّد من تطابق القائمة المرتبة لأرقام تعريف خوارزمية التوقيع في
digests
وsignatures
. (يهدف ذلك إلى منع إزالة التوقيع أو إضافته). - احسِب ملخص محتوى حزمة APK باستخدام خوارزمية الملخّص نفسها التي تستخدمها خوارزمية الملخص التي تستخدمها خوارزمية التوقيع.
- تأكَّد من أنّ الملخّص المحسوب مطابق لملف العميل المعني
digest
منdigests
. - تأكَّد من أنّ SubjectPublicKeyInfo لأول
certificate
منcertificates
مطابقة لـpublic key
.
- اختَر
- تنجح عملية إثبات الملكية إذا تم العثور على سمة
signer
واحدة على الأقل ونجحت الخطوة 3 لكل منها في العثور علىsigner
.
ملاحظة: يجب عدم التحقّق من حزمة APK باستخدام مخطط الإصدار 1 في حال حدوث خطأ في الخطوة 3 أو 4.
التحقّق من حِزم APK الموقَّعة باستخدام JAR (مخطّط الإصدار 1)
حزمة APK الموقَّعة باستخدام JAR هي حزمة JAR قياسية موقَّعة، ويجب أن تحتوي على الإدخالات المدرَجة فيملف META-INF/MANIFEST.MF بالضبط، ويجب أن تكون جميع الإدخالات موقَّعة من خلال المجموعة نفسها من الموقِّعين. يتم التحقّق من سلامتها على النحو التالي:
- يتم تمثيل كل موقّع من خلال إدخال META-INF/<signer>.SF و META-INF/<signer>.(RSA|DSA|EC) JAR.
- <signer>.(RSA|DSA|EC) هو PKCS #7 CMS ContentInfo مع بنية SignedData يتم التحقّق من توقيعه على <signer>.SF file.
- يحتوي ملف <signer>.SF على ملخّص للملف بأكمله من META-INF/MANIFEST.MF وملخّصات لكل قسم من META-INF/MANIFEST.MF. يتم التحقّق من خلاصة الملف بالكامل في MANIFEST.MF. وإذا تعذّر ذلك، يتم التحقّق من خلاصة كل قسم من أقسام MANIFEST.MF بدلاً من ذلك.
- يحتوي meta-INF/MANIFEST.MF على قسم ذي اسم مطابق، لكل إدخال في JAR محميًا بسلامة، يحتوي على ملخص لمحتوى الإدخال غير المضغوط. تم التحقّق من جميع هذه الملخّصات.
- يتعذّر إثبات صحة حزمة APK إذا كانت تحتوي على إدخالات JAR غير مُدرَجة في MANIFEST.MF وليست جزءًا من توقيع JAR.
وبالتالي، تكون سلسلة الحماية هي <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> محتوى كل إدخال JAR محمي بسلامة البيانات.