Ошибка цифровой подписи приложения Mac вне Xcode

Я разрабатываю приложение для Mac с Qt5, поэтому вне Xcode. Я хочу, чтобы GateKeeper разрешил моему приложению работать на клиентских компьютерах, а не выдавать предупреждение "Невозможно открыть, потому что личность разработчика не может быть подтверждена".

Я успешно подписал приложение цифровой подписью, но GateKeeper все еще приходит с этой жалобой. У меня есть сертификат разработчика Apple (я являюсь командным агентом), и моя цепочка для ключей говорит, что он действителен. Я также установил два корневых сертификата Apple.

Я использую утилиту командной строки codesign для цифровой подписи всех двоичных файлов внутри папки приложения, и, кроме того, я сам подписываю цифровую подпись папки приложения. Во всех случаях ответ кодового знака является информативным и не отображает ошибок. С помощью CodeSign я могу проверить, что действительно все двоичные файлы подписаны, работает

$ codesign --verify --deep --verbose=2 MyApp.app

показывает, что все двоичные файлы проверены. И кроме того, он сообщает:

MyApp.app: действительно на диске
MyApp.app: удовлетворяет установленному требованию

Бег:

$ codesign -v --verbose=4 --display MyApp.app   

дает

Исполняемые =/Users/ ххх / багажник / ууу / развернуть / релиз /MyApp.app/ Содержание /MacOS/MyApp
Идентификатор =aaaa.MyApp
Формат = комплект с тонким Mach-O (x86_64)
CodeDirectory v =20200 size =12461 flags = 0x0 (none) hashes = 616 + 3 location = встроенный
Тип хэша = размер sha1 =20
CDHash = d1c12c783dac0e8d9a2b749fb896b11558cec8b6
Размер подписи = 8532
Authority = Заявление разработчика ID: XXXXX
Authority = Центр сертификации ID разработчика
Authority=Apple Root CA
Отметка времени =29 июл. 2015 12;04:40
Записи Info.plist = 8
TeamIdentifier = YYYYY
Версия Sealed Resources =2 правила =12 файлов =10
Количество внутренних требований = 1 размер =180

что кажется нормальным

Бег

$ spctl -a -t exec -vv MyApp.app

на всех двоичных файлах дает как результат

MyApp.app: принято
источник = идентификатор разработчика
origin = ID приложения разработчика: XXXX

что тоже кажется нормальным

Запуск контрольной подписи инструмента командной строки XCode в приложении или в двоичных файлах внутри папки приложения:

$ ./check-signature /Users/xxx/trunk/yyy/release/MyApp.app

дает в результате

(c) Apple Inc., 2014 г. Все права защищены.
ДА

что во всех случаях является желаемым результатом.

Но GateKeeper по-прежнему не принимает приложение и жалуется на то, что разработчик не может быть подтвержден.

[добавлено автором в пятницу 17 июля 2015 г.]

Я думаю, что нашел проблему. Я не знаю, является ли это особенность или ошибка OSX. Мне очень помог вопрос stackru 19551298.

Когда файл загружается из Интернета, он получает расширенный атрибут файла com.apple.quarantine, связанный с ним. При двойном щелчке по этому загруженному файлу в Finder у GateKeeper есть две возможности:

  1. Когда файл не подписан, он выдает сообщение "Неопознанный разработчик и т. Д."

  2. Когда файл имеет цифровую подпись, он выдает сообщение "Разработчик не может быть подтвержден и т. Д."

В обоих случаях MessageBox имеет только одну кнопку - кнопку ОК. При нажатии этой кнопки ничего не происходит, кроме закрытия MessageBox.

Если расширенный атрибут удален (xattr -d), приложения запускаются, подписанные или нет.

Поведение отличается, когда приложения запускаются с помощью щелчка правой кнопкой мыши в Finder на приложении, а затем щелкают по "открытому" действию меню. Снова отображается одно из двух окон сообщений, но теперь с дополнительной кнопкой, позволяющей пользователю в любом случае открыть приложение. Опять же, единственная разница между подписанным и не подписанным - это сообщение "Неопознано" или "Не подтверждено". Я не ожидаю, что мои клиенты смогут сказать разницу. В результате подписание приложения является бесполезным упражнением.

