Нотаризация приложений под macOS с Axiom JDK и JavaPackager

Нотаризация приложений под macOS с Axiom JDK и JavaPackager


2 Апреля 2021 г.


Безопасность пользователей и защита от распространения вредоносных программ — задача первостепенной важности для IT-специалистов во всем мире. При этом она существенно усложняет работу разработчиков. Есть несколько методов обеспечения безопасности программных средств. Самый популярный — сканирование на наличие вредоносного ПО на компьютере с помощью антивирусной утилиты. Компания Apple создала дополнительный механизм: технология Gatekeeper принудительно проверяет цифровую подпись и загруженное приложение перед запуском.

Мы все чаще сталкиваемся с деанонимизацией, в результате которой происходит утечка персональных данных пользователей скомпрометированного ПО. Нотаризация — ответная реакция на такую угрозу. Благодаря ей вы можете быть уверены в том, что приложение создано надежным разработчиком и проверено на наличие вредоносного ПО.

Еще одним примером защиты пользователей является Windows Defender, который использует код подписи для проверки соответствия программы требованиям безопасности.

Сайты используют сертификаты SSL (англ. Secure Sockets Layer — уровень защищенных сокетов) для обеспечения конфиденциальности, аутентификации и целостности данных в сети. Компания Apple в свою очередь ввела инфраструктуру нотаризации.

Нотаризация — это важный, хотя и трудоемкий элемент разработки ПО, который на данный момент является обязательным. Тем не менее существуют способы упростить и даже усовершенствовать эту процедуру для приложений на Java™ и JavaFX, созданных в некоторых средах, например Liberica JDK (с 2022 года - Axiom JDK). Таким образом, нотаризация превращается из сложного действия в стандартный этап общего процесса.

У Liberica JDK есть ключевое преимущество: приложения, связанные со сборкой нашей среды исполнения Java™ в один пакет, гораздо легче пройдут процедуру нотаризации.

Что такое нотаризация и для чего она нужна?

Нотаризация позволяет пользователям удостовериться в том, что распространяемое ПО, подписанное сертификатом разработчика Developer ID, было проверено компанией Apple на наличие вредоносных компонентов.

Этот процесс не является частью сервиса App Review. Он представляет собой автоматизированную систему, которая сканирует ПО на наличие вредоносного содержимого, проверяет наличие проблем с подписанием кода и оперативно предоставляет пользователю результат проверки. При отсутствии проблем служба генерирует тикет, который разработчики могут применить к своему ПО. Затем этот тикет публикуется в Интернете, где его сможет найти функция безопасности Gatekeeper.

Когда пользователь впервые устанавливает или запускает какое-либо новое ПО, технология Gatekeeper находит тикет (опубликованный в Интернете или вшитый непосредственно в приложение) и получает сведения о нотаризации компанией Apple. Затем Gatekeeper выводит сведения в первом диалоговом окне, что позволяет пользователю понять, стоит ли запускать приложение. Начиная с версии macOS Catalina 10.15 эта операция стала обязательной, поэтому запуск приложения без нотаризации невозможен.

Axiom JDK прошла нотаризацию Apple. Какие преимущества получат наши клиенты и пользователи?

Если вы владеете английским языком, можете посмотреть развернутый ответ на этот вопрос в эпизоде Axiom Bar.

Liberica JDK является альтернативой OpenJDK. Она представляет собой основу для разработки и запуска приложений на Java SE. Нотаризацию OpenJDK нельзя назвать поверхностным процессом. Он предполагает подписание кода всех дистрибутивов и модулей.

Liberica JDK, официально признанное сертифицированное приложение, успешно прошло все этапы нотаризации еще летом 2019 года, что позволяет нам гарантировать подлинность нашего ПО.

Чтобы разработать и нотаризовать свое приложение, пользователям необходимо воспользоваться ПО, которое уже прошло эту процедуру. Поэтому вы можете ссылаться на нашу среду при разработке приложений под macOS: это просто и удобно! Liberica JDK предлагает альтернативу процедуре подписывания и нотаризации с Java™, тем самым сокращая время проверки ПО для разработчиков. К тому же сборка Liberica JDK обеспечивает простоту и удобство пакетной обработки приложений.

Инструмент JavaPackager

JavaPackager — важный элемент процедуры нотаризации и неотъемлемая часть пакета Liberica JDK. Инструмент Java Packager поставляется в комплекте с JDK. Он представляет собой упаковщик приложений на Java™ посредством командной строки, выступая в качестве альтернативы инструментам сторонних производителей. Важно помнить, что в JavaPackager недоступна функция автоматической генерации JAR-файла. Существует множество других форматов упаковщиков, в том числе нативные исполняемые модули, адаптированные под различные платформы.

