Графический терминал успешно подписывает приложение, а тот же скрипт через SSH завершается сообщением errSecInternalComponent.
Самое быстрое исправление — не отзывать сертификат: выполните одинаковый минимальный тест под тем же пользователем в графическом сеансе и через SSH, затем проверьте цифровую идентичность, приватный ключ, состояние Keychain и цепочку доверия.
Последнее обновление: 25 августа 2026 года. Актуальность сверена с материалами Apple о сертификатах подписи, Keychain, профилях Provisioning Profile и настройках сборки Xcode.
Эта статья предназначена для независимых разработчиков, которые подключаются к удалённому Mac по SSH, но могут выполнить Archive из графического интерфейса. Она также пригодится небольшим командам, использующим fastlane, скрипты или self-hosted Runner для автономной подписи, а также разработчикам, перенёсшим сертификаты на новую машину и не сумевшим использовать Apple Distribution или Developer ID.
00Зафиксируйте сбой до любых изменений
errSecInternalComponent — это общее сообщение о проблеме в цепочке подписания, а не доказательство того, что сертификат нужно немедленно создавать заново. В материалах Apple по диагностике code signing отдельно рассматриваются контекст пользователя, доступ к Keychain, наличие приватного ключа и корректность сертификата. Поэтому очистка Keychain, отзыв рабочих сертификатов или удаление профилей в начале диагностики создают дополнительный риск и стирают полезные признаки.
Сначала сохраните в обезличенном виде:
- имя пользователя macOS, от имени которого запускается сборка;
- способ входа — графический сеанс, SSH, агент CI или задача launchd;
- путь к используемому Keychain;
- команду
codesign,xcodebuildили fastlane без паролей и секретов; - название этапа, на котором появилась ошибка;
- идентификатор Team, Bundle ID, имя продукта и отпечаток сертификата в замаскированном виде;
- полный текст ошибки без приватных ключей, токенов и паролей.
Официальная техническая записка Apple о внутреннем устройстве сертификатов подписи помогает отделить сертификат от приватного ключа и цифровой идентичности. Это важно: импорт одного файла сертификата не означает, что на Mac появился соответствующий закрытый ключ.
Не следует сразу выполнять security delete-keychain, удалять все identities или отзывать Apple Distribution. Если позднее выяснится, что актив повреждён, повторный импорт или ротация останутся управляемыми действиями; если причина была только в блокировке Keychain, раннее удаление создаст ненужную замену подписывающих активов.
01Сравните два сеанса на одной машине
В течение первых минут необходимо получить базовую линию. Откройте графический терминал и SSH-сессию под одним и тем же пользователем. Используйте один и тот же тестовый .app, одну и ту же команду и одну и ту же signing identity. Нельзя сравнивать Archive из Xcode под одним пользователем с SSH-задачей, работающей от имени runner, root или другого системного аккаунта.
Минимальную проверку можно выполнить с явными заполнителями:
security find-identity -v -p codesigning
codesign --force --sign "<SIGNING_IDENTITY_PLACEHOLDER>" \
--timestamp=none \
"<APP_PATH_PLACEHOLDER>"
Флаг --timestamp=none здесь нужен только для локального диагностического теста, чтобы не смешивать проблему доступа к локальному ключу с сетевым запросом службы временных меток. Для релизной сборки параметры следует вернуть к требованиям проекта и типа распространения. Настройки подписи и связанные параметры сборки описаны в справочнике Apple по настройкам Xcode.
Результат сравнения удобно зафиксировать так:
| Наблюдение | Вероятное направление | Следующий безопасный шаг | Чего не делать |
|---|---|---|---|
| В графическом сеансе и SSH одинаковый сбой | Идентичность, приватный ключ или цепочка доверия | Проверить identity и пару сертификат — ключ | Не менять сертификаты без подтверждения |
| Графический сеанс успешен, SSH завершается ошибкой | Keychain заблокирован или запрошено интерактивное разрешение | Проверить путь Keychain и права доступа в SSH-контексте | Не отключать защиту macOS глобально |
find-identity не показывает рабочую identity |
Нет полной цифровой идентичности или выбран не тот Keychain | Найти сертификат и соответствующий приватный ключ | Не импортировать случайный .cer повторно |
Identity видна, но codesign не подписывает |
Нет доступа к ключу, нарушена цепочка или неверен пользователь | Проверить Keychain, trust settings и профиль | Не считать список identities доказательством готовности |
Минимальный codesign успешен, Archive падает |
Ошибка в entitlements, вложенном коде или Provisioning Profile | Перейти к полному Archive и экспорту | Не объявлять проблему решённой |
Если ошибка возникает только в SSH, наиболее вероятны блокировка Keychain и отсутствие интерактивного подтверждения доступа к приватному ключу. Если оба сеанса завершаются одинаково, сначала проверяются состав identity и доверие к сертификату. Это рабочая последовательность, а не утверждение, что errSecInternalComponent связан с конкретной версией Xcode: официальных оснований объявлять его уникальной регрессией одного релиза нет.
02Проверьте цифровую идентичность, а не только сертификат
Запрос «Как убедиться, что сертификат действительно связан с приватным ключом?» нельзя решать просмотром одного файла .cer. Для подписи требуется связка из сертификата, соответствующего приватного ключа и доступного пользователю Keychain. У сертификата есть открытая часть и сведения о владельце, но подписывающая операция использует закрытый ключ.
Начните с перечня доступных identities:
security find-identity -v -p codesigning
Затем отдельно посмотрите сертификаты нужного типа:
security find-certificate -a -c "Apple Distribution" -Z \
"<KEYCHAIN_PATH_PLACEHOLDER>"
Вместо настоящего пути используйте путь Keychain, применяемого именно процессом сборки. Если путь не указан, macOS может искать объект не там, где его ожидает скрипт. Полученные отпечатки сверяйте с обезличенной записью из панели разработчика и с сертификатом, который действительно должен использовать проект.
В обзоре типов сертификатов Apple Apple Distribution относится к распространению приложений, тогда как Developer ID применяется для подписи программ, распространяемых вне App Store. Эти identities нельзя взаимозаменять только потому, что обе отображаются как сертификаты подписи.
Проверьте четыре частых дефекта:
- сертификат просрочен или отозван;
- сертификат импортирован без matching private key;
- в Keychain находятся несколько одноимённых identities, и скрипт выбирает не ту;
- сертификат и приватный ключ находятся в разных хранилищах либо видны разным пользователям.
Ответ на вопрос «Почему identity видна в security find-identity, но подпись всё равно не выполняется?» заключается в том, что этот список не является полной проверкой успешной операции. Он показывает пригодные для обнаружения identities, но не гарантирует, что конкретный процесс имеет право использовать приватный ключ, что Keychain разблокирован в текущем сеансе и что entitlements с профилем соответствуют приложению.
Полезно также проверить, какой Keychain активен:
security list-keychains -d user
security default-keychain -d user
Эти команды запускаются от имени пользователя сборки. Выполнение через sudo меняет контекст и часто приводит к диагностике административного Keychain вместо пользовательского. Если требуется временная проверка от имени другого пользователя, это нужно явно зафиксировать в журнале, а не считать эквивалентом SSH-задачи.
03Восстановите доступ SSH без обхода защиты
Вопрос «Почему в обычном терминале codesign работает, а через SSH — нет?» обычно связан не с самим синтаксисом codesign, а с различием состояния сеанса. Графический вход мог разблокировать Keychain и показать системное окно разрешения, тогда как удалённый процесс не имеет возможности ответить на такой запрос.
Порядок восстановления должен быть ограниченным:
- Уточните пользователя процесса:
bash
id
whoami
echo "$HOME"
Значения должны соответствовать владельцу импортированной identity и ожидаемому домашнему каталогу.
- Укажите фактический пользовательский Keychain, если среда сборки не выбирает его автоматически:
bash
security list-keychains -d user
security default-keychain -d user
- Разблокируйте только нужное хранилище в контексте пользователя сборки:
bash
security unlock-keychain \
-p "<KEYCHAIN_PASSWORD_PLACEHOLDER>" \
"<KEYCHAIN_PATH_PLACEHOLDER>"
Пароль нельзя вписывать непосредственно в файл репозитория, командную строку, доступную другим пользователям, или открытый журнал CI. Секрет должен поступать из защищённого хранилища агента, а после выполнения не должен попадать в вывод.
-
Если macOS запрашивает разрешение на использование приватного ключа, настройте минимальный доступ для фактического инструмента подписи согласно политике команды. Перед изменением ACL сохраните текущую конфигурацию и проверьте, можно ли откатить изменение. Не предоставляйте доступ всем приложениям без необходимости.
-
Перезапустите только диагностическую задачу и сравните её с графическим тестом. Не меняйте сразу сертификат, профиль и параметры проекта: иначе будет невозможно понять, какое изменение исправило ошибку.
Документация Apple о Keychain описывает Keychain как защищённое хранилище, а не обычный каталог с файлами. Поэтому копирование файла хранилища между машинами без сохранения пользовательского контекста, ACL и пароля не является надёжным переносом подписывающей среды.
Команды с sudo, полным отключением системной защиты или постоянной публикацией пароля Keychain не являются штатным решением. Они могут скрыть ошибку пользователя или разрешений, но одновременно расширяют область доступа к приватному ключу. Для автономной подписи лучше один раз корректно подготовить выделенного пользователя и его Keychain, чем запускать сборку с административными полномочиями.
04Проверьте цепочку, профиль и вложенный код
После успешного минимального теста нельзя сразу считать удалённую подпись восстановленной. Простая команда может подписать внешний bundle, тогда как полноценный Archive дополнительно проверяет вложенные framework, расширения, entitlements, профиль Provisioning Profile и соответствие идентификатора приложения.
Переходите к тестовому проекту с тем же типом распространения:
xcodebuild \
-workspace "<WORKSPACE_PLACEHOLDER>" \
-scheme "<SCHEME_PLACEHOLDER>" \
-configuration Release \
-archivePath "<ARCHIVE_PATH_PLACEHOLDER>" \
archive
После создания архива проверьте подпись приложения:
codesign --verify --deep --strict --verbose=4 \
"<ARCHIVED_APP_PATH_PLACEHOLDER>"
Флаг --deep полезен для проверки вложенной структуры на этапе диагностики, но не должен заменять понимание того, какие компоненты проект подписывает отдельно. Для каждого расширения и framework важно выяснить, какая identity и какие entitlements применялись.
Профиль должен соответствовать Bundle ID, типу сборки и назначению сертификата. Правила создания профиля для App Store описаны в официальной инструкции Apple по App Store Provisioning Profile. Если профиль устарел или не соответствует текущему приложению, его следует скачать заново только после проверки того, что сертификат и Team ID правильные. Удаление действующего профиля без подготовленной замены способно остановить сборку.
Если вывод указывает на невозможность построить цепочку сертификатов, отсутствующий промежуточный сертификат или недействительную identity, дальнейшее действие выбирается по факту:
- восстановить недостающий промежуточный сертификат из официального источника;
- повторно импортировать полную цифровую identity, включая приватный ключ;
- удалить только подтверждённый дубликат после экспорта резервной копии;
- перейти к ротации сертификата, если ключ повреждён, недоступен или актив отозван.
Изменение профилей, их загрузка и удаление должны выполняться осознанно; соответствующие операции перечислены в справке Apple по управлению Provisioning Profile. До ротации нужно проверить, какие другие приложения, агенты CI и выпуски используют текущий сертификат.
05Проведите автономную приёмку в течение первой недели
Закрытая SSH-сессия — недостаточная проверка. Целью является воспроизводимый Archive после выхода из графического сеанса, повторного подключения и перезапуска машины. Все тесты выполняются на обезличенном приложении или отдельной ветке, но с тем же пользователем, Keychain и способом доставки секретов, что и в релизе.
Отметьте каждый пункт:
- [ ] Зафиксированы пользователь macOS, HOME, путь Keychain и способ запуска сборки.
- [ ] Один и тот же тестовый продукт успешно проверен в графическом терминале и через SSH.
- [ ]
security find-identityпоказывает ожидаемую identity без неразобранных дубликатов. - [ ] Для сертификата подтверждено наличие matching private key.
- [ ] Keychain разблокируется в начале задачи без ручного окна.
- [ ] Пароль Keychain не появляется в скрипте, аргументах процесса или журнале CI.
- [ ] Доступ к приватному ключу разрешён только необходимому инструменту и пользователю.
- [ ] После выхода из графического сеанса SSH-задача всё ещё выполняет минимальный
codesign. - [ ] После повторного SSH-подключения подготовительный этап восстанавливает нужное состояние Keychain.
- [ ] После перезапуска Mac выполняется полный Archive, а не только тест внешнего
.app. - [ ] Проверены вложенные code bundles, entitlements, Provisioning Profile и тип Apple Distribution либо Developer ID.
- [ ] Экспорт и последующая загрузка выполняются тем же автоматизированным путём, что будет использоваться при релизе.
- [ ] Для каждого изменения записаны область воздействия, резервная копия и условие отката.
Если сбой появляется только после перезапуска, проблема находится не в разовой команде подписи, а в инициализации среды. Скрипт подготовки должен безопасно выбирать нужный Keychain, проверять наличие identity и завершаться с понятным сообщением, если секрет или ключ недоступен. Молчаливое продолжение после неудачного unlock-keychain создаёт ложное ощущение стабильности.
Если после выхода из графического сеанса требуется ручное нажатие в системном окне, среда не готова к ночной публикации. На этом этапе следует либо исправить доступ Keychain в рамках минимальных разрешений, либо перенести сборку на постоянно доступный удалённый Mac с сохранённым пользовательским контекстом. При выборе такого варианта можно изучить справочную информацию NUKCLOUD о работе сервиса, но решение всё равно должно приниматься по результатам описанной приёмки, а не по одному успешному запуску.
06Когда текущий Mac лучше заменить постоянной средой
Если сертификат, приватный ключ, профиль и цепочка доверия исправны, но текущая машина регулярно теряет пользовательский Keychain, зависимость от ручной графической сессии становится операционным дефектом. Такой Mac может подходить для локальной разработки, но плохо подходит как постоянный iOS packaging server: релиз будет зависеть от того, кто последним вошёл в систему и подтвердил всплывающее разрешение.
Самостоятельно купленный Mac оправдан, когда команде нужны физические порты, локальный симулятор, постоянный доступ к периферии и длительная стабильная нагрузка. Однако для временного проекта или небольшой команды у него есть реальные недостатки: первоначальная стоимость оборудования, обслуживание, резервное питание, настройка удалённого доступа и ответственность за восстановление после сбоя диска или обновления.
Публичный облачный сервер без macOS не решает задачу Xcode и code signing. Windows или Linux могут оставаться рабочим местом, но для финального Archive, подписи Apple Distribution и операций App Store Connect потребуется отдельная macOS-среда. Виртуализированный или случайно настроенный Hackintosh, в свою очередь, добавляет вопросы совместимости, обновлений и сохранения Keychain.
Поэтому если текущий вариант требует постоянного графического входа, не сохраняет Keychain после перезапуска и заставляет вручную чинить подпись перед каждым выпуском, аренда удалённого Mac у NUKCLOUD может оказаться более предсказуемой для временной или тестовой нагрузки. Доступ по SSH, постоянное выполнение задач и полный контроль пользователя позволяют сначала провести ту же приёмку, а затем решить, подходит ли среда для регулярного Archive. Стоимость и доступные варианты следует сверять на странице тарифов NUKCLOUD, не перенося конфигурацию и цену из сторонних обзоров.
Итоговая граница выбора проста: не следует регенерировать Apple Distribution только из-за errSecInternalComponent. Сначала сравниваются графический и SSH-сеанс, затем подтверждаются цифровая identity и приватный ключ, после чего восстанавливается неинтерактивный доступ Keychain и выполняется полный Archive после выхода из системы и перезапуска. Если текущий Mac проходит эти проверки — его можно оставить; если нет, постоянная удалённая среда с контролируемым пользователем и Keychain обычно рациональнее, чем повторять аварийное восстановление во время каждого релиза.