APK Signature Scheme v2 یک طرح امضای کل فایل است که با شناسایی هرگونه تغییر در قسمت های محافظت شده APK، سرعت تأیید را افزایش می دهد و تضمین های یکپارچگی را تقویت می کند .
امضا با استفاده از APK Signature Scheme v2 یک بلوک امضای APK را بلافاصله قبل از بخش ZIP Central Directory در فایل APK وارد میکند. در داخل بلوک امضای APK، امضاهای v2 و اطلاعات هویت امضاکننده در یک APK Signature Scheme v2 Block ذخیره میشوند.
شکل 1. APK قبل و بعد از امضا
APK Signature Scheme v2 در اندروید 7.0 (نوقا) معرفی شد. برای نصب APK روی Android نسخه 6.0 (Marshmallow) و دستگاههای قدیمیتر، APK باید با استفاده از امضای JAR قبل از امضا شدن با طرح v2 امضا شود.
برای حفظ سازگاری با فرمت v1 APK، v2 و امضاهای جدیدتر APK در یک بلوک امضای APK ذخیره میشوند، یک ظرف جدید برای پشتیبانی از APK Signature Scheme v2. در یک فایل APK، بلوک امضای APK بلافاصله قبل از فهرست مرکزی ZIP، که در انتهای فایل قرار دارد، قرار دارد.
بلوک شامل جفتهای ID-value است که بهگونهای پیچیده شدهاند که مکانیابی بلوک را در APK آسانتر میکند. امضای v2 APK به عنوان یک جفت ID-value با شناسه 0x7109871a ذخیره میشود.
فرمت بلوک امضای APK به شرح زیر است (همه فیلدهای عددی اندکی اند):
-
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
به طور موثر به شروع بلوک در فایل اشاره می کند.
جفت های ID-مقدار با شناسه های ناشناخته باید هنگام تفسیر بلوک نادیده گرفته شوند.
APK توسط یک یا چند امضاکننده/هویت امضا میشود که هر کدام با یک کلید امضا نشان داده میشوند. این اطلاعات به عنوان یک بلوک APK Signature Scheme v2 ذخیره می شود. برای هر امضا کننده، اطلاعات زیر ذخیره می شود:
- (الگوریتم امضا، هضم، امضا) تاپل. خلاصه برای جدا کردن تأیید امضا از بررسی یکپارچگی محتوای APK ذخیره میشود.
- زنجیره گواهی X.509 نشان دهنده هویت امضاکننده است.
- ویژگی های اضافی به عنوان جفت کلید-مقدار.
برای هر امضاکننده، APK با استفاده از یک امضای پشتیبانی شده از لیست ارائه شده تأیید میشود. امضاهایی با الگوریتم امضا ناشناخته نادیده گرفته می شوند. این به هر پیاده سازی بستگی دارد که انتخاب کند در صورت مواجه شدن با چندین امضای پشتیبانی شده از کدام امضا استفاده کند. این امکان معرفی روش های امضای قوی تری را در آینده به روشی سازگار با عقب را فراهم می کند. روش پیشنهادی تأیید قوی ترین امضا است.
APK Signature Scheme v2 Block در داخل بلوک امضای APK تحت شناسه 0x7109871a
ذخیره میشود.
قالب APK Signature Scheme v2 Block به شرح زیر است (همه مقادیر عددی کمی اندین هستند، همه فیلدهای با پیشوند طول از uint32 برای طول استفاده می کنند):
- دنباله طول پیشوند
signer
با پیشوند طول:-
signed data
با پیشوند طول:- دنباله پیشوند طولی از
digests
پیشوند طول:-
signature algorithm ID
(uint32) -
digest
(با پیشوند طول) — به محتوای محافظت شده با یکپارچگی مراجعه کنید
-
- دنباله پیشوند طول
certificates
X.509 :-
certificate
X.509 با پیشوند طول (فرم 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
تمامی الگوریتم های امضای فوق توسط پلتفرم اندروید پشتیبانی می شوند. ابزارهای امضا ممکن است زیر مجموعه ای از الگوریتم ها را پشتیبانی کنند.
اندازه کلیدهای پشتیبانی شده و منحنی های EC:
- RSA: 1024، 2048، 4096، 8192، 16384
- EC: NIST P-256, P-384, P-521
- DSA: 1024، 2048، 3072
به منظور حفاظت از محتوای APK، یک APK از چهار بخش تشکیل شده است:
- محتویات ورودی های ZIP (از افست 0 تا شروع بلوک امضای APK)
- بلوک امضای APK
- دایرکتوری مرکزی ZIP
- پایان ZIP دایرکتوری مرکزی
شکل 2. بخش های APK پس از امضا
APK Signature Scheme v2 از یکپارچگی بخشهای 1، 3، 4 و بلوکهای signed data
APK Signature Scheme v2 Block موجود در بخش 2 محافظت میکند.
یکپارچگی بخش های 1، 3 و 4 توسط یک یا چند خلاصه از محتویات ذخیره شده در بلوک های signed data
محافظت می شود که به نوبه خود توسط یک یا چند امضا محافظت می شوند.
هضم بخش های 1، 3 و 4 به صورت زیر محاسبه می شود، شبیه به درخت مرکل دو سطحی. هر بخش به تکه های 1 مگابایتی (2 20 بایتی) متوالی تقسیم می شود. آخرین تکه در هر بخش ممکن است کوتاهتر باشد. خلاصه هر تکه بر روی الحاق بایت 0xa5
، طول تکه بر حسب بایت (uint32 کمی اندین) و محتویات تکه محاسبه می شود. خلاصه سطح بالا بر روی الحاق بایت 0x5a
، تعداد تکه ها (uint32 اندکی اندین)، و الحاق خلاصه های تکه ها به ترتیب ظاهر شدن تکه ها در APK محاسبه می شود. هضم به صورت تکهای محاسبه میشود تا با موازی کردن آن، محاسبات را سرعت بخشد.
شکل 3. خلاصه APK
حفاظت از بخش 4 (ZIP End of Central Directory) به دلیل بخشی که شامل افست ZIP Central Directory است پیچیده است. هنگامی که اندازه بلوک امضای APK تغییر می کند، به عنوان مثال، هنگامی که یک امضای جدید اضافه می شود، افست تغییر می کند. بنابراین، هنگام محاسبه خلاصه بر روی پایان ZIP دایرکتوری مرکزی، فیلد حاوی افست دایرکتوری مرکزی ZIP باید به عنوان حاوی افست بلوک امضای APK در نظر گرفته شود.
مهاجم میتواند سعی کند یک APK با امضای v2 را به عنوان یک APK با امضای v1 در پلتفرمهای Android که از تأیید APK با امضای v2 پشتیبانی میکنند، تأیید کند. برای کاهش این حمله، فایلهای APK با امضای v2 که دارای امضای v1 نیز هستند، باید دارای ویژگی X-Android-APK-Signed در بخش اصلی فایلهای META-INF/*.SF خود باشند. مقدار مشخصه مجموعه ای از شناسه های طرح امضای APK جدا شده با کاما است (شناسه این طرح 2 است). هنگام تأیید امضای v1، تأییدکننده APK لازم است فایلهای APK را که برای طرح امضای APK که تأییدکننده از این مجموعه ترجیح میدهد، رد کند (به عنوان مثال، طرح v2). این حفاظت متکی به این واقعیت است که محتویات فایل های META-INF/*.SF توسط امضاهای v1 محافظت می شوند. به بخش تأیید APK امضا شده با JAR مراجعه کنید.
مهاجم میتواند سعی کند امضاهای قویتری را از بلوک APK Signature Scheme v2 حذف کند. برای کاهش این حمله، فهرست شناسههای الگوریتم امضایی که APK با آن امضا شده است در بلوک signed data
ذخیره میشود که توسط هر امضا محافظت میشود.
در Android نسخه 7.0 و جدیدتر، فایلهای APK را میتوان براساس APK Signature Scheme v2+ یا امضای JAR (طرح v1) تأیید کرد. پلتفرم های قدیمی امضاهای v2 را نادیده می گیرند و فقط امضاهای v1 را تأیید می کنند.
شکل 4. فرآیند تأیید امضای APK (مراحل جدید به رنگ قرمز)
- بلوک امضای APK را پیدا کنید و تأیید کنید که:
- دو فیلد اندازه بلوک امضای APK حاوی یک مقدار است.
- ZIP Central Directory بلافاصله پس از ZIP End of Central Directory ثبت می شود.
- پایان ZIP از دایرکتوری مرکزی با داده های بیشتری دنبال نمی شود.
- اولین بلوک APK Signature Scheme v2 را در داخل بلوک امضای APK قرار دهید. اگر v2 Block وجود دارد، به مرحله 3 بروید. در غیر این صورت، به تأیید APK با استفاده از طرح v1 برگردید.
- برای هر
signer
در بلوک APK Signature Scheme v2:- قوی ترین
signature algorithm ID
پشتیبانی شده را ازsignatures
انتخاب کنید. ترتیب قدرت به هر نسخه پیاده سازی/پلتفرم بستگی دارد. -
signature
مربوطه را ازsignatures
در برابرsigned data
با استفاده ازpublic key
تأیید کنید. (اکنون تجزیهsigned data
امن است.) - بررسی کنید که لیست مرتب شده شناسه های الگوریتم امضا در
digests
وsignatures
یکسان است. (این کار برای جلوگیری از حذف/افزودن امضا است.) - خلاصه محتویات APK را با استفاده از الگوریتم خلاصه مشابه الگوریتم خلاصه مورد استفاده توسط الگوریتم امضا محاسبه کنید .
- بررسی کنید که خلاصه محاسبه شده با
digest
مربوطه ازdigests
یکسان است. - بررسی کنید که
certificates
certificate
public key
یکسان است.
- قوی ترین
- اگر حداقل یک
signer
پیدا شود و مرحله 3 برای هرsigner
پیدا شده باشد، تأیید موفقیت آمیز است.
توجه : اگر در مرحله 3 یا 4 مشکلی رخ داد، APK نباید با استفاده از طرح v1 تأیید شود.
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 تأیید میشود.
- فایل <signer>.SF حاوی خلاصه فایل کامل META-INF/MANIFEST.MF و خلاصه هر بخش از META-INF/MANIFEST.MF است. خلاصه کل فایل MANIFEST.MF تأیید شده است. اگر این کار انجام نشد، خلاصه هر بخش MANIFEST.MF به جای آن تأیید می شود.
- META-INF/MANIFEST.MF شامل، برای هر ورودی JAR محافظت شده با یکپارچگی، یک بخش با نام مربوطه شامل خلاصه محتویات فشرده نشده ورودی است. همه این هضم ها تأیید شده است.
- اگر APK حاوی ورودیهای JAR باشد که در MANIFEST.MF فهرست نشدهاند و بخشی از امضای JAR نیستند، تأیید APK انجام نمیشود.
بنابراین زنجیره حفاظتی <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> محتویات هر ورودی JAR محافظت شده با یکپارچگی است.