Как правило, JavaPackager в комплекте Liberica JDK применяется в процессе сборки для облегчения процесса нотаризации приложений. Но его можно использовать и как самостоятельный инструмент.

Несколько вариантов применения JavaPackager:

  • создать образ приложения macOS (для тестирования);
  • создать нотаризованный DMG-образ (для распространения среди конечных пользователей);
  • создать нотаризованный PKG-установщик (для распространения среди конечных пользователей).

В этой статье мы расскажем, как выполнить все три операции. Если же чувствуете, что вам нужна помощь, или просто не хотите проходить нотаризацию самостоятельно, обратитесь в нашу службу поддержки. Введите свои данные по кнопке ниже и получите бесплатную консультацию от наших экспертов.

Как выполнить нотаризацию приложения на Java™ или JavaFX? Краткое руководство

Процесс нотаризации приложения с помощью пакета Liberica JDK и JavaPackager очень прост. Выполните описанные ниже действия и помните, что необходимо использовать экземпляр Liberica JDK без подписи. Итоговое приложение будет считаться нотаризованным и будет иметь идентификационные данные, необходимые для соответствия требованиям, предъявляемым компанией Apple.

Создание приложения с помощью Liberica JDK и JavaPackager — это многоэтапный процесс, включающий создание архива JAR с приложением, генерацию пакета и его нотаризацию.

Для успешной нотаризации необходимо получить специальную сборку Liberica JDK 8u252 для macOS, которая позволяет создать приложение с подписанием кода. В этом примере мы назвали ее bellsoft-jdk8u252+9-macos-amd64-full-nosign.zip. Мы используем специальную неподписанную копию Liberica JDK 8u252, поскольку JavaPackager не может подписывать JDK повторно.

Извлеките неподписанный пакет Liberica JDK 8u252:

unzip ./bellsoft-jdk8u252+9-macos-amd64-full-nosign.zip

Перед созданием пакетов приложения и нотаризацией необходимо разблокировать ключ разработчика:

security unlock-keychain -p "${YOUR_PASSWORD}"

Затем сформируем установочные пакеты — проиллюстрируем этот шаг далее с помощью редактора кода Recaf.

Создание JAR-файла приложения

Мы используем Recaf версии 2.0.0, поскольку его архитектура оптимальна для работы в качестве самостоятельного приложения для macOS. Для начала нам нужно создать JAR-файл с Recaf, сформировать пакет приложений и выполнить процесс нотаризации. Служба нотаризации проверяет содержимое приложения, сканируя JAR и проверяя наличие подписи всех нативных библиотек исполняемого файла. Таким образом мы должны сгенерировать файл с Recaf, проверить наличие нативной составляющей и подписать ее.

Создайте клон Recaf в своей рабочей среде:

git clone https://github.com/Col-E/Recaf.git

Проверьте ветвь объектов Recaf:

git checkout 2.0.0-redesign.10

Выполните процесс компиляции и упаковки с помощью скрипта сборки или следующей команды:

mvn clean package

При успешном завершении сборки полученный JAR-файл будет помещен в каталог target под следующим именем: recaf-2.0.0-J8-jar-with-dependencies.jar.

В качестве входных данных JavaPackager принимает основной класс приложения. Если основной класс приложения неизвестен, вы можете легко его узнать:

  1. Для извлечения манифеста из JAR введите следующую команду:
./jdk8u252.jdk-full-nosign/Contents/Home/bin/jar \
     xf ./recaf-2.0.0-J8-jar-with-dependencies.jar \
     META-INF/MANIFEST.MF

  1. В полученном результате MANIFEST.MF выделите основной класс:
grep Main-Class META-INF/MANIFEST.MF

Выходные данные с основным именем класса для Recaf:

Main-Class: me.coley.recaf.Recaf

Подписание кода нативных библиотек в рамках приложения

Нотаризация приложения невозможна без подписания кода его нативных библиотек. При отсутствии таковых этот шаг можно пропустить. В противном случае вы не сможете завершить процедуру: служба нотаризации Apple отреагирует сообщением об ошибке с указанием библиотеки без подписи.