Исходя из документации службы поддержки Apple, я ожидал другого, гораздо более приятного поведения GateKeeper при двойном щелчке по загруженному приложению (возможно, документация устарела или я ее неправильно прочитал):

  1. если приложение подписано, GateKeeper должен показать MessageBox с надписью "Загружен из Интернета и т. д." и кнопку с надписью "Все равно продолжить?"

  2. если приложение не подписано, MessageBox с одной кнопкой ОК и текстом "Неопознанный разработчик и т. д."

4 ответа

Решение

Извините за ответ на мой собственный вопрос, но я не вижу другого пути, так как редактирование исходного вопроса приведет к тексту спагетти.

Я наконец решил свою проблему. Сначала заслуга: (i) Ответ на мой другой вопрос о стековом потоке был очень полезен, и (ii) я получил очень хороший (платный) совет от официального разработчика Apple, зарегистрировав так называемый инцидент технической поддержки (TSI).

На основании всего этого я теперь могу дать очень краткий рецепт того, как успешно обрабатывать ваше приложение Mac с помощью GateKeeper. После детализации рецепта я покажу, в чем заключалась моя первоначальная ошибка.

Цель: после разработки приложения для Mac вне XCode, чтобы GateKeeper выдавал предупреждение "Загружено из Интернета..." с тремя кнопками, одна из которых "открыта".

Ошибка: когда GateKeeper выдает предупреждение либо с текстом ".. неопознанный разработчик..", либо с текстом "… неподтвержденный разработчик.." с - в обоих случаях - окном сообщения с одной кнопкой ОК.

Подготовка вашего приложения к GateKeeper-ready включает три этапа:

  • Сделайте ваше приложение автономным без неприемлемых внешних зависимостей. Единственными приемлемыми внешними зависимостями являются системные библиотеки. Все остальные зависимости должны быть скопированы в вашу папку MyApp.app. GateKeeper отклоняет любое приложение, имеющее несистемные внешние зависимости
  • Двоичные файлы не должны находиться в недопустимых местах в папке MyApp.app. Библиотеки идут в MyApp/Contents/Frameworks, а исполняемый файл - в MyApp/Contents/MacOS
  • Все двоичные файлы внутри MyApp должны иметь цифровую подпись. Тогда папка MyApp.app должна быть подписана. Для этой подписи необходим сертификат Apple "Application ID Application ..."

Наш рецепт автоматический. Вся работа выполняется одним скриптом. В случае Qt Creator мы используем скрипт qmake, где мы получаем доступ к системной оболочке через $$system команда. При использовании любой из системных команд (Xcode) codesign, spctl или же check-signature мы предполагаем, что вы перенаправили stderr в stdout, как указано в ответе на вопрос. В противном случае вы не сможете перехватить ответ системы при запуске этих утилит. В дальнейшем мы не будем явно показывать это перенаправление.

ЗДЕСЬ НАШ РЕЦЕПТ

А. Создание приложения автономным:

  1. скопировать (со скриптом) все необходимые двоичные файлы в папку MyApp.folder
  2. запустить (со скриптом) install_name_tool -change а также install_name_tool -id так что все зависимости внутри приложения имеют относительный тип @executable_path/../MacOS.. или же @executable_path/../Frameworks
  3. запустить (со скриптом) otool -L на всех двоичных файлах в папке MyApp.app и помечать любые недопустимые зависимости, такие как "@rpath..." или абсолютные пути к файлам, которые не являются системными. Обратите внимание, что otool -L не гарантируется найти все зависимости. Плагины часто находятся за горизонтом otool, Вот почему вам нужна следующая проверка.
  4. запустите терминал в расположении "MyApp.app/Contents/MacOS". Бежать export DYLD_PRINT_LIBRARIES=1, Затем запустите внутри того же окна терминала ./MyApp, Ваш терминал будет заполнен более чем сотней загруженных библиотек. Проверьте этот список еще раз на наличие запрещенных библиотек (библиотек, имеющихся на вашем компьютере, но не на компьютере ваших клиентов).
  5. доказательство пудинга в еде. Мы используем виртуальные машины MacInCloud и проверяем, работает ли там наше приложение. Альтернативным решением может быть Mac родственника, который не является разработчиком. Или вы также можете создать нового пользователя ("тест") на своем собственном Mac и скопировать приложение в его Download (или папку Desktop, или...). В последнем случае вы должны временно переименовать корневую папку вашей IDE, иначе пользователь "test" найдет отсутствующие двоичные файлы там.

