Автор перевода: Norbert Beaver
Переведено специально для форума xss.pro
Источник: github.com/Karmaz95/Snake_Apple/blob/main/README.md
В данной статье описывается принцип работы подписания кода, а также способы чтения подписи кода на системе macOS.
ВВЕДЕНИЕ
Добро пожаловать в очередную статью из серии о внутренней безопасности macOS!
Эта статья предоставляет подробную информацию о формате подписи кода и объясняет, как происходит процесс подписи. Здесь вы узнаете:
Подписание кода
Подписание кода гарантирует целостность программного обеспечения и идентификацию его автора.
Также, обеспечивает аутентичность и целостность файлов Mach-O. Это является обязательным требованием в современных версиях macOS. Пример ниже упрощает понимание:
Подпись является обязательным условием для включения защищенного выполнения (Hardened Runtime) и песочницы приложений, гарантируя, что они не были подвержены изменениям (подробнее об этом в будущих статьях).
Упрощенный процесс подписи:
Временная подпись
Современная macOS на процессорах ARM64 требует, чтобы весь код был как минимум временно подписан. Компоновщик автоматически подпишет бинарный файл после компиляции. Это делает подпись кода постоянной частью формата файла Mach-O.
Изменение бинарного файла нарушает целостность подписи, что препятствует запуску приложения на macOS. Поэтому после любых изменений необходимо повторно подписывать файл:
Чтобы повторно подписать бинарный файл с использованием подписи без сертификата, используйте - .
Обратите внимание, что флаг, поставленный компоновщиком, отсутствует, и идентификатор изменился:
Временная подпись означает подписание бинарного файла без криптографического доказательства.
Временно подписанные бинарные файлы Apple проверяются путем сравнения хэша CDHash (sha256) с списком известных действительных CDHash, хранящихся в кэшах доверия AMFI.
Я создал
Сертификация и нотаризация
Подписанные временно бинарные файлы будут работать только локально и не пройдут проверку Gatekeeper в других местах. Они подходят только для локального тестирования:
Для распространения вашего кода среди других пользователей подпишите его с использованием сертификата Developer ID от учетной записи разработчика Apple и пройдите процедуру нотаризации.
Подпись кода
Я уже рассмотрел формат Mach-O и его компонентов в предыдущей статье: karol-mazurek.medium.com/snake-apple-i-mach-o-a8eda4b87263
Однако я не описал подпись кода, добавленную в конце файла и загруженную через
Изображение ниже показывает один и тот же бинарный файл, открытый дважды в MachOView, чтобы показать, как загружается подпись кода и её место в файле Mach-O:
Обзор структуры
На изображении ниже показана упрощенная структура подписи кода:
Подпись кода может быть разделена на:
Что такое Blob?
Большой бинарный объект (Binary Large Object) — это коллекция бинарных данных, хранящихся как единое целое.
Самодостаточный, структурированный объект данных используется для хранения и управления различными типами информации без фиксированной схемы.
Ключевая идея здесь в том, что SuperBlob действует как контейнер для различных блоков (blob), каждый из которых выполняет свою собственную задачу, что упрощает их разбор и проверку целостности и подлинности кода. Вы можете увидеть его структуру ниже:
Как видно выше, SuperBlob начинается с
Каждый блок имеет 4-байтовое значение
Пример структуры блоков (нулевые байты были удалены для видимости):
Блок каталога кода
Наиболее важным является то, что он хранит хеши всего бинарного файла в слотах кода.
Каталог кода включает в себя такие детали, как версия каталога кода, флаги, информация о хешах для слотов кода, платформа и размер страницы.
В зависимости от версии, он может включать дополнительные элементы данных, такие как векторы рассеяния, идентификаторы команд, ограничения кода, детали исполняемого сегмента, информацию о времени выполнения и типы хешей связей.
Текущая структура каталога кода показана ниже:
Каталог кода — слоты кода
Хеши хранятся в слотах кода — один хеш на каждую страницу (4096 байт).
Хеш первой страницы хранится в слоте 0. Однако есть нюанс. Последний слот кода может быть меньше и заканчивается там, где начинается кодовая подпись.
Для примера, посмотрите на бинарный файл ниже в MachOView:
Чтобы подтвердить это предположение, вычислите последний хеш и сравните его с выводом команды codesign:
Каталог кода — специальные слоты
Они представлены отрицательными числами в выводе команды codesign.
Ниже приведён пример каталога кода версии 20400 без каких-либо флагов времени выполнения, содержащий 15 слотов хешей sha256, из которых 2 являются специальными.
Вы можете заметить, что слот -1 содержит только нули. Это связано с тем, что для нашего примерного бинарного файла нет списка свойств информации (
Хеши соответствуют страницам памяти бинарного файла (начиная с первого байта бинарного файла) и хранятся в слотах кода, начиная с слота 0.
Чтобы вручную вычислить тот же хеш, который хранится в блоге каталога кода, выведите первые
Как видно, вывод первого хеша в слоте кода совпадает:
Каталог кода — Флаги
Каталог кода — Флаги
Они являются необязательными и регулируют использование подписи.
Каталог кода — CDHash
Это финальный хеш, созданный путём хеширования всего блога каталога кода.
Ниже изображены магические байты каталога кода
Чтобы показать пример, вы можете сгенерировать CDHash вручную, извлекая
Хеш, рассчитанный таким образом, совпадает с выводом команды codesign.
Этот хеш вычисляется перед выполнением основного кода бинарного файла, а затем сравнивается с CDHash в подписи CMS.
Блок требований
Указывает дополнительные условия, необходимые для действительности подписи.
Блок требований начинается с магических байтов
Для разбора используйте команду codesign с флагом -r:
Основываясь на приведённом примере, требования следующие:
Ресурсы
Подпись кода защищает его и ресурсы, если ваш код упакован.
Хеши ресурсов хранятся в файле CodeResources PLIST:
Ключ files хранит хеш в формате sha1 для обратной совместимости, в то время как files2 хранит sha256. Они представлены в виде закодированных в base64 необработанных байтов.
Финальный хеш вычисляется на основе содержимого CodeResources и хранится в слоте -3 в каталоге кода. Чтобы вычислить этот хеш вручную, используйте:
Как видно, хеш в слоте -3 совпадает с вычисленным нами:
Блоки полномочий
Предоставляют исполняемому файлу разрешение на использование сервиса или технологии.
Существует два блока полномочий:
Первый, хранящийся в формате XML, начинается с магических байтов
Второй, хранящийся в формате DER, начинается с магических байтов
Пример блока полномочий в формате XML:
Мне кажется, что Apple медленно отказывается от формата XML, и в будущем может остаться только формат DER:
Чтобы разобрать блок полномочий, используйте команду codesign с флагом --entitlements:
Для справки: Полномочия и Полномочия на macOS.
Blob подписи CMS (BLOBWRAPPER)
Это сердце кодовой подписи. Он используется для подписания кода и его валидации.
Наиболее важно, что он хранит зашифрованный хеш каталога кода в качестве подписи и сертификата подписи, а также открытый ключ, используемый для расшифровки.
Ниже показана структура одного сертификата:
Подпись CMS хранит все сертификаты для проверки цепочки доверия. Вы можете извлечь их с помощью команды codesign и разобрать с помощью openssl:
При использовании выше представленной команды, вы получите 3 файла, содержащие сертификаты:
Они используются для проверки целостности сертификата подписи (cert_0). Вы можете вручную проверить цепочку доверия сертификатов с помощью openssl:
Однако подпись CMS — это не просто эти три сертификата. Прежде всего, она представлена в формате Distinguished Encoding Rules (DER), который является основным форматом сериализации ASN.1 и начинается с байта
CMS Signature Blob начинается с магических байтов
Я не нашел способа извлечь весь blob с помощью
Вы также можете использовать приведенный ниже bash-скрипт:
Затем вы можете проверить подпись CMS с помощью openssl:
Я также создал SignatureReader для разбора в человекочитаемом формате:
Вывод из команды выше находится в формате Cryptographic Message Syntax, описанном в RFC 5652. Я [...УДАЛИЛ...] некоторые части для улучшения читаемости:
Как вы можете видеть, помимо сертификатов существует много других данных, но наиболее важными являются зашифрованные signedAttrs, хранящиеся в виде подписи.
signedAttrs содержит CDHash и другую информацию, которая должна быть включена для обеспечения целостности подписи в соответствии с:
signedAttrs сначала хэшируется с использованием алгоритма, указанного в:
Чтобы точно увидеть, что здесь происходит, выведите все signedAttrs. Чтобы получить точное смещение, вы можете навести курсор на поле SignedAttributes:
Имея эту информацию, используйте openssl для извлечения signedAttrs:
Чтобы получить дайджест сообщения, который нужно зашифровать, вы должны хешировать его с использованием указанного алгоритма, но есть одна загвоздка. Если вы посмотрите на первый байт выгруженных signedAttrs, вы увидите байт
Тег SET OF (0x31) должен использоваться вместо тега IMPLICIT[0] (закодированного как 0xA0) для первого байта, как указано в RFC 5652#5.4:
Поэтому первый байт нужно восстановить в значение 0x31:
В конце файл signedAttrs должен содержать массив cdhashes, который включает CDHash в исходной байтовой форме sha256 в кодировке base64:
Обратите внимание, что эти хеши совпадают с хешами в выводе codesign:
Теперь signedAttrs готов для хеширования, чтобы создать дайджест сообщения:
Это дайджест сообщения, который будет зашифрован во время подписи кода с использованием закрытого ключа, соответствующего сертификату подписи, и сохранён в виде подписи.
Вы можете извлечь эту подпись с помощью SignatureReader:
Проверка подписи
Чтобы проверить подпись, необходимо расшифровать SignatureValue, используя открытый ключ, и сравнить его с дайджестом сообщения, предоставленным выше.
1. Начните с извлечения SignatureValue из CMS-подписи:
2. Извлеките открытый ключ из ранее извлечённого сертификата подписи:
3. Расшифруйте подпись с использованием открытого ключа и разберите данные в формате ASN.1:
Как видно ниже, дайджест сообщения signedAttrs, рассчитанный ранее, совпадает с дайджестом из расшифрованной подписи.
Переведено специально для форума xss.pro
Источник: github.com/Karmaz95/Snake_Apple/blob/main/README.md
В данной статье описывается принцип работы подписания кода, а также способы чтения подписи кода на системе macOS.
ВВЕДЕНИЕ
Добро пожаловать в очередную статью из серии о внутренней безопасности macOS!
Эта статья предоставляет подробную информацию о формате подписи кода и объясняет, как происходит процесс подписи. Здесь вы узнаете:
- Как определить, имеет ли файл действительную подпись
- Где хранятся разрешения и требования
- Низкоуровневый формат всех элементов подписи кода
- Концепция временной (ad hoc) подписи и криптографии с открытым ключом
- Формат ASN.1 и многое другое…
Подписание кода
Подписание кода гарантирует целостность программного обеспечения и идентификацию его автора.
Также, обеспечивает аутентичность и целостность файлов Mach-O. Это является обязательным требованием в современных версиях macOS. Пример ниже упрощает понимание:
Подпись является обязательным условием для включения защищенного выполнения (Hardened Runtime) и песочницы приложений, гарантируя, что они не были подвержены изменениям (подробнее об этом в будущих статьях).
Упрощенный процесс подписи:
- Разделение данных. Все данные делятся на страницы размером 0x1000 (4096 байт).
- Хеширование. Каждая страница хешируется с использованием алгоритма sha256, а хеши сохраняются в структуре CS_CodeDirectory.
- Хэш-сообщение. CS_CodeDirectory хешируется для генерации CDHash, который сохраняется в структуре SignedAttr внутри подписи CMS. Затем вся структура снова хешируется для генерации хэш-сообщения.
- Шифрование. Хэш-сообщение шифруется с использованием закрытого ключа, соответствующего сертификату подписи, и сохраняется как подпись в CMS.
- Подпись CMS. Формируется окончательное криптографическое доказательство (подпись CMS), которое содержит подпись SignedAttr (CDHash), зашифрованное хэш-сообщение и цепочку доверия сертификатов вместе с сертификатом подписи.
Временная подпись
Современная macOS на процессорах ARM64 требует, чтобы весь код был как минимум временно подписан. Компоновщик автоматически подпишет бинарный файл после компиляции. Это делает подпись кода постоянной частью формата файла Mach-O.
Пример бинарного файла с временной подписью от компоновщика
Изменение бинарного файла нарушает целостность подписи, что препятствует запуску приложения на macOS. Поэтому после любых изменений необходимо повторно подписывать файл:
Пример подписи с нарушенной целостностью из-за модификации байткода
Чтобы повторно подписать бинарный файл с использованием подписи без сертификата, используйте - .
codesign --force --sign - YOUR_BINARYОбратите внимание, что флаг, поставленный компоновщиком, отсутствует, и идентификатор изменился:
Временная подпись означает подписание бинарного файла без криптографического доказательства.
Временно подписанные бинарные файлы Apple проверяются путем сравнения хэша CDHash (sha256) с списком известных действительных CDHash, хранящихся в кэшах доверия AMFI.
Код:
/System/Volumes/Preboot/*/boot/*/usr/standalone/firmware/FUD/BaseSystemTrustCache.img4
/System/Volumes/Preboot/*/boot/*/usr/standalone/firmware/FUD/StaticTrustCache.img4
/System/Library/Security/OSLaunchPolicyData
Примеры хешей (исходная структура)
Я создал
TrustCacheParser для разбора кэшей доверия. Он основан на PyIMG4 и trustcache. Прежде чем использовать его, необходимо установить эти библиотеки:
Код:
python3 -m pip install pyimg4
wget https://github.com/CRKatri/trustcache/releases/download/v2.0/trustcache_macos_arm64 -O /usr/local/bin/trustcache
chmod +x /usr/local/bin/trustcache
xattr -d com.apple.quarantine /usr/local/bin/trustcache
Бинарные файлы, временно подписанные и явно поддерживаемые ядром, гарантируют, что приложение остается неизменным во время начальных этапов запуска системы.
Сертификация и нотаризация
Подписанные временно бинарные файлы будут работать только локально и не пройдут проверку Gatekeeper в других местах. Они подходят только для локального тестирования:
Пример временно подписанного бинарного файла, распространенного на другую macOS, который заблокирован Gatekeeper-ом
Для распространения вашего кода среди других пользователей подпишите его с использованием сертификата Developer ID от учетной записи разработчика Apple и пройдите процедуру нотаризации.
- Для получения дополнительной информации по подписанию см. руководство TN3125: Внутреннее подписание кода и профили предоставления.
Для руководства по нотаризации проверьте документ Notarizing macOS software before distribution.
Подпись кода
Я уже рассмотрел формат Mach-O и его компонентов в предыдущей статье: karol-mazurek.medium.com/snake-apple-i-mach-o-a8eda4b87263
Однако я не описал подпись кода, добавленную в конце файла и загруженную через
LC_CODE_SIGNATURE:
Изображение ниже показывает один и тот же бинарный файл, открытый дважды в MachOView, чтобы показать, как загружается подпись кода и её место в файле Mach-O:
Обзор структуры
На изображении ниже показана упрощенная структура подписи кода:
Подпись кода может быть разделена на:
- Super Blob — список других блоков (блок каталога кода, блок требований, блок полномочий, блок подписи).
- Каталог кода — содержит криптографические хеши, которые фиксируют исполняемые страницы кода, ресурсы и метаданные.
- Требование — критерии, которым должна соответствовать кодовая подпись, прежде чем исполняемый файл будет разрешено запускать.
- Полномочия (XML и DER) — пары ключ-значение, которые дают исполняемому файлу разрешение на использование различных системных ресурсов, таких как сеть, файловая система и т.д.
- Подпись CMS — сама криптографическая подпись в формате Cryptographic Message Syntax (CMS), определённом в RFC565, используемая для валидации.
Для справки: Понимание подписи кода.
Что такое Blob?
Большой бинарный объект (Binary Large Object) — это коллекция бинарных данных, хранящихся как единое целое.
Самодостаточный, структурированный объект данных используется для хранения и управления различными типами информации без фиксированной схемы.
Ключевая идея здесь в том, что SuperBlob действует как контейнер для различных блоков (blob), каждый из которых выполняет свою собственную задачу, что упрощает их разбор и проверку целостности и подлинности кода. Вы можете увидеть его структуру ниже:
Как видно выше, SuperBlob начинается с
magic, [URL='https://github.com/Karmaz95/Snake_Apple/blob/main/II.%20Code%20Signing/mac/cs_blobs.h#L278']length[/URL] и count всех блоков внутри. Затем идет type и offset для каждого блока.Каждый блок имеет 4-байтовое значение
magic, 4-байтовую length (включая размер поля магического числа и длины), и данные, определенные соответствующим типом блока.
Структура блока это Type-Length-Value
Пример структуры блоков (нулевые байты были удалены для видимости):
Блок каталога кода
Наиболее важным является то, что он хранит хеши всего бинарного файла в слотах кода.
Каталог кода включает в себя такие детали, как версия каталога кода, флаги, информация о хешах для слотов кода, платформа и размер страницы.
В зависимости от версии, он может включать дополнительные элементы данных, такие как векторы рассеяния, идентификаторы команд, ограничения кода, детали исполняемого сегмента, информацию о времени выполнения и типы хешей связей.
Текущая структура каталога кода показана ниже:
Пример блока каталога кода (версия 0x20400)
Каталог кода — слоты кода
Хеши хранятся в слотах кода — один хеш на каждую страницу (4096 байт).
Хеш первой страницы хранится в слоте 0. Однако есть нюанс. Последний слот кода может быть меньше и заканчивается там, где начинается кодовая подпись.
Изображение показывает пример смещения последнего байта страницы памяти на 0xC520.
Для примера, посмотрите на бинарный файл ниже в MachOView:
Изображение показывает начальное смещение кодовой подписи на 0xC520.
- Кодовая подпись начинается со смещения
0xC520. - Последняя страница памяти должна быть кратна
0x1000без остатка. - В данном случае это будет
0xC000:
Python:
def roundToLastPage(hex_number):
rounded_number = (hex_number + 0xFFF) & 0xFFFFE000
return hex(rounded_number)
Чтобы подтвердить это предположение, вычислите последний хеш и сравните его с выводом команды codesign:
Сравнение хеша последней страницы памяти.
Каталог кода — специальные слоты
Они представлены отрицательными числами в выводе команды codesign.
Ниже приведён пример каталога кода версии 20400 без каких-либо флагов времени выполнения, содержащий 15 слотов хешей sha256, из которых 2 являются специальными.
Вы можете заметить, что слот -1 содержит только нули. Это связано с тем, что для нашего примерного бинарного файла нет списка свойств информации (
Info.plist). Слот -2 хранит хеш поля требований, которое присутствует в нашем бинарном файле:
Хеши соответствуют страницам памяти бинарного файла (начиная с первого байта бинарного файла) и хранятся в слотах кода, начиная с слота 0.
Чтобы вручную вычислить тот же хеш, который хранится в блоге каталога кода, выведите первые
0x1000 байт бинарного файла:
Python:
dd bs=1 skip=0 count=0x1000 if=signed_ad_hoc_example 2>/dev/null | sha256sum
Как видно, вывод первого хеша в слоте кода совпадает:
Каталог кода — Флаги
Каталог кода — Флаги
Они являются необязательными и регулируют использование подписи.
Для справки: SecCodeSignatureFlags.
Каталог кода — CDHash
Это финальный хеш, созданный путём хеширования всего блога каталога кода.
Ниже изображены магические байты каталога кода
0xFADE0C02 вместе с его длиной 0x259, хранящимися в кодовой подписи:
Чтобы показать пример, вы можете сгенерировать CDHash вручную, извлекая
0x259 байт, начиная с смещения 0xC544, и передав их в sha256sum:
Python:
dd bs=1 skip=0xc544 count=0x259 if=signed_ad_hoc_example 2>/dev/null | sha256sum
Хеш, рассчитанный таким образом, совпадает с выводом команды codesign.
Один и тот же CDHash из вывода команды codesign и при ручном разборе.
Этот хеш вычисляется перед выполнением основного кода бинарного файла, а затем сравнивается с CDHash в подписи CMS.
Блок требований
Указывает дополнительные условия, необходимые для действительности подписи.
Блок требований начинается с магических байтов
0xFADE0C01 и оборачивает другие отдельные блоки требований, начинающиеся с магических байтов 0xFADE0C00.
Пример блока требований
Для разбора используйте команду codesign с флагом -r:
Код:
codesign -d -r - YOUR_APP
Пример стандартных требований
Основываясь на приведённом примере, требования следующие:
- Идентификатор: signed_ad_hoc_example
- Цепочка сертификатов должна вести к корневому сертификату Apple: anchor apple generic
- Листовой (подписывающий) сертификат должен быть: Apple Development
XXXXXXXXXX)
- Сертификат, выдающий листовой сертификат, должен иметь поле 1.2.840.113635.100.6.2.1, что означает, что он должен быть выдан Apple Worldwide Developer Relations Certification Authority.
Требования заданы на языке требований к кодовой подписи, закодированном с помощью опкодов, определённых в requirement.h. Хеш требований хранится в специальном слоте. Для справки посетите: TN3127: Inside Code Signing: Requirements.
Ресурсы
Подпись кода защищает его и ресурсы, если ваш код упакован.
Хеши ресурсов хранятся в файле CodeResources PLIST:
Ключ files хранит хеш в формате sha1 для обратной совместимости, в то время как files2 хранит sha256. Они представлены в виде закодированных в base64 необработанных байтов.
Пример sha1 хеша из ключа files в файле CodeResources.
Финальный хеш вычисляется на основе содержимого CodeResources и хранится в слоте -3 в каталоге кода. Чтобы вычислить этот хеш вручную, используйте:
Код:
openssl sha256 "$YOUR_APP/Contents/_CodeSignature/CodeResources"
Как видно, хеш в слоте -3 совпадает с вычисленным нами:
Хеш ресурсов
Для справки: TN3126: Inside Code Signing: Hashes — Resources
Блоки полномочий
Предоставляют исполняемому файлу разрешение на использование сервиса или технологии.
Существует два блока полномочий:
Первый, хранящийся в формате XML, начинается с магических байтов
0xFADE7171, а его хеш хранится в слоте -5.Второй, хранящийся в формате DER, начинается с магических байтов
0xFADE7172, а его хеш хранится в слоте -7. Оба блока полномочий содержат одни и те же данные.
Пример обоих блоков полномочий
Пример блока полномочий в формате XML:
Мне кажется, что Apple медленно отказывается от формата XML, и в будущем может остаться только формат DER:
Чтобы разобрать блок полномочий, используйте команду codesign с флагом --entitlements:
Код:
codesign -d --entitlements - --xml YOUR_APP | plutil -convert xml1 -o - -
codesign -d --entitlements - --der YOUR_APP
codesign -d --entitlements - YOUR_APP
Для справки: Полномочия и Полномочия на macOS.
Blob подписи CMS (BLOBWRAPPER)
Это сердце кодовой подписи. Он используется для подписания кода и его валидации.
Наиболее важно, что он хранит зашифрованный хеш каталога кода в качестве подписи и сертификата подписи, а также открытый ключ, используемый для расшифровки.
Ниже показана структура одного сертификата:
Подпись CMS хранит все сертификаты для проверки цепочки доверия. Вы можете извлечь их с помощью команды codesign и разобрать с помощью openssl:
Код:
codesign -d --extract-certificates=cert_ YOUR_APP
openssl x509 -text -noout -in cert_0
При использовании выше представленной команды, вы получите 3 файла, содержащие сертификаты:
Они используются для проверки целостности сертификата подписи (cert_0). Вы можете вручную проверить цепочку доверия сертификатов с помощью openssl:
Код:
for i in 0 1 2; do openssl x509 -in "cert_$i" -out "cert_$i.pem"; done
openssl verify -CAfile cert_2.pem cert_1.pem
openssl verify -CAfile cert_1.pem cert_0.pem
Больше о цепочке доверия сертификатов: Руководство по криптографическим сервисам.
Однако подпись CMS — это не просто эти три сертификата. Прежде всего, она представлена в формате Distinguished Encoding Rules (DER), который является основным форматом сериализации ASN.1 и начинается с байта
0x30.Больше о ASN.1: Добро пожаловать в ASN.1 и DER.
CMS Signature Blob начинается с магических байтов
0xFADE0B01 и в формате ASN.1 оборачивает эти три сертификата вместе с другой информацией:
Источник: Собственное исследование — пример CMS Signature Blob.
Я не нашел способа извлечь весь blob с помощью
codesign. Я создал опцию --extract_cms для CrimsonUroboros для извлечения:
Вы также можете использовать приведенный ниже bash-скрипт:
Код:
# Путь к файлу
binary_path="YOUR_APP"
# Извлечение сдвига магических байтов
binary_in_hex=$(xxd -p -u -c0 "$binary_path")
# Это будет работать с multiarch-приложением - учитываться будет только первый сдвиг (NR==1)
offset=$(echo -n "$binary_in_hex" | grep -ob 'FADE0B01' | awk -F: 'NR==1{print $1}')
# Данные CMS появляются после магических байтов и длины, поэтому вы должны добавить 8 байтов к значению сдвига
CMS_offset_in_dec=$(( ($offset / 2) + 8))
# Извлечение длины blob
CMS_length=$(echo -n "$binary_in_hex" | awk 'match($0, /FADE0B01/) { print substr($0, RSTART + RLENGTH, 8) }')
# Извлечение подписи CMS из файла
dd bs=1 skip="$CMS_offset_in_dec" count="0x$CMS_length" if="$binary_path" of=cms_sign 2>/dev/null
Код:
openssl cms -cmsout -print -inform DER -in cms_sign
Код:
SignatureReader --load_cms cms_sign --human
Как вы можете видеть, помимо сертификатов существует много других данных, но наиболее важными являются зашифрованные signedAttrs, хранящиеся в виде подписи.
Упрощенный процесс подписи
signedAttrs содержит CDHash и другую информацию, которая должна быть включена для обеспечения целостности подписи в соответствии с:
signedAttrs сначала хэшируется с использованием алгоритма, указанного в:
Декодирование ASN.1 с помощью онлайн-декодера для отображения алгоритма
Чтобы точно увидеть, что здесь происходит, выведите все signedAttrs. Чтобы получить точное смещение, вы можете навести курсор на поле SignedAttributes:
Пример смещения SignedAttributes
Имея эту информацию, используйте openssl для извлечения signedAttrs:
Код:
openssl asn1parse -in cms_sign -inform DER -strparse 4035 -noout -out signedAttrs
0x0A:
Выгруженные signedAttrs
Тег SET OF (0x31) должен использоваться вместо тега IMPLICIT[0] (закодированного как 0xA0) для первого байта, как указано в RFC 5652#5.4:
Поэтому первый байт нужно восстановить в значение 0x31:
Код:
printf "\x31" | dd of=signedAttrs bs=1 seek=0 count=1 conv=notrunc
Извлечённые и изменённые signedAttrs
Обратите внимание, что эти хеши совпадают с хешами в выводе codesign:
Декодированный CDHash
Теперь signedAttrs готов для хеширования, чтобы создать дайджест сообщения:
Это дайджест сообщения, который будет зашифрован во время подписи кода с использованием закрытого ключа, соответствующего сертификату подписи, и сохранён в виде подписи.
Пример зашифрованного дайджеста сообщения, хранящегося в подписи
Вы можете извлечь эту подпись с помощью SignatureReader:
Пример извлечённой подписи
Проверка подписи
Чтобы проверить подпись, необходимо расшифровать SignatureValue, используя открытый ключ, и сравнить его с дайджестом сообщения, предоставленным выше.
1. Начните с извлечения SignatureValue из CMS-подписи:
Код:
openssl asn1parse -in cms_sign -inform DER -strparse 4522 -noout -out signature
Код:
openssl x509 -inform DER -in cert_0 -pubkey -noout > pubkey.pem
Код:
openssl rsautl -in signature -verify -asn1parse -inkey pubkey.pem –pubin
Как видно ниже, дайджест сообщения signedAttrs, рассчитанный ранее, совпадает с дайджестом из расшифрованной подписи.
Проверка дайджеста сообщения
***Достигнуто максимальное количество загружаемых файлов, продолжение ниже***
***Достигнуто максимальное количество загружаемых файлов, продолжение ниже***