Утилита keytool, входящая в комплект Java Development Kit (JDK), создаёт пару ключей внутри хранилища ключей Java и преобразует её в CSR (Certificate Signing Request) — закодированный блок, который удостоверяющий центр (CA) считывает, чтобы идентифицировать вас перед выпуском сертификата. Для подписи кода правила легитимного использования keytool изменились 1 июня 2023 года. В этом руководстве сначала излагается правило, а затем рассматриваются команды keytool, которые остаются актуальными: против программного хранилища ключей для внутренней подписи и против аппаратного токена или HSM через PKCS#11.
Ключи для подписи кода должны создаваться на аппаратном обеспечении
С 1 июня 2023 года базовые требования CA/Browser Forum к подписи кода обязывают, чтобы приватный ключ для каждого публично доверенного сертификата подписи кода генерировался и хранился в аппаратном криптографическом модуле, соответствующем стандарту FIPS 140-2 Level 2, Common Criteria EAL4+ или эквивалентному. Ключ должен быть неэкспортируемым. Это применяется как к стандартным сертификатам (с проверкой организации), так и к сертификатам с расширенной проверкой (EV). EV-сертификаты подписи кода всегда требовали аппаратного обеспечения; изменение 2023 года распространило это же правило на стандартные сертификаты. Те же требования устанавливают минимальный размер ключа RSA 3072 для сертификатов подписи кода, действующий с 1 июня 2021 года.
Последствие для данной страницы прямое. Файл хранилища ключей, который keytool создаёт на вашем ноутбуке или сервере, будь то PKCS12 или более старый формат JKS, содержит программный ключ. CSR, сгенерированный из него, не принимается публичным удостоверяющим центром для сертификата подписи кода. Удостоверяющие центры прекратили поддержку генерации ключей в браузере и загружаемых файлов ключей для этих продуктов. Теперь ваш ключ создаётся в одном из двух мест:
- На предварительно настроенном токене, который отправляет вам CA. CA генерирует пару ключей и CSR на сертифицированном USB-токене на своей стороне, загружает на него выпущенный сертификат и отправляет вам этот токен по почте. Вам не нужно создавать CSR самостоятельно.
- На вашем собственном HSM или токене с аттестацией ключа. Вы генерируете ключ внутри устройства, создаёте из него CSR и отправляете файл аттестации, который доказывает CA, что ключ был создан на соответствующем требованиям аппаратном обеспечении и не может быть экспортирован. Требуются обе части; CSR без действительной аттестации отклоняется.
Какой путь применяется, решается при оформлении заказа. Для сравнения см. способы доставки сертификатов подписи кода. Если у вас уже есть соответствующее требованиям аппаратное обеспечение, следуйте руководству для вашего устройства: генерация CSR и аттестация для YubiKey 5 FIPS или руководство по CSR и аттестации для Luna Network Attached HSM v7.x.
Где keytool всё ещё уместен
Ничто из вышесказанного не выводит keytool из употребления. Он остаётся подходящим инструментом в трёх ситуациях, и только первая из них даёт CSR, который можно отправить в публичный CA:
- В качестве интерфейса PKCS#11 к вашему токену или HSM. keytool изначально поддерживает PKCS#11. Укажите ему на библиотеку PKCS#11 вашего производителя, и ключ никогда не покинет устройство, при этом привычная команда -certreq по-прежнему создаёт CSR. Соответствующие команды приведены в разделе про аппаратное обеспечение ниже.
- Для внутренней или корпоративной подписи. Если вы подписываете внутреннее программное обеспечение собственным CA вашей организации, вашей внутренней политикой регулируется хранение ключей, а не публичными базовыми требованиями. Программное хранилище ключей — обычный выбор в этом случае.
- Для тренировки. Правильно указать имя субъекта с первой попытки проще, если вы один раз прошли через подсказки на одноразовом хранилище ключей.
Одно ограничение, о котором стоит знать перед началом: keytool не генерирует аттестацию ключа. Аттестация создаётся собственными инструментами производителя токена или HSM, поэтому даже на пути через PKCS#11 вы будете использовать утилиту производителя для этого файла, а keytool — только для CSR.
Создание CSR с помощью keytool
Если вы уже создали CSR и удостоверяющий центр выпустил сертификат, переходите сразу к импорту ответа CA — шагу, который незаметно завершается неудачей, если псевдоним не совпадает.
keytool поставляется вместе с JDK, поэтому, если у вас его нет, сначала установите актуальную версию JDK. JDK 25 — текущий выпуск с долгосрочной поддержкой, а JDK 26 — текущий краткосрочный выпуск. Приведённые ниже команды работают на JDK 17 и более новых версиях, и различия между версиями, имеющие значение, отмечены там, где они возникают. Убедитесь, что инструмент доступен в PATH:
java -version
keytool -help
jarsigner -version
Одной среды выполнения Java (Java Runtime Environment) недостаточно, а работающий keytool не является доказательством наличия JDK: старая Oracle JRE 8 поставляется с keytool, но без jarsigner. Выполните сейчас все три команды. Если первые две отвечают, а третья нет, значит у вас установлена JRE, и установить JDK сейчас проще, чем обнаружить проблему в момент подписи.
Шаг 1: Создание хранилища ключей и пары ключей
Выполните это в терминале, либо в командной строке или PowerShell в Windows, из каталога, в котором вы хотите хранить файл хранилища ключей:
keytool -genkeypair -alias codesign -keyalg RSA -keysize 3072 -storetype PKCS12 -keystore codesign.p12
Четыре детали в этой команде отличаются от старых инструкций, и каждая из них важна:
- -genkeypair, а не -genkey. Старое написание -genkey по-прежнему работает, и keytool не выдаёт предупреждения об этом, но оно сохранено в исходном коде только как устаревший псевдоним и больше не встречается нигде в документации JDK. Пишите -genkeypair.
- -storetype PKCS12, а не JKS. JKS — это проприетарный формат хранилища ключей от Oracle. PKCS12 — отраслевой стандарт, являющийся форматом по умолчанию в JDK начиная с Java 9. Если вы всё же создадите хранилище JKS, keytool будет предупреждать вас при каждой команде, обращающейся к нему: «The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format.»
- Имя файла не определяет формат. Присвоение файлу имени keystore.jks не делает его хранилищем JKS. keytool определяет тип из -storetype, либо из свойства keystore.type в файле безопасности JDK, если параметр не указан. В JDK 9 и более новых версиях это свойство имеет значение pkcs12, поэтому команда, записывающая данные в keystore.jks без указания -storetype, незаметно создаст файл PKCS12 с вводящим в заблуждение именем.
- -keysize 3072, а не 2048. Базовые требования устанавливают RSA 3072 как минимум для подписи кода, поэтому запрос с 2048-битным ключом будет отклонён. Текущие версии JDK по умолчанию используют 3072 бита для RSA, но JDK 17 и более ранние версии по умолчанию используют 2048 бит, поэтому передавайте параметр явно, чтобы команда вела себя одинаково везде.
Псевдоним, codesign в примере, — это метка данной записи внутри хранилища ключей. Выберите что-то узнаваемое и запишите это: каждая последующая команда нуждается в нём, а несовпадение — это то, что ломает импорт сертификата, описанный далее.
Также допускается ключ ECDSA. Замените параметры RSA на -keyalg EC -groupname secp256r1, если вы предпочитаете P-256, и уточните у вашего CA, поддерживает ли заказанный продукт ECDSA.
Шаг 2: Ответьте на подсказки, формирующие ваше отличительное имя
keytool дважды запрашивает пароль хранилища ключей, а затем задаёт шесть вопросов в строго определённом порядке. Порядок важен: второй запрос спрашивает об организационном подразделении, а не об организации, и в старых руководствах это перечислено неверно, из-за чего читатель, вводящий там название компании, помещает его в неправильный компонент имени.
- Каково ваше имя и фамилия (What is your first and last name)? Это становится Common Name (CN), несмотря на формулировку. Для сертификата подписи кода CN — это идентичность издателя, которую увидят пользователи, поэтому введите точное юридическое название вашей организации либо ваше полное юридическое имя для индивидуального сертификата. Не вводите здесь доменное имя.
- Каково название вашего организационного подразделения (organizational unit)? Отдел, например, ИТ. Не нажимайте Enter, чтобы пропустить этот пункт: keytool в этом случае запишет буквальное значение Unknown в имя, и OU=Unknown окажется в вашем CSR. Если вы вообще не хотите указывать организационное подразделение, используйте форму с -dname ниже и не включайте компонент OU в строку.
- Каково название вашей организации? Зарегистрированное юридическое название, написанное так, как оно указано в официальных документах. CA проверяет это по публичным реестрам.
- Каково название вашего города или населённого пункта? Город регистрации, указанный полностью.
- Каково название вашего штата или региона? Полное название, а не сокращение.
- Каков двухбуквенный код страны для этого подразделения? Код ISO, например, US.
Затем keytool выводит собранное имя и просит подтвердить его:
Is CN=Example LLC, OU=IT, O=Example LLC, L=Miami, ST=Florida, C=US correct?
[no]:
Ответ по умолчанию — no, поэтому нажатие Enter отправит вас заново через все шесть вопросов. Введите yes, чтобы принять. Сначала внимательно прочитайте эту строку: это всё отличительное имя целиком, а не только Common Name, и каждый его компонент попадает в CSR.
В хранилище PKCS12 нет отдельного пароля для ключа. Старые руководства завершают этот шаг фразой «введите пароль для ключа», что соответствует поведению JKS. В хранилище PKCS12 пароль ключа совпадает с паролем хранилища, и если вы передадите -keypass с другим значением, keytool сообщит вам об этом: «Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -keypass value.»
Чтобы полностью пропустить подсказки, укажите всё имя целиком через -dname. Держите значение в одинарной паре прямых кавычек:
keytool -genkeypair -alias codesign -keyalg RSA -keysize 3072 -storetype PKCS12 -keystore codesign.p12 -dname "CN=Example LLC, OU=IT, O=Example LLC, L=Miami, ST=Florida, C=US"
Не указывайте -storepass в командной строке и позвольте keytool запросить его отдельно. Передача пароля в качестве аргумента записывает его в историю вашей оболочки и делает его доступным для всех, кто может просматривать список выполняемых процессов.
Шаг 3: Создание CSR
Теперь хранилище ключей содержит приватный ключ и временный самоподписанный сертификат. Преобразуйте эту запись в запрос на сертификат:
keytool -certreq -alias codesign -keystore codesign.p12 -file codesign.csr
Введите пароль хранилища ключей, когда будет запрошено. Здесь не требуется указывать -storetype: keytool определяет формат уже существующего файла хранилища ключей. Псевдоним должен совпадать с тем, что был использован на шаге 1, поскольку CSR подписывается приватным ключом этой записи.
Эта команда не создаёт приватный ключ. Ключ был создан на шаге 1 и остаётся внутри хранилища ключей, поэтому файл хранилища и его пароль теперь так же чувствительны, как и сам ключ. Любой, кто владеет обоими этими элементами, может подписывать программное обеспечение от вашего имени.
Шаг 4: Проверьте CSR перед отправкой
Отклонённый CSR обходится в целый цикл проверки, поэтому расшифруйте его и перечитайте:
keytool -printcertreq -file codesign.csr
Проверьте три вещи в выводе. Строка Subject должна содержать ваши данные в правильных компонентах, с юридическим названием организации в O и идентичностью издателя в CN. Строка публичного ключа должна показывать 3072-bit RSA key или больше. Алгоритм подписи должен быть алгоритмом семейства SHA-2: текущие версии JDK подписывают 3072-битный запрос RSA с помощью SHA384withRSA, а более старые используют SHA256withRSA, и оба варианта допустимы. Этот алгоритм лишь доказывает, что вы владеете приватным ключом, и не является алгоритмом, который CA будет использовать для подписи вашего сертификата.
Вы также можете вставить запрос в наш декодер CSR, чтобы прочитать те же поля в браузере.
При отправке запроса откройте файл в обычном текстовом редакторе и скопируйте всё, включая первую и последнюю строки. keytool записывает эти точные маркеры, с пятью дефисами с каждой стороны:
-----BEGIN NEW CERTIFICATE REQUEST-----
MIID3TCCAkUCAQAwaDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0Zsb3JpZGExDjAM
...base64 encoded request...
-----END NEW CERTIFICATE REQUEST-----
Формулировка NEW CERTIFICATE REQUEST нормальна для вывода keytool, и формы регистрации принимают её. Если ваш редактор заменил какую-либо последовательность дефисов на длинное тире, запрос будет отклонён: перепечатайте маркеры обычными дефисами или скопируйте файл с помощью редактора кода.
Создание CSR на токене или HSM с помощью keytool
Это путь, который даёт CSR, готовый к обработке публичным CA. keytool взаимодействует с аппаратным токеном через провайдер SunPKCS11, поэтому пара ключей создаётся внутри устройства и никогда не существует в виде файла. Начните с написания небольшого конфигурационного файла, например token.cfg, который называет ваш токен и указывает на библиотеку PKCS#11, установленную вашим производителем:
name = token
library = /usr/local/lib/libeToken.so
Эти две строки, name и library, — единственные обязательные. Путь к библиотеке зависит от производителя и различается в зависимости от операционной системы, поэтому берите его из документации к вашему токену, а не из этого примера. В Windows это DLL в системном каталоге. Без строки slot провайдер подключается к первому слоту, о котором сообщает устройство, что подходит, когда подключён один токен. Если у вас несколько считывателей или токенов, добавьте либо slotListIndex с позицией в этом списке, отсчитываемой от нуля, либо slot с числовым идентификатором слота, который выводит утилита вашего производителя. В файле может присутствовать только один из этих двух параметров, и идентификатор слота — это не то же самое число, что позиция в списке, так что не угадывайте наугад со slot = 0.
Выведите список того, что находится на устройстве. Токен предоставляет псевдоним, поэтому это нужно сделать в первую очередь:
keytool -list -keystore NONE -storetype PKCS11 -providerClass sun.security.pkcs11.SunPKCS11 -providerArg token.cfg
-keystore NONE требуется всегда, когда хранилище ключей не является файлом, а запрос пароля означает запрос PIN-кода токена. keytool также принимает -addprovider SunPKCS11 -providerarg token.cfg вместо пары -providerClass и -providerArg; обе формы работают, и документация удостоверяющих центров обычно показывает более старую.
Имея псевдоним, сгенерируйте ключ на устройстве, а затем запросите для него CSR:
keytool -genkeypair -alias codesign -keyalg RSA -keysize 3072 -keystore NONE -storetype PKCS11 -providerClass sun.security.pkcs11.SunPKCS11 -providerArg token.cfg -dname "CN=Example LLC, O=Example LLC, L=Miami, ST=Florida, C=US"
keytool -certreq -alias codesign -keystore NONE -storetype PKCS11 -providerClass sun.security.pkcs11.SunPKCS11 -providerArg token.cfg -file codesign.csr
Здесь есть две оговорки. Некоторые токены не позволяют генерацию ключа через PKCS#11 и требуют использования собственной утилиты производителя, что не является проблемой: keytool всё равно может создать CSR для ключа, сгенерированного инструментом производителя. И keytool не может создать файл аттестации, который запросит ваш CA, поэтому создавайте его с помощью инструментов производителя одновременно с созданием ключа, следуя руководству по YubiKey или Luna HSM.
Импорт ответа CA в тот же псевдоним
Когда сертификат приходит, его нужно вернуть в ту же запись, которая сгенерировала CSR. Импортируйте его куда-либо ещё, и keytool всё равно сообщит об успехе, при этом создав хранилище ключей, которое не может подписывать, поэтому прочитайте этот раздел, прежде чем что-либо запускать.
Сначала импортируйте корневой сертификат CA и все промежуточные сертификаты, каждый под своим собственным псевдонимом:
keytool -importcert -trustcacerts -alias caroot -file root.crt -keystore codesign.p12
keytool выводит сертификат, который собирается сохранить, и спрашивает Trust this certificate? с ответом по умолчанию no, поэтому введите yes. Перед тем как ответить, сверьте отпечаток с тем, который публикует ваш удостоверяющий центр.
Пропустите импорт корневого сертификата, и следующая команда завершится ошибкой, сообщение которой не даёт никакого намёка на причину:
keytool error: java.lang.Exception: Failed to establish chain from reply
Теперь импортируйте выпущенный сертификат, используя тот же псевдоним, что и на шаге 1:
keytool -importcert -alias codesign -file codesign.crt -keystore codesign.p12
Нужное вам сообщение — Certificate reply was installed in keystore. Это означает, что keytool распознал существующий приватный ключ под этим псевдонимом и привязал к нему выпущенный сертификат вместе с его цепочкой.
Если вместо этого вы придумаете новый псевдоним, keytool примет файл и выведет Certificate was added to keystore. Это выглядит как успех, но им не является. keytool сохранил сертификат как отдельную доверенную запись без стоящего за ней приватного ключа, и эта запись никогда не сможет ничего подписать. Тем временем исходный псевдоним по-прежнему хранит временный самоподписанный сертификат с шага 1. Проверьте, какой из двух вариантов у вас есть:
keytool -list -keystore codesign.p12
Ваш псевдоним для подписи должен быть указан как PrivateKeyEntry. Псевдоним, показанный как trustedCertEntry, — это ошибка, описанная выше. Удалите его командой keytool -delete -alias wrongalias -keystore codesign.p12 и повторите импорт с правильным псевдонимом. Добавьте -v к команде list, чтобы убедиться, что запись теперь содержит полную цепочку сертификатов, а не одиночный самоподписанный сертификат.
На аппаратном токене в большинстве случаев импортировать нечего, поскольку CA загружает сертификат на устройство перед его отправкой. Если ваш CA отправляет вам файл сертификата для ключа, который вы сгенерировали на собственном HSM, используйте ту же команду -importcert с параметрами PKCS#11 из предыдущего раздела.
Подпись JAR-файла с помощью сертификата
Смысл размещения сертификата подписи кода в хранилище ключей Java заключается в подписи JAR-файлов с помощью jarsigner, который также поставляется вместе с JDK. Когда сертификат установлен под правильным псевдонимом:
jarsigner -keystore codesign.p12 -tsa https://your-ca-timestamp-url application.jar codesign
Всегда передавайте -tsa с URL-адресом временных меток, который публикует ваш удостоверяющий центр. Временная метка фиксирует, что JAR был подписан, пока сертификат ещё был действителен, поэтому подпись продолжает работать даже после истечения срока действия сертификата. Без неё каждая копия вашего программного обеспечения перестанет проходить проверку в тот день, когда истечёт срок действия сертификата.
Когда ключ находится на токене, укажите jarsigner на PKCS#11 точно так же, как это делает keytool:
jarsigner -keystore NONE -storetype PKCS11 -providerClass sun.security.pkcs11.SunPKCS11 -providerArg token.cfg -tsa https://your-ca-timestamp-url application.jar codesign
Если на токене недостаточно места для полной цепочки сертификатов, укажите её отдельно с помощью -certchain. Проверьте результат после этого:
jarsigner -verify -verbose -certs application.jar
Успешное выполнение выводит jar verified вместе с отличительным именем подписавшего и деталями временной метки. Текущие версии JDK используют SHA-384 в качестве алгоритма дайджеста по умолчанию, поэтому вам редко нужно вручную задавать -digestalg или -sigalg.
Другие способы создания этого запроса рассмотрены в руководствах по OpenSSL, CertReq, Microsoft Management Console и macOS Keychain Access, и то же правило об аппаратном обеспечении применяется ко всем им. См. также наш полный набор руководств по подписи кода и обзор генерации CSR для сертификатов подписи кода. Если вам нужен CSR через keytool для сертификата веб-сайта, а не для подписи кода, следуйте вместо этого руководству по Tomcat или JBoss.
Часто задаваемые вопросы
Только если keytool сгенерировал ключ внутри аппаратного токена или HSM через PKCS#11 и вы можете предоставить аттестацию, которую запрашивает удостоверяющий центр. CSR, созданный из обычного файла хранилища ключей на вашем компьютере, представляет собой программный ключ, а с 1 июня 2023 года публичные CA не выпускают сертификаты подписи кода для программных ключей. Программные хранилища ключей по-прежнему подходят для подписи с помощью внутреннего или корпоративного CA.
Используйте -genkeypair. Написание -genkey — это устаревший псевдоним, который JDK по-прежнему принимает без каких-либо предупреждений, но он отсутствует в документации уже много релизов. Сегодня оба варианта делают одно и то же; задокументирован только один из них.
Используйте PKCS12. Это отраслевой стандартный формат, и он является типом хранилища ключей по умолчанию в JDK начиная с Java 9, тогда как JKS является проприетарным форматом Oracle и заставляет keytool выводить предупреждение о миграции при каждой команде. Расширение файла вообще не имеет значения: keytool определяет формат из параметра -storetype, либо из свойства безопасности keystore.type, если он опущен, поэтому файл с именем keystore.jks, созданный в современной JDK, обычно является файлом PKCS12. Чтобы преобразовать существующее хранилище ключей, выполните keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.p12 -deststoretype pkcs12.
RSA размером 3072 бита или больше, что требуется базовыми требованиями к подписи кода с 1 июня 2021 года, либо эквивалентный ключ ECDSA, например P-256. Указывайте -keysize 3072 явно, поскольку JDK 17 и более ранние версии по умолчанию используют 2048 бит, и такой запрос будет отклонён.
Вы почти наверняка импортировали ответ CA в новый псевдоним, а не в тот псевдоним, который сгенерировал CSR. В этом случае keytool сохраняет его как отдельный доверенный сертификат без привязанного приватного ключа и выводит сообщение Certificate was added to keystore, которое выглядит как успех. Выполните keytool -list -keystore codesign.p12: псевдоним для подписи должен отображаться как PrivateKeyEntry, а не trustedCertEntry. Удалите неправильную запись и повторите импорт с исходным псевдонимом — вы должны увидеть сообщение Certificate reply was installed in keystore.
keytool не может построить путь от вашего выпущенного сертификата до сертификата, которому он уже доверяет. Сначала импортируйте корневой и промежуточные сертификаты CA в то же хранилище ключей, каждый под своим собственным псевдонимом с помощью -importcert -trustcacerts, а затем импортируйте свой сертификат снова. В качестве альтернативы импортируйте единый файл, содержащий ваш сертификат, за которым следуют промежуточные сертификаты и корневой сертификат.
Нет. Сертификат подписи кода определяется его расширенным использованием ключа для подписи кода, а не набором инструментов, поэтому один и тот же сертификат подписывает JAR-файлы с помощью jarsigner и исполняемые файлы Windows с помощью signtool. Что вы действительно выбираете при заказе, так это способ доставки, который определяет, отправит ли вам CA предварительно настроенный токен, или вы сгенерируете ключ на уже имеющемся у вас аппаратном обеспечении.
С токеном или HSM ключ находится внутри устройства и не может быть скопирован — в этом весь смысл требования. С программным хранилищем ключей, используемым для внутренней подписи, ключ находится в файле хранилища, поэтому файл и его пароль вместе являются секретом: храните их в защищённом месте, никогда не передавайте ни то, ни другое в систему контроля версий и позвольте keytool запрашивать пароль вместо передачи -storepass в командной строке, где он попадёт в историю вашей оболочки.
Сэкономьте 10% на SSL-сертификатах при заказе сегодня!
Быстрая выдача, надежное шифрование, 99,99% доверия к браузеру, специализированная поддержка и 25-дневная гарантия возврата денег. Код купона: SAVE10

