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

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

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

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

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

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

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

Для обеспечения обратной совместимости с форматом APK версии 1 подписи APK версии 2 и более новых хранятся внутри блока подписи APK — нового контейнера, представленного для поддержки схемы подписи APK v2. В файле APK блок подписи APK расположен непосредственно перед центральным каталогом ZIP, который находится в конце файла.

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

Формат

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

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

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

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

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

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

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

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

Формат

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

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

  • Последовательность с префиксом длины signer с префиксом длины:
    • signed data с префиксом длины:
      • последовательность digests с префиксом длины:
      • Последовательность certificates X.509 с префиксом длины:
        • certificate X.509 с префиксом длины (форма ASN.1 DER)
      • Последовательность additional attributes с префиксом длины:
        • ID (uint32)
        • value (variable-length: длина дополнительного атрибута — 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.
  • ЕС: НИСТ 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 центрального каталога) усложняется разделом, содержащим смещение центрального каталога ZIP. Смещение меняется при изменении размера блока подписи APK, например, при добавлении новой подписи. Таким образом, при вычислении дайджеста по концу ZIP центрального каталога поле, содержащее смещение центрального каталога ZIP, должно рассматриваться как содержащее смещение блока подписи APK.

Защита от отката

Злоумышленник может попытаться проверить APK, подписанный версией 2, как APK, подписанный версией 1, на платформах Android, поддерживающих проверку APK, подписанных версией 2. Чтобы смягчить эту атаку, APK-файлы с подписью v2, которые также имеют подпись v1, должны содержать атрибут X-Android-APK-Signed в основном разделе своих файлов META-INF/*.SF. Значением атрибута является набор идентификаторов схемы подписи APK, разделенных запятыми (идентификатор этой схемы равен 2). При проверке подписи v1 верификатор APK должен отклонять APK, которые не имеют подписи для схемы подписи APK, которую верификатор предпочитает из этого набора (например, схемы v2). Эта защита основана на том факте, что содержимое файлов META-INF/*.SF защищено подписями версии 1. См. раздел о проверке APK, подписанного JAR .

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

Проверка

В Android 7.0 и более поздних версиях APK-файлы можно проверять в соответствии со схемой подписи APK v2+ или подписью JAR (схема v1). Старые платформы игнорируют подписи версии 2 и проверяют только подписи версии 1.

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

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

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

  1. Найдите блок подписи APK и убедитесь, что:
    1. Два поля размера блока подписи APK содержат одно и то же значение.
    2. Сразу за ZIP Central Directory следует запись ZIP End of Central Directory.
    3. ZIP Конец центрального каталога не сопровождается дополнительными данными.
  2. Найдите первый блок схемы подписи APK v2 внутри блока подписи APK. Если блок v2 присутствует, перейдите к шагу 3. В противном случае вернитесь к проверке APK с использованием схемы v1 .
  3. Для каждого signer в блоке схемы подписи APK 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/<signer>.SF и META-INF/<signer>.(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 с защитой целостности.