B Подписание приложения

  1. Подписание: с нашим скриптом мы запускаем codesign --force --verify --verbose --sign \"Developer ID Application: ....\" \"/path/to/binary\" на всех двоичных файлах в приложении, а затем на самой папке приложения. В каждом случае системный ответ ловится. Он должен содержать в каждом случае строку "подписанный Mach-O thin".
  2. Проверка: выполнить (с помощью скрипта) команду codesign --verify --verbose \"/path/to/binary\" на каждый двоичный файл в вашем приложении и на самом приложении и перехватывает ответ системы. В каждом случае он должен содержать строки "действителен на диске" и "удовлетворяет своему назначенному требованию".
  3. Проверка GateKeeper: Выполнить (со скриптом) spctl -a -t exec -vv /path/to/binary\" на каждом двоичном файле и на самой папке приложения. Ответ системы пойман. Во всех случаях он должен содержать строку "принятый источник".
  4. check-подпись: запустить (со скриптом) check-signature \"/path/to/banary\" на каждом двоичном файле и на самой папке приложения. Ответ системы пойман. Он должен содержать строку "ДА" в каждом случае.

C Внешняя проверка

  1. zip-приложение в один zip-файл. Загрузить на один из ваших облачных серверов

  2. GateKeepers хранит длинный список (обычно сотни элементов) исключений для своей общей роли привратника. Ваше приложение не должно быть в этом списке, если вы хотите протестировать GateKeeper. Вместо того, чтобы редактировать этот список, гораздо проще создать нового пользователя на вашем Mac. Войдите в систему этого пользователя и загрузите zip-файл с облачного интернет-сервера. Finder автоматически распакует его. Нажмите здесь. Если GateKeeper сообщает вам, что может открыть приложение, но в то же время предупреждает вас о том, что оно загружено из Интернета, пора взять (белое) пиво.

Вот нужное предупреждение GateKeeper:

Виноват

Я сделал большую часть установки и подписания без явной проверки результата для каждого двоичного файла. После этого я бы использовал otool -L на нескольких двоичных файлах, но не на всех. Я упустил тот факт, что обновление до Qt 5.5 с более ранней версии Qt бинарного libqminimal.dylib приобрел дополнительную зависимость, а именно: QtDBus, Я не заметил этого, но GateKeeper сделал.

Разработчики Qt могут задаться вопросом, почему мы не просто используем macdeployqt для развертывания приложения Qt на Mac. Во-первых, нам не нравится не использовать плохо документированные утилиты черного ящика. На интернет-форумах довольно много людей сообщают о проблемах с macdeployqt, Кроме того, библиотеки Qt могут иметь разные места установки (как сообщает otool-L) при сравнении разных версий Qt. Когда у нас будет новая версия Qt, наш скрипт немедленно начнет кричать о запрещенных зависимостях. Таким образом, мы получаем информацию о том, что изменилось в этой новой версии.