Приложение Recaf, которое мы используем для примера, содержит нативную библиотеку libjnidispatch.jnilib внутри архива JAR. Таким образом, для обеспечения нотаризации нам нужно подписать ее в JAR-файле Recaf. Выполните следующие шаги:

  1. Извлеките библиотеку из JAR:
      jar -xf recaf-2.0.0-J8-jar-with-dependencies.jar \
     com/sun/jna/darwin/libjnidispatch.jnilib
  1. Разблокируйте ключевую последовательность:
security unlock-keychain -p ${YOUR_PASSWORD}
  1. Создайте файл разрешений для подписания библиотеки (мы будем использовать те же разрешения, которые используются в Liberica JDK, см. ниже).

  2. Создайте подпись библиотеки:

   codesign -o runtime -s "${IDENTITY}" \
   --prefix me.coley.recaf. \
     --entitlements ./entitlements \
     -vvvv ./com/sun/jna/darwin/libjnidispatch.jnilib
  1. Добавьте подписанную библиотеку в JAR-файл Recaf:
      jar uf recaf-2.0.0-J8-jar-with-dependencies.jar \
  com/sun/jna/darwin/libjnidispatch.jnilib

Теперь вы можете использовать JAR Recaf для создания и нотаризации приложения под macOS с помощью пакета Liberica JDK.

Создание образа приложения для Mac с помощью пакета Axiom JDK

JavaPackager в комплекте Liberica JDK создает подпись приложения, добавляет необходимые разрешения, фиксирует защитные метки времени и прописывает ограничения выполнения (функция hardened runtime) во время создания нативных образов macOS.

При необходимости получения справки по опциям, вызовите справку:

./jdk8u252.jdk-full-nosign/Contents/Home/bin/javapackager \
          -help <Native image type - mac.app, pkg etc.> \
          <-verbose>

Параметры пакета для образа приложения для Mac (mac.app) приведены ниже:

name - App Name - String
arguments - Command Line Arguments - List
mac.bundle-id-signing-prefix - Bundle Signing Prefix - String
classpath - Main Jar Classpath - String
mac.signing-key-developer-id-app - Apple Developer ID Application Signing Key - String
icon.icns - .icns Icon - File
jvmOptions - JVM Options - List
jvmProperties - JVM System Properties - Map
mac.category - Category - String
mac.CFBundleIdentifier - CFBundleIdentifier - String
mac.CFBundleName - CFBundleName - String
mac.CFBundleVersion - CFBundleVersion - String
runtime - JRE - RelativeFileSet
applicationClass - Main Class - String
mainJar - Main Jar - RelativeFileSet
preferencesID - Preferences ID - String
userJvmOptions - User JVM Options - Map
appVersion - Version – String

Следующий шаг — создание образа нативного приложения для Mac с помощью следующей команды:

./jdk8u252.jdk-full-nosign/Contents/Home/bin/javapackager -deploy \
  -native image \
  -name Recaf \
  -outdir res_image \
  -srcfiles ./recaf-2.0.0-J8-jar-with-dependencies.jar \
  -outfile recaf \
  -appclass me.coley.recaf.Recaf \
  -Bruntime=./jdk8u252.jdk-full-nosign/Contents/Home/ \
  -BappVersion=2.0.0-liber

Вы также можете передать файл с разрешениями с помощью опции -Bmac.entitlements=&lt;entitlements file path>. Если файл с разрешениями не указан, используются следующие разрешения:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
     <dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
     </dict>
</plist>

Итоговый образ будет создан в директории res_image/bundles/Recaf.app .

Чтобы убедиться в наличии подписей и необходимых свойств/функций приложения, используйте следующую команду:

codesign -dv --entitlements :./res_image/bundles/Recaf.app/

Она должна выдать аналогичный результат:

Executable=***/res_image/bundles/Recaf.app/Contents/MacOS/Recaf
Identifier=me.coley.recaf
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=378 flags=0x10000(runtime) hashes=3+5 location=embedded
Signature size=8998
Timestamp=***********************
Info.plist entries=15
TeamIdentifier=8LBATW8FZA
Runtime Version=10.14.0
Sealed Resources version=2 rules=13 files=5
Internal requirements count=1 size=176
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
     <dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
     </dict>
</plist>

Образы приложений чаще используются для тестирования, чем непосредственно в качестве дистрибутивов. Поэтому теперь мы перейдем к созданию пакетов DMG или PKG, предназначенных для конечных пользователей.

Создание DMG-пакета

Вам на выбор доступно два идеальных формата для конечных пользователей: DMG или PKG, при этом их параметры несколько отличаются. Отдельные разработчики и поставщики предоставляют сразу оба расширения, либо включают PKG в состав DMG. DMG является, по сути, образом диска. Пользователь сможет установить приложение обычным образом, щелкнув на иконку.

