Схема подписи APK v2

APK Signature Scheme v2 — это схема подписи всего файла, которая увеличивает скорость проверки и укрепляет гарантии целостности , обнаруживая любые изменения в защищенных частях APK.

Подписание с использованием схемы подписи APK v2 вставляет блок подписи APK в файл APK непосредственно перед разделом ZIP Central Directory. Внутри блока подписи APK подписи версии 2 и идентификационная информация подписывающего лица хранятся в блоке схемы подписи APK версии 2 .

APK до и после подписания

Рисунок 1. APK до и после подписания

Схема подписи APK v2 была представлена ​​в Android 7.0 (Nougat). Чтобы APK можно было установить на Android 6.0 (Marshmallow) и более ранние устройства, APK должен быть подписан с использованием подписи JAR перед подписанием по схеме v2.

Блок подписи APK

Чтобы поддерживать обратную совместимость с форматом APK v1, подписи APK v2 и более новых версий хранятся внутри блока подписи APK, нового контейнера, введенного для поддержки схемы подписи APK v2. В файле APK блок подписи APK расположен непосредственно перед центральным каталогом ZIP, который находится в конце файла.

Блок содержит пары идентификатор-значение, упакованные таким образом, чтобы упростить поиск блока в APK. Подпись v2 APK хранится в виде пары идентификатор-значение с идентификатором 0x7109871a.

Формат

Формат блока подписи APK следующий (все числовые поля имеют обратный порядок байтов):

  • size of block в байтах (исключая это поле) (uint64)
  • Последовательность пар ID-значение с префиксом длины uint64:
    • ID (uint32)
    • value (переменная-длина: длина пары - 4 байта)
  • size of block в байтах — такой же, как у самого первого поля (uint64)
  • magic «APK Sig Block 42» (16 байт)

APK анализируется, сначала находя начало центрального каталога ZIP (находя запись ZIP End of Central Directory в конце файла, а затем считывая начальное смещение центрального каталога из записи). magic значение обеспечивает быстрый способ установить, что то, что предшествует Central Directory, вероятно, является блоком подписи APK. Затем size of block эффективно указывает на начало блока в файле.

Пары идентификатор-значение с неизвестными идентификаторами следует игнорировать при интерпретации блока.

Схема подписи APK v2 Block

APK подписывается одним или несколькими подписывающими лицами/идентификаторами, каждое из которых представлено ключом подписи. Эта информация хранится в виде блока схемы подписи APK v2. Для каждого подписывающего хранится следующая информация:

  • (алгоритм подписи, дайджест, подпись) кортежи. Дайджест хранится, чтобы отделить проверку подписи от проверки целостности содержимого APK.
  • Цепочка сертификатов X.509, представляющая личность подписывающей стороны.
  • Дополнительные атрибуты в виде пар ключ-значение.

Для каждой подписывающей стороны APK проверяется с использованием поддерживаемой подписи из предоставленного списка. Подписи с неизвестными алгоритмами подписи игнорируются. Каждая реализация сама выбирает, какую сигнатуру использовать при обнаружении нескольких поддерживаемых сигнатур. Это позволяет в будущем внедрять более надежные методы подписи с обратной совместимостью. Предлагаемый подход заключается в проверке самой надежной подписи.

Формат

Блок APK Signature Scheme v2 хранится внутри блока подписи APK под идентификатором 0x7109871a .

Формат блока APK Signature Scheme v2 следующий (все числовые значения имеют обратный порядок байтов, все поля с префиксом длины используют uint32 для длины):

  • последовательность с префиксом длины signer стороны с префиксом длины:
    • signed data с префиксом длины:
      • последовательность digests с префиксом длины:
      • последовательность 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

Все вышеперечисленные алгоритмы подписи поддерживаются платформой Android. Инструменты подписи могут поддерживать подмножество алгоритмов.

Поддерживаемые размеры ключей и кривые EC:

  • ЮАР: 1024, 2048, 4096, 8192, 16384
  • ЕС: NIST P-256, P-384, P-521
  • ДСА: 1024, 2048, 3072

Содержимое, защищенное целостностью

В целях защиты содержимого APK APK состоит из четырех разделов:

  1. Содержимое записей ZIP (от смещения 0 до начала блока подписи APK)
  2. Блок подписи APK
  3. Центральный каталог ZIP
  4. ZIP-конец центрального каталога

Разделы APK после подписания

Рисунок 2. Разделы APK после подписания