Вопрос и самооценка adlag были неоценимыми, помогая мне преодолеть ту же проблему. Однако, как бы ни был хорош его рецепт, некоторые утверждения не совсем верны, поэтому я хотел бы предложить несколько дополнительных моментов.

  • Нет необходимости заменять записи @rpath в ваших двоичных файлах и динамических библиотеках операторами @executable_path. Операторы @rpath хороши, если фактические записи rpath, встроенные в двоичные файлы, не являются абсолютными. Вы можете найти множество допустимых пакетов приложений Qt, которые используют rpath. Вы можете заставить это работать, делая то, что сказал adlag, но вы можете делать работу для себя.
  • См. Комментарий Джила выше о том, как использовать otool -l $file | grep -A2 LC_RPATH а также install_name_tool -delete_rpath $path $file для проверки и удаления встроенных путей в ваших двоичных файлах и библиотеках
  • См. https://developer.apple.com/library/content/technotes/tn2206/_index.html для ясного объяснения того, почему GateKeeper жалуется на пути в ваших двоичных файлах, и как вы можете увидеть конкретную жалобу в системном журнале.
  • Если у вас есть проблема с абсолютными путями, вы должны сначала попытаться исправить вашу сборку, а не использовать install_name_tool после факта.
  • Если вы используете cmake, это, вероятно, полезно: https://cmake.org/Wiki/CMake_RPATH_handling
  • Не беги spctl -a -t exec -vv /path/to/binary в файлах dylib. Вы получите ошибки о конверте ресурса. Это ожидается, и не проблема.
  • По моему опыту, macdeployqt работает отлично. Я решил проблему, изменив мою сборку так, чтобы абсолютные пути не попадали в файл dylib-нарушителя (libquazip). Я все еще использовал install_name_tool удалить абсолютные пути к установке Qt. Я тогда использовал macdeployqt чтобы создать пакет, подпишите пакет и создайте файл DMG.

Мои два бита:

  1. Чтобы действительно проверить кодировку, мне нужно было либо загрузить свой DMG на сервер и загрузить его с помощью браузера, либо вручную установить атрибут карантина:

    APP_PATH="Any.app"
    xattr -w com.apple.quarantine '0081;5a37dc6a;Google Chrome;F15F7E1C-F894-4B7D-91B4-E110D11C4858' "$APP_PATH"
    xattr -l "$APP_PATH" # You should see the quarantine attribute here
    open "$APP_PATH"
    

    Если ваше приложение правильно подписано, вы должны увидеть системное диалоговое окно с кнопкой "Открыть".

    Я нашел значение атрибута карантина, посмотрев на другой.app, скачанный из интернета. Я не знаю, что означает значение.

    Я не очень понимаю, почему команда spctl говорит "принято", даже если служба Gatekeeper запрещает открытие приложения.

  2. У меня было окно сообщения "Неопознанный разработчик", потому что мои платформы Qt назывались "@rpath/QtCore.framework". Изменив его на "@application_path /../ Frameworks/QtCore.framework" с помощью install_name_tool, я решил проблему в моем приложении.

Выяснил проблему после много попыток.

В моем случае: Pop Message - поврежденное приложение пришло из-за отсутствия библиотек. Я создал.app файл, используя QT. Для создания DMG я использовал командный инструмент deploymacqt. Инструмент deploymacqt создает динамические библиотеки внутри.app, поэтому, в основном, если мы создадим код перед созданием dmg, это изменение будет манипулировать знаком кода. Так что правильное решение есть.

# Create dmg using 
    deploymacqt <yourapp.app> -dmg

# Open resulted dmg file, copy <yourapp.app> to different folder(let's say /Documents/<yourapp.app>)

# Codesign the /Documents/<yourapp.app> using 
    codesign --deep --force --verify --verbose --sign "Developer ID Application: <developerid>" <yourapp.app>

# Verify using
    codesign --verify --verbose=4 <yourapp.app>
 * you should see something like this
    <yourapp.app>: valid on disk
    <yourapp.app>: satisfies its Designated Requirement

# Now create again the dmg file using dropdmg(https://c-command.com/dropdmg/) application, download, install dropdmg. set the cofiguration preferences with your developer id certificate in signing option.

# drag and drop <yourapp.app> to dropdmg app, wait for creation of dmg to complete. voila you have now successfully created dmg with proper developer id certification.

# verify resulted dmg again using   
     codesign --verify --verbose=4 <yourapp.dmg>
# you can also verify with gatekeeper
     spctl -a -t exec -vv <yourapp.dmg>

как только вы закончите с ними, вы не увидите всплывающее сообщение о том, что приложение повреждено или сломано или неопознанный разработчик.

Вам нужно проверить все ваши rpath с помощью командной строки otool -l вашего исполняемого файла и вашего фреймворка. Если у вас есть локальный rpath (например: /user/name/Qt/) в вашем исполняемом файле, удалите его (с помощью этой команды install_name_tool -delete_rpath).

Другие вопросы по тегам