Для создания DMG-образа с помощью JavaPackager введите следующую команду:

./jdk8u252.jdk-full-nosign/Contents/Home/bin/javapackager -deploy \
  -native dmg \
  -name Recaf \
  -outdir res_dmg \
  -srcfiles ./recaf-2.0.0-J8-jar-with-dependencies.jar \
  -outfile recaf \
  -appclass me.coley.recaf.Recaf \
  -Bruntime=./jdk8u252.jdk-full-nosign/Contents/Home/ \
  -BappVersion=2.0.0-liber

Готовый образ DMG будет сформирован в директории ./res_dmg/bundles/Recaf-2.0.0-liber.dmg

Вам остается только предоставить DMG-пакет в Apple для прохождения стандартной процедуры нотаризации. Для этого Apple предлагает комплекс инструментов. Самые важные из них, пожалуй, altool (для взаимодействия со службой нотаризации) и stapler (для работы с результатами нотаризации и последующей сборки пакета).

Чтобы направить DMG-пакет в службу нотаризации Apple, введите команду:

xcrun altool --notarize-app \
    -primary-bundle-id com.bell-sw.recaf.0 \
    -username ${USERNAME} \
    -password '${YOUR_PASSWORD}' \
    -file ./res_dmg/bundles/Recaf-2.0.0-liber.dmg

Командная строка выдаст следующий ответ:

2020-04-27 08:43:58.152 altool[47259:19144203] No errors uploading 'res_dmg/bundles/Recaf-2.0.0-liber.dmg'.
RequestUUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

На данном этапе необходимо дождаться ответа от службы нотаризации. Вы можете периодически проверять статус запроса, используя универсальный уникальный идентификатор (UUID):

xcrun altool --notarization-info  ${RequestUUID} \
  -u ${USERNAME} \
  -p '${YOUR_PASSWORD}'

Имейте в виду, что процесс нотаризации занимает некоторое время. При отправке запроса сразу после запуска программа выдаст сообщение об ошибке с указанием, что UUID запроса не найден. После обработки пакета службой вы увидите следующее сообщение:

*********************** altool[47696:19145453] No errors getting notarization info.
   RequestUUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
          Date: ***
    Status: in progress
     LogFileURL: (null)

После проверки пакета статус сменится на success («успешно»). Он прошел подтверждение подлинности.

*********************** altool[88539:21766482] No errors getting notarization info.

   RequestUUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
          Date: ***
    Status: success
        LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets/*********
   Status Code: 0

Status Message: Package Approved

По ссылке LogFileURL вы получите отформатированный ответ сервера JSON:

{
  "logFormatVersion": 1,
  "jobId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "Accepted",
  "statusSummary": "Ready for distribution",
  "statusCode": 0,
  "archiveFilename": "Recaf-2.0.0-liber.dmg",
  "uploadDate": "********************",
  "sha256": "3d742fbfbbf6252a6d35eb23114b387934dac1c0be423b357c0b257026cd1fba",
  "ticketContents": [
     {
     "path": "Recaf-2.0.0-liber.dmg",
     "digestAlgorithm": "SHA-256",
     "cdhash": "56e2fa7437a638721fed896a24880607ffca619d"
     },
     {
     "path": "Recaf-2.0.0-liber.dmg/Recaf.app",
     "digestAlgorithm": "SHA-256",
     "cdhash": "8739a51515aded06148e434b735ddf7147d4821a",
     "arch": "x86_64"
     },
     ...
  ],
  "issues": null
}

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

xcrun stapler staple ./res_dmg/bundles/Recaf-2.0.0-liber.dmg

В результате вы должны получить следующее сообщение:

Processing: *****/res_dmg/bundles/Recaf-2.0.0-liber.dmg
Processing: *****/res_dmg/bundles/Recaf-2.0.0-liber.dmg
The staple and validate actions worked!

Валидация DMG-пакета

Валидацию пакета также можно выполнить отдельно с помощью команды:

xcrun stapler validate ./res_dmg/bundles/Recaf-2.0.0-liber.dmg

Она должна выдать следующий результат:

Processing: *****/res_dmg/bundles/Recaf-2.0.0-liber.dmg
The validate action worked!

Recaf

После успешной валидации вы можете установить приложение.

Создание PKG-пакета

