CSR: что это, как сгенерировать запрос на сертификат в OpenSSL и не потерять приватный ключ
Платный или корпоративный SSL-сертификат начинается с CSR — файла, который вы отдаёте удостоверяющему центру. Разбираем, что в нём лежит, почему без SAN сертификат не примет ни один браузер, как сгенерировать запрос одной командой OpenSSL для RSA и ECDSA, что делать в Windows и панелях хостинга, как проверить CSR перед отправкой и как после выпуска собрать цепочку и поставить сертификат в nginx или Apache.
CSR (Certificate Signing Request, запрос на подпись сертификата) — файл в формате PKCS#10 по RFC 2986, в котором лежат публичный ключ, имена доменов и данные владельца, и который подписан парным приватным ключом. CSR отправляют в удостоверяющий центр, а приватный ключ, созданный вместе с ним, никуда не уходит и остаётся на сервере. Из CSR УЦ делает сертификат, который работает только в паре с тем самым ключом: потеряли ключ — сертификат придётся перевыпускать.
Что находится внутри CSR и что из этого уходит в удостоверяющий центр
По RFC 2986 запрос состоит из трёх частей: блока CertificationRequestInfo (версия — всегда 0, имя субъекта, публичный ключ и атрибуты), идентификатора алгоритма подписи и самой подписи. Подпись сделана парным приватным ключом — так УЦ убеждается, что ключ у вас есть; право на домен она не доказывает, его УЦ проверяет отдельно через DNS-запись, файл на сайте или письмо.
Домены живут не в имени субъекта, а в атрибуте extensionRequest из PKCS#9 — там перечислены расширения будущего сертификата, прежде всего subjectAltName. Всё содержимое CSR публично: приватный ключ в файл не попадает ни в каком виде. Сводка полей на сентябрь 2026 по CA/Browser Forum Baseline Requirements (версия 2.3.0 от 7 сентября 2026):
| Поле | Пример | Статус по Baseline Requirements | Типичная ошибка |
|---|---|---|---|
| SAN (subjectAltName) | DNS:example.com, DNS:www.example.com | Обязателен, минимум одно имя DNS или IP | Не указан вовсе — браузер покажет ERR_CERT_COMMON_NAME_INVALID |
| CN (commonName) | example.com | Не рекомендован; если есть — ровно одно имя, скопированное из SAN; до 64 символов | Кириллица вместо punycode; www забыт |
| O (organizationName) | Romashka LLC | OV/EV: обязательно, сверяется с реестром; DV: запрещено | Название не совпадает с ЕГРЮЛ — проверка OV затягивается |
| OU (organizationalUnitName) | IT Department | Запрещено с ballot SC047 (2021), УЦ вырежет | Заполнен по старой инструкции |
| L / ST (город / регион) | Moscow / Moscow | OV: хотя бы одно из двух, до 128 символов; DV: запрещено | Сокращения «Msk», «MO» |
| C (countryName) | RU | Ровно две буквы ISO 3166-1 | «RUS», «Russia» |
| Публичный ключ | RSA 2048 или EC P-256 | RSA от 2048 бит, кратно 8; ECDSA только P-256, P-384, P-521 | RSA 1024 из старого скрипта — отказ УЦ |
| Подпись запроса | sha256WithRSAEncryption | SHA-256 и выше | default_md = sha1 в древнем openssl.cnf |
Практический вывод: для DV-сертификата (проверяется только домен) в CSR достаточно SAN и, по традиции, CN — остальное УЦ выбросит. Для OV и EV поля O, L или ST и C обязательны и должны совпадать с реестром юрлиц.
Почему CN без SAN не работает и как правильно перечислить домены
Браузеры сверяют имя сайта только со списком subjectAltName; поле CN для этого не используется. Chrome отключил сопоставление по CN в версии 58 (апрель 2017): RFC 2818 объявил CN устаревшим ещё в 2000 году, а Baseline Requirements с 2012 года требуют SAN во всех публичных сертификатах. Действующие BR формулируют жёстко: SAN обязателен, CN «не рекомендован», а если есть — обязан быть точной копией одного из имён SAN.
Отсюда правила заполнения. Голый домен и www — два разных имени, оба нужно перечислить явно. Wildcard *.example.com покрывает ровно один уровень: shop.example.com — да, example.com и a.b.example.com — нет. Кириллические домены записываются только в punycode (пример.рф → xn--e1afmkfd.xn--p1ai). IP-адрес в SAN допустим, но только публичный: зарезервированные диапазоны УЦ не подпишет. Лимит на число имён у каждого УЦ свой; у Let's Encrypt, по его документации, — до 100 идентификаторов в одном сертификате.
| Что нужно закрыть | Что писать в SAN | Что писать в CN | Примечание |
|---|---|---|---|
| Сайт с www и без | DNS:example.com, DNS:www.example.com | example.com | Многие УЦ добавляют www бесплатно, но в CSR лучше указать самому |
| Все поддомены первого уровня | DNS:*.example.com, DNS:example.com | *.example.com | Wildcard не покрывает голый домен — добавляйте отдельно |
| Несколько разных сайтов | DNS:romashka.ru, DNS:romashka.com, DNS:shop.romashka.ru | romashka.ru | Тариф multi-domain (SAN/UCC); каждое имя проходит свою проверку |
| Кириллический домен | DNS:xn--e1afmkfd.xn--p1ai | xn--e1afmkfd.xn--p1ai | Только punycode; Unicode в CSR — отказ или битый сертификат |
Как сгенерировать CSR в OpenSSL одной командой: RSA и ECDSA
Ключ и запрос создаются одной командой openssl req с флагом -newkey; SAN добавляется флагом -addext без правки конфигов. Так это выглядит в OpenSSL 3.x (флаг -noenc появился в 3.0; в OpenSSL 1.1.1 вместо него пишите -nodes — в 3.x он объявлен устаревшим, но работает):
# RSA 2048: ключ без пароля + CSR с двумя именами в SAN openssl req -new -newkey rsa:2048 -noenc \ -keyout example.com.key -out example.com.csr \ -subj "/C=RU/ST=Moscow/L=Moscow/O=Romashka LLC/CN=example.com" \ -addext "subjectAltName=DNS:example.com,DNS:www.example.com" # ECDSA P-256: то же, но ключ на эллиптической кривой openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -noenc \ -keyout example.com.ec.key -out example.com.ec.csr \ -subj "/C=RU/O=Romashka LLC/CN=example.com" \ -addext "subjectAltName=DNS:example.com,DNS:www.example.com" # Ключ читает только root chmod 600 example.com.key example.com.ec.key
Файл .csr — блок между строками -----BEGIN CERTIFICATE REQUEST----- и -----END CERTIFICATE REQUEST-----; его целиком, с обеими строками-ограничителями, вставляют в форму УЦ. Файл .key остаётся на сервере: если форма УЦ просит «загрузить ключ», это повод закрыть вкладку. Когда имён много, вынесите всё в конфиг — его удобно хранить в репозитории и повторять при перевыпуске.
# san.cnf [ req ] default_bits = 3072 default_md = sha256 prompt = no distinguished_name = dn req_extensions = ext [ dn ] C = RU ST = Moscow L = Moscow O = Romashka LLC CN = example.com [ ext ] subjectAltName = @alt [ alt ] DNS.1 = example.com DNS.2 = www.example.com DNS.3 = shop.example.com DNS.4 = api.example.com # Генерация по конфигу: ключ RSA 3072 возьмётся из default_bits openssl req -new -noenc -config san.cnf \ -keyout example.com.key -out example.com.csr
Какой ключ выбирать — RSA или ECDSA. Оба разрешены Baseline Requirements (раздел 6.1.5): RSA от 2048 бит, ECDSA на кривых P-256, P-384 и P-521; другого публичные УЦ не подпишут. Разница — в размере и скорости при равной стойкости (значения — из таблицы сопоставимых длин ключей NIST SP 800-57 Part 1):
| Параметр | RSA 2048 | RSA 3072 | ECDSA P-256 |
|---|---|---|---|
| Стойкость по NIST, бит | 112 | 128 | 128 |
| Размер публичного ключа | 256 байт | 384 байт | 64 байта |
| Размер подписи | 256 байт | 384 байт | около 70 байт |
| Совместимость с клиентами | Все, включая устаревшие | Все | Все современные; нет у Windows XP, Android до 4.x, Java 6 |
| Когда выбирать | Максимально широкая аудитория, старые кассы и терминалы | Требование 128 бит при обязательном RSA | Обычный сайт или API в 2026 году, мобильный трафик |
Компромисс — два сертификата сразу: nginx с версии 1.11.0 принимает несколько пар ssl_certificate / ssl_certificate_key и отдаёт ECDSA современным клиентам, а RSA — старым; для этого делают два CSR на один набор имён.
Как сделать CSR в Windows и панелях хостинга и где он не нужен вообще
В Windows штатный инструмент — certreq с INF-файлом: ключ создаётся в хранилище компьютера, SAN задаётся в секции [Extensions]. Мастер «Создать запрос сертификата» в IIS Manager проще, но SAN не добавляет, поэтому пользуйтесь certreq:
; request.inf
[Version]
Signature = "$Windows NT$"
[NewRequest]
Subject = "CN=example.com, O=Romashka LLC, L=Moscow, S=Moscow, C=RU"
KeyLength = 2048
KeyAlgorithm = RSA
HashAlgorithm = sha256
Exportable = TRUE
MachineKeySet = TRUE
KeySpec = 1
RequestType = PKCS10
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "dns=example.com&"
_continue_ = "dns=www.example.com"
:: В cmd от администратора: создать запрос
certreq -new request.inf example.com.csr
:: После выпуска: привязать сертификат к ключу в хранилище
certreq -accept example.com.cer
Ключ при этом лежит не в файле, а в хранилище сертификатов компьютера; на другой сервер его переносят экспортом в PFX через certlm.msc (нужен Exportable = TRUE, заданный при создании). В панелях хостинга логика та же: cPanel в разделе SSL/TLS требует сначала создать приватный ключ и только потом CSR; ISPmanager делает запрос в разделе «SSL-сертификаты» с типом «запрос на сертификат». Ключ хранит панель, в неё же вставляют выпущенный сертификат; без экспорта на другой сервер он не уедет.
Есть сценарий, где CSR вы вообще не видите: протокол ACME. Клиент вроде certbot или acme.sh сам генерирует ключ, собирает CSR, проходит проверку домена и ставит сертификат. Так работают Let's Encrypt и другие бесплатные DV-центры — подробно в статье про Let's Encrypt. Обратная сторона: срок 90 дней (по желанию — 6) и только DV, без OV и EV.
| Способ | Где создаётся ключ | Где он потом лежит | SAN | Кому подходит |
|---|---|---|---|---|
| openssl req | На вашем сервере | Файл .key рядом с CSR | -addext или конфиг | Linux, BSD, macOS; скрипты и CI |
| certreq | Windows, хранилище ключей | Хранилище компьютера, экспорт в PFX | Секция [Extensions] | IIS, Exchange, RDP-шлюзы |
| cPanel, ISPmanager | На хостинге | Внутри панели | Список доменов в форме | Виртуальный хостинг; панель сама ставит сертификат |
| Форма УЦ «сгенерировать за меня» | В браузере или у УЦ | Скачивается файлом один раз | Да | Когда сервера ещё нет; ключ путешествует по почте — худший вариант |
| ACME-клиент | На вашем сервере, автоматически | Каталог клиента, например /etc/letsencrypt/live/ | Да, по списку -d | Бесплатный DV с автопродлением |
Как проверить CSR перед отправкой в УЦ
Одна команда показывает всё содержимое запроса и проверяет его подпись: openssl req -text -noout -verify. Смотреть нужно на четыре строки — Subject, Public-Key, Subject Alternative Name и Signature Algorithm; если что-то не так, перегенерируйте запрос до отправки: после выпуска исправление — это перевыпуск.
openssl req -in example.com.csr -text -noout -verify
# пример вывода (OpenSSL 3.x)
Certificate request self-signature verify OK
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = RU, ST = Moscow, O = Romashka LLC, CN = example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.com
Signature Algorithm: sha256WithRSAEncryption
# Убедиться, что CSR сделан именно этим ключом
openssl req -in example.com.csr -noout -pubkey | sha256sum
openssl pkey -in example.com.key -pubout | sha256sum
# пример вывода: хеши совпадают
3f1a9c...e07d -
3f1a9c...e07d -
«Version: 1 (0x0)» — норма: в PKCS#10 номер версии равен 0, OpenSSL показывает его как v1. В OpenSSL 1.1.1 первая строка короче — «verify OK»; с версии 3.3 при неверной подписи команда завершается с кодом 1, что удобно в скриптах. Онлайн-декодеры CSR использовать можно: запрос содержит только публичные данные. Опасна соседняя кнопка «сгенерировать CSR онлайн» — она создаёт ключ в чужом браузере или на чужом сервере, а это единственное, чего с ключом делать нельзя.
Что делать после выпуска: цепочка, проверка ключа и установка в nginx и Apache
УЦ присылает сам сертификат и один или несколько промежуточных (ca-bundle); сервер должен отдавать их вместе, ваш сертификат — первым. Без промежуточных на компьютере, где они уже закешированы, всё откроется, а на телефонах посетители увидят ERR_CERT_AUTHORITY_INVALID. Корневой сертификат в файл добавлять не нужно — он и так есть у клиента.
# Собрать цепочку: сначала ваш сертификат, затем промежуточные cat example.com.crt ca-bundle.crt > fullchain.pem # Убедиться, что сертификат подходит к ключу (RSA и ECDSA) openssl x509 -in example.com.crt -noout -pubkey | sha256sum openssl pkey -in example.com.key -pubout | sha256sum # Классический способ только для RSA: сравнить modulus openssl x509 -in example.com.crt -noout -modulus | openssl md5 openssl rsa -in example.com.key -noout -modulus | openssl md5 # Срок и имена в полученном сертификате openssl x509 -in example.com.crt -noout -dates -ext subjectAltName
Дальше — конфиг веб-сервера. В nginx две директивы; в Apache 2.4.8 и новее промежуточные сертификаты кладут в тот же файл SSLCertificateFile, а SSLCertificateChainFile объявлен устаревшим:
# nginx
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
}
# проверить синтаксис и перечитать конфиг без остановки
nginx -t && systemctl reload nginx
# Apache 2.4.8+: SSLCertificateFile /etc/ssl/example.com/fullchain.pem
# SSLCertificateKeyFile /etc/ssl/example.com/example.com.key
# Проверка снаружи: цепочка и код верификации
openssl s_client -connect example.com:443 -servername example.com \
</dev/null 2>/dev/null | grep -E "s:|i:|Verify return"
# пример вывода: ваш сертификат, промежуточный, код 0
0 s:CN = example.com
i:C = US, O = Example CA, CN = Example CA DV R3
1 s:C = US, O = Example CA, CN = Example CA DV R3
Verify return code: 0 (ok)
Если перепутать порядок в fullchain.pem, nginx не стартует с ошибкой «SSL_CTX_use_PrivateKey_file … key values mismatch»: он прикладывает ваш ключ к первому сертификату в файле, а это промежуточный. Та же ошибка при верном порядке означает, что ключ и сертификат из разных пар. Остальные поля s_client разобраны в статье о проверке SSL-сертификата сайта.
Отдельный вопрос — как часто всё это повторять. Максимальный срок сертификата задают Baseline Requirements (раздел 6.3.2); ballot SC-081v3, принятый в апреле 2025 года, запустил его сокращение по расписанию, и на сентябрь 2026 действует уже второй этап (срок повторного использования проверки домена сокращается синхронно, а с 2029 года падает до 10 дней):
| Сертификат выпущен | Максимальный срок | Что это значит для CSR |
|---|---|---|
| До 15.03.2026 | 398 дней | Один CSR в год |
| 15.03.2026 – 14.03.2027 (сейчас) | 200 дней | Два выпуска в год; годовой тариф УЦ — два сертификата подряд |
| 15.03.2027 – 14.03.2029 | 100 дней | Четыре выпуска в год |
| С 15.03.2029 | 47 дней | Восемь выпусков в год; ручной CSR нереалистичен — нужна автоматизация через ACME или API УЦ |
Типичные ошибки: потерянный ключ, чужой сервер, пароль на ключе и старый сертификат
Почти все проблемы после выпуска сводятся к одному: сертификат и ключ разъехались — ключ не там, его нет или сервер не может его прочитать. Восстановить ключ из сертификата или CSR невозможно; лечится это новым CSR и перевыпуском, который у большинства УЦ бесплатен в течение оплаченного срока.
| Симптом | Причина | Что делать |
|---|---|---|
| Сертификат пришёл, а файла .key нет | CSR делали на другом сервере, в панели, в форме УЦ или на ноутбуке, который переустановили | Забрать ключ оттуда, где он создан (экспорт PFX, панель); не нашли — новый CSR и перевыпуск |
| nginx: «key values mismatch» | Ключ от другой пары или в fullchain первым идёт промежуточный сертификат | Сравнить хеши -pubkey (команды выше); проверить порядок в fullchain.pem |
| После перезагрузки сайт не поднялся, в логе «Enter PEM pass phrase» | Ключ зашифрован паролем, автозапуск ждёт ввода | Снять пароль: openssl pkey -in enc.key -out plain.key; либо ssl_password_file в nginx (с 1.7.3) или SSLPassPhraseDialog exec: в Apache |
| Сертификат заменили, браузер показывает старый | Конфиг не перечитан; другой server-блок отвечает первым; CDN или балансировщик перед сервером со своим сертификатом | nginx -T | grep ssl_certificate; reload; s_client с -servername; обновить сертификат на CDN отдельно |
Как сгенерировать CSR и установить сертификат: пошагово
Порядок для Linux с nginx или Apache; в Windows шаги те же, меняются инструменты.
- Составьте полный список имён: голый домен, www, поддомены или wildcard, кириллические домены в punycode. Выберите тип ключа: ECDSA P-256 для нового сайта, RSA 2048 для аудитории со старыми устройствами.
- На сервере, где будет работать сайт, выполните openssl req -new -newkey … -noenc -keyout -out с -subj и -addext subjectAltName или с конфигом san.cnf; сразу сделайте chmod 600 на ключ.
- Проверьте запрос командой openssl req -text -noout -verify: подпись OK, длина ключа, все имена в Subject Alternative Name, подпись sha256.
- Вставьте содержимое .csr вместе со строками BEGIN и END в форму УЦ и пройдите проверку владения доменом: DNS-запись, файл по HTTP или письмо.
- Получив сертификат и промежуточные, соберите fullchain.pem (сначала ваш сертификат) и сверьте хеш публичного ключа сертификата с хешем ключа.
- Пропишите пути в ssl_certificate и ssl_certificate_key (nginx) или SSLCertificateFile и SSLCertificateKeyFile (Apache 2.4.8+), проверьте конфиг и сделайте reload.
- Проверьте снаружи: openssl s_client -servername должен показать цепочку и Verify return code 0; если перед сервером CDN или балансировщик, загрузите сертификат и туда.
- Заведите автоматический контроль срока: при действующем лимите 200 дней следующий CSR понадобится через полгода, а с 2027 года — каждые три месяца.
Как не пропустить истечение и убедиться, что новый сертификат виден отовсюду
Разовая проверка s_client показывает состояние сертификата на одном сервере в одну минуту. Она не скажет, что срок кончится в субботу или что один из узлов CDN всё ещё отдаёт старый сертификат, — для этого нужна проверка по расписанию из разных сетей.
Пруфен выполняет проверку SSL-сертификата с интервалом от суток из 20 точек мониторинга — 17 в России, а также Минск, Астана и Испания: контролирует срок действия, соответствие имени и цепочку, а об истечении предупреждает за заданное число дней (параметр expiration_notify_before_days есть и в REST API). Рядом стоит проверка домена: истёкшая регистрация роняет сайт так же надёжно, как истёкший сертификат. После замены сертификата HTTP-проверка из тех же 20 точек с интервалом от 2 минут покажет во вкладке «По локациям», что каждый регион и каждый узел CDN получает рабочий HTTPS.
Пруфен предупредит заранее об истечении сертификата и сообщит в Email, Telegram или Макс, если после замены сайт перестал открываться по HTTPS хотя бы из части точек; когда ресурс восстановится, придёт второе уведомление. Если ресурс так и не поднялся, подключается голосовой звонок. Время обнаружения равно интервалу проверки. Бесплатный план включает 5 проверок — хватит на сертификат, домен и HTTP главной страницы; при регистрации на баланс начисляется 150 ₽.
Частые вопросы
Можно ли при продлении использовать старый CSR?
Технически да: CSR не имеет срока действия, большинство УЦ примут тот же файл повторно, и сертификат будет работать с прежним ключом. Но ключ тогда живёт годами, и любая его утечка обесценивает все выпущенные на него сертификаты. Разумнее генерировать новую пару при каждом выпуске — команда та же и занимает секунду.
Где взять приватный ключ, если CSR делали в панели хостинга или в форме УЦ?
Там, где генерировали: в cPanel — раздел SSL/TLS → Private Keys, в ISPmanager — карточка сертификата, откуда ключ можно скачать. Если CSR делала форма УЦ в браузере, ключ отдавали ровно один раз в момент генерации; не сохранили — его нет ни у кого, включая УЦ. Выход один: новый CSR на своём сервере и бесплатный перевыпуск.
Нужно ли добавлять www в SAN, если сайт работает без www?
Нужно, если www.example.com редиректит на голый домен: редирект по HTTPS браузер выполнит только после TLS-рукопожатия, а для него нужен сертификат с именем www. Иначе посетитель, набравший www, получит ошибку раньше, чем редирект. Не включать www можно только если это имя вообще не резолвится в DNS.
Почему УЦ выпустил сертификат на 200 дней, если оплачен год?
Потому что с 15 марта 2026 года Baseline Requirements запрещают публичным УЦ выпускать сертификаты дольше 200 дней; годовая подписка означает право на бесплатные перевыпуски в течение оплаченного периода. Перевыпуск делают тем же или новым CSR через кабинет УЦ. С 15 марта 2027 года лимит станет 100 дней, с 15 марта 2029-го — 47.