Схема подписи APK v2 защищает целостность разделов 1, 3, 4 и signed data блока схемы подписи APK v2, содержащегося внутри раздела 2.

Целостность разделов 1, 3 и 4 защищена одним или несколькими дайджестами их содержимого, хранящимися в signed data , которые, в свою очередь, защищены одной или несколькими подписями.

Дайджест по разделам 1, 3 и 4 вычисляется следующим образом, аналогично двухуровневому дереву Меркла . Каждый раздел разбит на последовательные фрагменты размером 1 МБ (2 20 байт). Последний фрагмент в каждом разделе может быть короче. Дайджест каждого фрагмента вычисляется путем конкатенации байта 0xa5 , длины фрагмента в байтах (uint32 с прямым порядком байтов) и содержимого фрагмента. Дайджест верхнего уровня вычисляется на основе конкатенации байта 0x5a , количества фрагментов (с прямым порядком байтов uint32) и конкатенации дайджестов фрагментов в том порядке, в котором фрагменты появляются в APK. Дайджест вычисляется по частям, чтобы ускорить вычисление за счет его распараллеливания.

Дайджест 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 v2+ или JAR (схема v1). Старые платформы игнорируют подписи v2 и проверяют только подписи v1.

Процесс проверки подписи APK

Рисунок 4. Процесс проверки подписи APK (новые шаги выделены красным)

Проверка схемы подписи APK v2

  1. Найдите блок подписи APK и убедитесь, что:
    1. Два поля размера блока подписи APK содержат одно и то же значение.
    2. За ZIP-файлом центрального каталога сразу же следует ZIP-конец записи центрального каталога.
    3. ZIP End of Central Directory не сопровождается дополнительными данными.
  2. Найдите первый блок схемы подписи APK v2 внутри блока подписи APK. Если блокировка v2 присутствует, перейдите к шагу 3. В противном случае вернитесь к проверке APK с использованием схемы v1 .
  3. Для каждой signer стороны в блоке APK Signature Scheme v2:
    1. Выберите signature algorithm ID подписи из signatures . Порядок прочности зависит от каждой версии реализации/платформы.
    2. Сверьте соответствующую signature из signatures с signed data используя public key . (Теперь безопасно анализировать signed data .)
    3. Убедитесь, что упорядоченный список идентификаторов алгоритмов подписи в digests и signatures идентичен. (Это сделано для предотвращения удаления/добавления подписи.)
    4. Вычислите дайджест содержимого APK , используя тот же алгоритм дайджеста, что и алгоритм дайджеста, используемый алгоритмом подписи.
    5. Убедитесь, что вычисленный дайджест идентичен соответствующему digest из digests .
    6. Убедитесь, что SubjectPublicKeyInfo первого certificate certificates идентичен public key .
  4. Проверка завершается успешно, если найдена хотя бы одна signer и шаг 3 выполнен успешно для каждой найденной signer .

Примечание . APK нельзя проверять по схеме v1, если на шаге 3 или 4 произошел сбой.

Проверка APK с подписью JAR (схема v1)

APK-файл с подписью JAR — это стандартный подписанный JAR -файл, который должен содержать точно те записи, которые перечислены в META-INF/MANIFEST.MF, и где все записи должны быть подписаны одним и тем же набором подписывающих сторон. Его целостность проверяется следующим образом:

  1. Каждая подписывающая сторона представлена ​​записью JAR META-INF/<подписавшая>.SF и META-INF/<подписавшая>.(RSA|DSA|EC).
  2. <signer>.(RSA|DSA|EC) — это CMS ContentInfo PKCS #7 со структурой SignedData , подпись которой проверяется в файле <signer>.SF.
  3. Файл <signer>.SF содержит дайджест всего файла META-INF/MANIFEST.MF и дайджесты каждого раздела META-INF/MANIFEST.MF. Дайджест всего файла MANIFEST.MF проверяется. Если это не удается, вместо этого проверяется дайджест каждого раздела MANIFEST.MF.
  4. META-INF/MANIFEST.MF содержит для каждой записи JAR с защитой целостности раздел с соответствующим названием, содержащий дайджест несжатого содержимого записи. Все эти дайджесты проверены.
  5. Проверка APK завершается ошибкой, если APK содержит записи JAR, которые не указаны в MANIFEST.MF и не являются частью подписи JAR.

Таким образом, цепочка защиты выглядит следующим образом: <подписавший>.(RSA|DSA|EC) -> <подписавший>.SF -> MANIFEST.MF -> содержимое каждой записи JAR с защитой целостности.