Как было сказано ранее, на данном этапе необходимо выбрать формат файла (DMG или PKG). Расширение PKG представляет комплексную систему средств для установки приложений.

Инструкции по созданию и нотаризации PKG-файлов с помощью JavaPackager аналогичны указаниям для DMG-файлов.

Для генерации пакета PKG с Recaf введите эту команду:

./jdk8u252.jdk-full-nosign/Contents/Home/bin/javapackager -deploy \
  -native pkg \
  -name Recaf \
  -outdir res_pkg \
  -srcfiles ./recaf-2.0.0-J8-jar-with-dependencies.jar \
  -outfile recaf \
  -appclass me.coley.recaf.Recaf \
  -Bruntime=./jdk8u252.jdk-full-nosign/Contents/Home/ \
  -BappVersion=2.0.0-liber

Затем выполните нотаризацию через службу нотаризации Apple:

xcrun altool --notarize-app \
  -primary-bundle-id com.bell-sw.recaf.0 \
  -username ${USERNAME} \
  -password '${YOUR_PASSWORD}' \
  --file ./res_pkg/bundles/Recaf-2.0.0-liber.pkg

В результате вы должны получить следующее сообщение:

*********************** altool[30652:19480464] No errors uploading 'res_pkg/bundles/Recaf-2.0.0-liber.pkg'.
RequestUUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Через некоторое время можете проверить результат:

xcrun altool --notarization-info  ${RequestUUID} \
  -u ${USERNAME} \
  -p '${YOUR_PASSWORD}'

Имейте в виду, что процесс нотаризации службой Apple занимает некоторое время.

По завершении процесса команда выдаст следующее:

*********************** altool[30708:19481150] No errors getting notarization info.
   RequestUUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
          Date: ***********************
    Status: success
     LogFileURL: https://osxapps-ssl.itunes.apple.com/*****
   Status Code: 0

Status Message: Package Approved

По ссылке LogFileURL вы получите сведения о нотаризации своего пакета:

{
  "logFormatVersion": 1,
  "jobId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "Accepted",
  "statusSummary": "Ready for distribution",
  "statusCode": 0,
  "archiveFilename": "Recaf-2.0.0-liber.pkg",
  "uploadDate": "***********************",
  "sha256": "cf6480a8feb54236a9e0a2894a85c130b373a173bc270e661f111e2df4b65e4c",
  "ticketContents": [
     {
     "path": "Recaf-2.0.0-liber.pkg",
     "digestAlgorithm": "SHA-1",
     "cdhash": "22d41fe89a8af1bcd7e594eab94dce942f822874"
     },
     {
     "path": "Recaf-2.0.0-liber.pkg/Recaf-app.pkg Contents/Payload/Applications/Recaf.app/Contents/MacOS/libpackager.dylib",
     "digestAlgorithm": "SHA-256",
     "cdhash": "81ec29cd1ed5c30b1ac0f9aca785e8389d3b0c16",
     "arch": "x86_64"
     },
     ...
 ],
  "issues": null
}

После этого включите сгенерированный тикет в сборку приложения:

xcrun stapler staple ./res_pkg/bundles/Recaf-2.0.0-liber.pkg

Команда должна выдать следующий результат:

Processing: ***/res_pkg/bundles/Recaf-2.0.0-liber.pkg
Processing: ***/res_pkg/bundles/Recaf-2.0.0-liber.pkg
The staple and validate actions worked!

Валидация PKG-пакета

Проверить подпись в файле PKG можно нажатием на иконку блокировки в правом верхнем углу. Подробные сведения приведены во всплывающем диалоговом окне.

Validate

Заключение

Процесс нотаризации приложения с помощью пакета Liberica JDK и JavaPackager очень прост. Выполните описанные действия и помните, что необходимо использовать экземпляр Liberica JDK без подписи. Итоговое приложение будет считаться нотаризованным и будет иметь идентификационные данные, необходимые для соответствия требованиям, предъявляемым компанией Apple. Чтобы получить неподписанную копию Liberica JDK, нажмите на кнопку ниже, оставьте свои данные, и наша служба поддержки с радостью вам поможет.

Author image

Олег Чирухин

Директор по коммуникациям с разработчиками (DevRel)

Команда Axiom JDK roman.karpov@axiomjdk.ru Команда Axiom JDK logo Axiom Committed to Freedom 199 Obvodnogo Kanala Emb. 190020 St. Petersburg RU +7 812-336-35-67 Команда Axiom JDK 199 Obvodnogo Kanala Emb. 190020 St. Petersburg RU +7 812-336-35-67