OpenSSL, часть 3: Создание ключа и сертификата для клиента

В данной серии публикаций предполагается, что читатель знаком с концепцией криптографических систем с открытым ключом PKI. Почитать теорию можно, например, в Википедии. Для реализации PKI будем использовать свободное ПО OpenSSL. В отличии от большинства публикаций, буду параллельно описывать процедуру сразу в 2-х вариантах PKI для криптоалгоритма RSA и ГОСТ Р 34.10-2012. Синтаксис команд не отличается для различных дистрибутивов Linux и даже для Windows, полная инструкция будет состоять из следующих частей:
- Часть 1: Создание корневого Удостоверяющего Центра (УЦ);
- Часть 2: Создание ключа и сертификата для веб-серверов;
- Часть 3: Создание ключа и сертификата для пользователей [ВЫ НАХОДИТЕСЬ ЗДЕСЬ];
Основы безопасности
В норме, приватный ключ и запрос сертификата должны генерироваться на стороне запрашивающего (в нашем случае это должен делать сам клиент), а подписываться ключом УЦ со стороны администратора ИТ-безопасности. Как и в случае веб-сервера, это два разных человека каждый со своей зоной ответствености, поэтому получение сертификатов точно так же технически разделено на 2 или даже 3 этапа. При этом приватный ключ не должен покидать компьютер запрашивающего, а отправляться на подпись должен только файл-запрос CSR. Часто это правило нарушается админами, но я такое поощрять не буду: скрипты будут разделены, но вы можете объединить их в один, если иначе не обойтись.
Получение подписанного УЦ сертификата для клиента (RSA)
В качестве примера, ключ создается для клиента с именем Marcus Tullius Cicero, c электронной почтой сicero@rome.it.
ЭТАП 1: Скрипт, генерирующий приватный ключ клиента длиной 3072 бит и запрос сертификата. В OpenSSL 3.1 это делается в один заход, с применением рекомендованных параметров для конкретных задач клиента. Естественно, клиент не является высококвалифицированным специалистом PKI и обычно разработчики ПО максимально упрощают ему задачу. Создайте и отдайте вместе с инструкциями клиенту такой bash-скрипт с именем gen-client-rsa.sh, выделеные участки в начале скрипта требуется заменить на свое имя и электронный адрес:
#!/bin/bash
CLIENT="Marcus Tullius Cicero"
EMAIL="сicero@rome.it""
openssl req -newkey rsa:3072 \
-keyform PEM \
-outform PEM \
-nodes \
-keyout "./newkey/$CLIENT.key" \
-out "./newkey/$CLIENT.csr" \
-subj "/C=RU/CN=$CLIENT" \
-addext "basicConstraints = CA:FALSE" \
-addext "subjectKeyIdentifier = hash" \
-addext "keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment" \
-addext "extendedKeyUsage=clientAuth,codeSigning,emailProtection,timeStamping,OCSPSigning,ipsecIKE" \
-addext "subjectAltName = email:$EMAIL"
openssl req -text -noout -in "./newkey/$CLIENT.csr"
При формировании запроса обратите внимание на поле extendedKeyUsage: оставляем там только то, что нужно для выполнения задач этим пользователем. Для авторизации на веб-сервере достаточно значения clientAuth, для защиты электронной почты emailProtection, для клиента VPN IPSec IKE нужен ipsecIKE, а если пользователь является ещё и программистом, то для подписывания написанных им приложений для Java, Android и др. ему нужен codeSigning и т.д.
Вывод команды:
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = RU, CN = Marcus Tullius Cicero
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (3072 bit)
Modulus:
00:ca:bd:46:0c:08:23:5b:5d:d3:0f:73:08:d8:d9:
...
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
38:55:9E:D4:79:D7:F7:91:CF:97:3C:39:A7:CE:07:E1:D8:AF:AE:ED
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping, OCSP Signing, ipsec Internet Key Exchange
X509v3 Subject Alternative Name:
email:сicero@rome.it
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
46:cb:b0:d5:d3:81:ac:58:39:75:02:bb:1e:5c:67:a3:19:67:
...
ЭТАП 2: Администратор УЦ подписывает запрошенный сертификат на срок 365 дней следующим скриптом (назвать его можно например sign-client-rsa.sh), в качестве аргумента скрипту требуется передать имя файла запроса, без расширения, в кавычках:
#!/bin/bash
openssl x509 -req \
-in "./newkey/$1.csr" \
-CA ./ca/ca.crt \
-CAkey ./ca/ca.key \
-CAcreateserial \
-days 365 \
-out "./newkey/$1.crt" \
--copy_extensions=copyall
openssl x509 -text -in "./newkey/$1.crt"
Скрипт выводит на просмотр готовый сертификат, нужно проверить по выделенным полям, всё ли в порядке:
$ sign-client-rsa.sh "Marcus Tullius Cicero"
Certificate request self-signature ok
subject=C = RU, CN = Marcus Tullius Cicero
Enter pass phrase for ./ca/ca.key:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
31:51:0f:05:c6:26:99:88:63:eb:85:e5:79:98:19:ee:03:64:19:6a
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = RU, CN = Demo Root CA 2023
Validity
Not Before: Oct 24 09:58:37 2023 GMT
Not After : Oct 23 09:58:37 2024 GMT
Subject: C = RU, CN = Marcus Tullius Cicero
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (3072 bit)
Modulus:
00:ca:bd:46:0c:08:23:5b:5d:d3:0f:73:08:d8:d9:
...
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
38:55:9E:D4:79:D7:F7:91:CF:97:3C:39:A7:CE:07:E1:D8:AF:AE:ED
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping, OCSP Signing, ipsec Internet Key Exchange
X509v3 Subject Alternative Name:
email:сicero@rome.it
X509v3 Authority Key Identifier:
0F:15:FA:80:32:90:70:6C:15:CA:0F:FC:E9:36:6E:C5:A2:2D:43:18
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
3b:fc:23:87:78:73:f7:27:d9:7c:a0:67:ea:ba:fe:33:3a:06:
...
В результате выполнения скрипта в папке newkey сохраняется файл с подписанным УЦ открытым сертификатом пользователя, имя файла будет Marcus Tullius Cicero.crt. Для некоторых программ предусмотрена раздельная установка приватного ключа и сертификата, в этом случае достаточно отдать сертификат пользователю и он сам установит всё что нужно в программу. Но в большинстве браузеров и множестве других программ требуется устанавливать все ключи и сертификаты за одну операцию, в этом случае требуется собрать всё в один файл с расширением .p12 или .pfx (без разницы, в настоящее время разделения нет, оба суть контейнер PKCS#12).
И здесь возникает дилемма, как и на Этапе 1: либо пусть сборку файла PKCS#12 делает сам пользователь, либо это сделает админ за него. Второй выбор конечно не очень хороший, так как требует выдавать приватные ключи админу, что нарушает принцип PKI: но иногда некуда деваться и всегда бывают исключения.
ЭТАП 3: Создаем скрипт c наименованием pkcs12-rsa.sh для сборки всех ключей и сертификатов в один файл PKCS#12. Скрипт, аналогично как для подписания, требует указать аргументом наименование без расширения, а также 2 раза ввести пароль для защиты приватного ключа:
#!/bin/bash
openssl pkcs12 \
-export \
-in "./newkey/$1.crt" \
-inkey "./newkey/$1.key" \
-certfile ./ca/ca.crt \
-out "./newkey/$1.p12"
$ ./pkcs12-rsa.sh "Marcus Tullius Cicero"
Enter Export Password:
Verifying - Enter Export Password:
Если сборку PKCS#12 выполняет сам пользователь, то имеет смысл сделать ему готовый архив с вписанными его данными и всеми необходимыми файлами, чтоб ему оставалось только добавить файл со своим приватным ключом, и запустить на выполнение. В результат в папке newkey будет создан контейнер ключей в формате PKCS#12. Его можно импортировать, например в Firefox.
Получение подписанного УЦ сертификата для клиента (ГОСТ-2012)
ВНИМАНИЕ! Вы не можете использовать полученные нижеописанным способом ключи для использования в качестве квалифицированной электронной подписи для юридически значимого документооборота с государственными органами РФ! Для этого, согласно законодательству, требуется использовать только сертифицированные ФСБ программы, к коим OpenSSL никаким образом не относится. Этот опыт скорее интересен с технической стороны, т.к. алгоритмы ГОСТ-2012 являются на порядок более защищёнными и производительными, чем RSA, при одинаковой длине ключа.
ЭТАП 1: Здесь всё во многом аналогично RSA, за исключением некоторых параметров. Скрипт gen-client-gost.sh:
#!/bin/bash
CLIENT="Александръ Сергѣевичъ Пушкинъ"
EMAIL="alex.pushkin@spb.ru""
openssl req -engine gost \
-utf8 \
-newkey gost2012_512 \
-pkeyopt paramset:C \
-keyform PEM \
-outform PEM \
-nodes \
-keyout "./newkey/$CLIENT.key" \
-out "./newkey/$CLIENT.csr" \
-subj "/C=RU/CN=$CLIENT" \
-addext "basicConstraints = CA:FALSE" \
-addext "subjectKeyIdentifier = hash" \
-addext "keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment" \
-addext "extendedKeyUsage=clientAuth,codeSigning,emailProtection,timeStamping,OCSPSigning,ipsecIKE" \
-addext "subjectAltName = email:$EMAIL"
openssl req -text -noout -in "./newkey/$CLIENT.csr"
$ ./gen-client-gost.sh
Engine "gost" set.
-----
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = RU, CN = \C3\90\C2\90\C3\90\C2\BB\C3\90\C2\B5\C3\90\C2\BA\C3\91\C2\81\C3\90\C2\B0\C3\90\C2\BD\C3\90\C2\B4\C3\91\C2\80\C3\91\C2\8A \C3\90\C2\A1\C3\90\C2\B5\C3\91\C2\80\C3\90\C2\B3\C3\91\C2\A3\C3\90\C2\B5\C3\90\C2\B2\C3\90\C2\B8\C3\91\C2\87\C3\91\C2\8A \C3\90\C2\9F\C3\91\C2\83\C3\91\C2\88\C3\90\C2\BA\C3\90\C2\B8\C3\90\C2\BD\C3\91\C2\8A
Subject Public Key Info:
Public Key Algorithm: GOST R 34.10-2012 with 512 bit modulus
Public key:
X:CFBFB4732374CB76ABC2C4BFD4CEA4BB624591552830CE9418A3D3064F12FA6C8D318A420A3E80F48F5EA5E74F9F694BB8AE3DF701BF15FF5BEE48F72FEAB2F
Y:5226C1D0C916E8720DDE85DCE9140BDE0C36408EAE55C3C495FD3AA7FA50D81657148D7082A5B4068FF830F157B0EFFC808F8512A0DC2FF546CA05ADC45EC40A
Parameter set: GOST R 34.10-2012 (512 bit) ParamSet C
Attributes:
Requested Extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
AC:CC:16:3A:84:EA:A4:DA:F9:CB:5C:3C:52:74:4C:02:74:F6:99:5C
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping, OCSP Signing, ipsec Internet Key Exchange
X509v3 Subject Alternative Name:
email:alex.pushkin@spb.ru
Signature Algorithm: GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)
Signature Value:
3b:bb:c3:71:40:28:38:89:45:8d:06:f5:57:28:86:c2:c8:06:
...
Здесь имя труднее проконтролировать, так как он был задан в кирилллице в кодировке utf8. Но в графической оболочке всё нормально будет:

ЭТАП 2: Администратор УЦ ГОСТ подписывает запрошенный сертификат на срок 365 дней (sign-client-gost.sh):
#!/bin/bash
openssl x509 -req \
-engine gost \
-in "./newkey/$1.csr" \
-CA ./ca/ca.crt \
-CAkey ./ca/ca.key \
-CAcreateserial \
-days 365 \
-out "./newkey/$1.crt" \
--copy_extensions=copyall
openssl x509 -text -in "./newkey/$1.crt"
$ ./sign-client-gost.sh "Александръ Сергѣевичъ Пушкинъ"
Engine "gost" set.
Certificate request self-signature ok
subject=C = RU, CN = \D0\90\D0\BB\D0\B5\D0\BA\D1\81\D0\B0\D0\BD\D0\B4\D1\80\D1\8A \D0\A1\D0\B5\D1\80\D0\B3\D1\A3\D0\B5\D0\B2\D0\B8\D1\87\D1\8A \D0\9F\D1\83\D1\88\D0\BA\D0\B8\D0\BD\D1\8A
Enter pass phrase for ./ca/ca.key: Вводим пароль ключа УЦ
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
31:51:0f:05:c6:26:99:88:63:eb:85:e5:79:98:19:ee:03:64:19:6b
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = RU, CN = Demo Root CA 2023
Validity
Not Before: Oct 24 11:37:18 2023 GMT
Not After : Oct 23 11:37:18 2024 GMT
Subject: C = RU, CN = \D0\90\D0\BB\D0\B5\D0\BA\D1\81\D0\B0\D0\BD\D0\B4\D1\80\D1\8A \D0\A1\D0\B5\D1\80\D0\B3\D1\A3\D0\B5\D0\B2\D0\B8\D1\87\D1\8A \D0\9F\D1\83\D1\88\D0\BA\D0\B8\D0\BD\D1\8A
Subject Public Key Info:
Public Key Algorithm: GOST R 34.10-2012 with 512 bit modulus
Public key:
X:B69943F3BEF8067E04B9EE092DCB29FE98EE78AF111CAB83CF92BF05E3F9A2E8C4073597D763101A6BF8539CA45AB7049F8CA6BFC042E68F4D1CC6BCCFC6384
Y:ED95E6EAF99F691FB644C173BE138AA2F45F6359EE401570C030B58C085234E042A450452EA2356FB28B36BE2FDEFAE4F1C4DB2711523634E09EF920C414ADC9
Parameter set: GOST R 34.10-2012 (512 bit) ParamSet C
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
28:74:B7:F6:7C:01:B8:6C:2D:54:5C:4E:CC:76:BC:F2:55:D3:89:EC
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping, OCSP Signing, ipsec Internet Key Exchange
X509v3 Subject Alternative Name:
email:alex.pushkin@spb.ru
X509v3 Authority Key Identifier:
0F:15:FA:80:32:90:70:6C:15:CA:0F:FC:E9:36:6E:C5:A2:2D:43:18
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
80:ae:b6:b8:3c:22:5a:e7:52:1e:7c:58:74:6c:67:af:81:84:
...
ЭТАП 3: Создаем скрипт c наименованием pkcs12-gost.sh для сборки всех ключей и сертификатов в один файл PKCS#12. Скрипт точно такой же как для RSA и также требует указать аргументом наименование без расширения, а также 2 раза ввести пароль для защиты приватного ключа:
#!/bin/bash
openssl pkcs12 \
-export \
-in "./newkey/$1.crt" \
-inkey "./newkey/$1.key" \
-certfile ./ca/ca.crt \
-out "./newkey/$1.p12"
$ ./pkcs12-gost.sh "Александръ Сергѣевичъ Пушкинъ"
Enter Export Password:
Verifying - Enter Export Password:
ВНИМАНИЕ! Полученный файл не совместим для работы с криптопровайдерами CryptoPro и ViPNet, т.к. у них отличающийся формат PFX ГОСТ (обратный порядок байтов). В разделе загрузок CryptoPro есть утилита p12tocp.zip для конвертации и загрузки ключей в КриптоПро CSP. Также на практике следует учитывать, что в расширении X509 возможно потребуется указывать особые OID, но это уже специфично для провайдера услуг и используемого ПО - здесь у каждого будут свои заморочки.