Центр Сертификации (Certificate Authority).
Потребовалось по работе настроить Центр Сертификации. До этого был на Windows Server, но не понравилось людям. Да и сам я не смог разобраться, как в консоли автоматизировать процесс.
Поэтому решил попробовать сделать на Ubuntu Server 20.04. Конечно, взял за основу статью и немного под себя ее переделал:
- написал скрипты с псевдографикой — whiptail;
- у них используется определенная дата, до которой надо выпускать серты. Я не смог понять как ее сделать, и пришлось колхозить. Может кто подскажет мою ошибку;
- упаковка в pfx с паролем.
И так, начнем.
Cсылка на источник как всегда внизу.
Предварительная подготовка.
Содержание
Установка обновлений ОС.
1 | sudo aptitude update && sudo aptitude upgrade -y |
EASY-RSA.
1 | sudo aptitude install easy-rsa |
Создадим каталог easy-rsa в домашнем каталоге пользователя
1 | mkdir easy-rsa |
Теперь создадим символические ссылки на ранее установленный easy-rsa (необходимо для того, что бы при обновлении мы имели всегда актуальную версию ПО)
1 | ln -s /usr/share/easy-rsa/* ~/easy-rsa/ |
Ограничим доступ, предоставим разрешения только владельцу
1 | sudo chmod 700 easy-rsa |
Инициализируем PKI (Public Key Infrastructure)
1 2 | cd easy-rsa ./easyrsa init-pki |
Как видно, создался каталог со всем необходимым содержимым.
Далее, создадим ЦС.
Центр Сертификации (CA).
Создадим файл с переменными
1 2 | pwd nano vars |
Здесь необходимо заполнить по вашему усмотрению. В моем случае:
set_var EASYRSA_CERT_EXPIRE «» — вычисляемое значение (об этом ниже), т.ч. тут оно у меня пустое. А так количество дней действия сертификата.
1 2 3 4 5 6 7 8 9 | set_var EASYRSA_REQ_COUNTRY "RU" set_var EASYRSA_REQ_PROVINCE "" set_var EASYRSA_REQ_CITY "" set_var EASYRSA_REQ_ORG "" set_var EASYRSA_REQ_EMAIL "" set_var EASYRSA_REQ_OU "" set_var EASYRSA_ALGO "ec" set_var EASYRSA_DIGEST "sha256" set_var EASYRSA_CERT_EXPIRE "" |
Теперь создадим пару закрытого и открытого ключа
1 | ./easyrsa build-ca |
В данном случае от вас дальше потребуется ввести пароль. Он будет нужен при подписании или отзыве сертификата. Пароль следует держать в надежном месте, как вариант распечатать и убрать в сейф, либо загрузить в надежное облако в архиве с паролем.
В моем случае пароль не требуется — требование заказчика, т.ч я добавлю к команде nopass
1 | ./easyrsa build-ca nopass |
Так же потребуется ввести CN-имя (общее имя для сервера в контексте Центра Сертификации). В моём случае я просто указал CA.
Но надо быть внимательным и учитывать то, где будут использоваться сертификаты. Т.к. могут возникнут проблемы из-за несовпадения имен.
Как видно на скриншоте у нас появился сертификат севера — ca.crt и закрытый ключ к нему — ca.key
ca.key — необходимо держать в надежном месте и не допускать его утери\кражи.
ca.crt — публичный сертификат ЦС. Его копию надо будет передавать пользователям для установки, что бы выпущенные сертификаты обладали доверием, т.к. были подписаны этим ЦС.
Теперь самое интересное — создание запросов на сертификаты, подписание и отзыв.
Хотя ЦС уже настроен на подписание запросов, но в моем случае требуется все это делать в одном месте. Дальше я распишу команды, а в конце добавлю свои скрипты, которые сделал для пользователей.
Создание запросов на сертификат (ssl req).
Проверим установлен ли openssl. По-идеи, должен быть.
1 | openssl version |
У меня вот такой вывод:
1 | sudo aptitude install openssl |
Создадим закрытый ключ
1 | openssl genrsa -out test-server.key |
В таком случае у нас создался ключ длиной в 2048 бит. Если надо, допустим, большую длину ключа, то можно указать 4096 (размер):
1 | openssl genrsa -out test-server4096.key 4096 |
Проверить длину ключа можно вот такой командой:
1 | openssl rsa -in test-server.key -text -noout | grep -i "private-key" |
После этого создадим запрос (CSR — Certificate Signing Request)
1 | openssl req -new -key test-server.key -out test-server-csr.req |
-key — созданный ранее ключ,
-out — название нашего запроса (я сделал по имени сервера, что бы было понятно).
Потребуется ввести информацию, но это по желанию. В поле «Organization Name» можно поставить просто . (точка).
Для реальных сертификатов лучше всё заполнить как есть.
Можно проверить поля нашего запроса:
1 | openssl req -in test-server.req -noout -subject |
Подписание CSR.
После создания запроса надо его подписать. Сначала выполним импорт запроса:
1 2 | cd ~/easy-rsa ./easyrsa import-req test-server.req test-server |
- test-server.req — csr-запрос, созданный на предыдущем шаге
- test-server — общее имя запроса.
Теперь подпишем импортируемый запрос:
1 | ./easyrsa sign-req server test-server |
server — тип запроса. Может быть «cleint», «server» и «ca».
Появится информация о запросе. Необходимо всё проверить и, если всё правильно, то ввести yes и подтвердить ввод.
Если был установлен пароль на ключ ЦС, то надо будет его ввести. В данном случае без пароля.
После этого в каталоге ~/easy-rsa/pki/issued появился сертификат сервера.
Теперь у нас есть связка закрытого ключа (test-server.key) и сертификата (test-server.crt). Можно их передать, например, на веб-сервер, настроить apache или nginx и включить SSL. Но это только как пример.
Отзыв сертификата.
Выполним команду для отзыва:
1 | ./easyrsa revoke test-server |
И команду для генерации списка отозванных сертификатов (CRL — Certificate Revocation List):
1 | ./easyrsa gen-crl |
Как видно на скрине ниже, отозванный сертификат появился в соответствующем каталоге:
Мои скрипты автоматизации создания сертификата(ов) и их отзыва.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #!/bin/bash CCRT=/home/admin1/sh/ds.sh MCRT=/home/admin1/sh/mult_crt.sh RCRT=/home/admin1/sh/revoke_crt.sh while [ 1 ] do CHOICE=$( whiptail --title "Меню работы со скриптами ЦС" --menu "Пожалуйста, выберите необходимую задачу" 10 60 4 \ "1" "Создание 1 (одного) сертификата" \ "2" "Создание сертификатов из файла" \ "3" "Отзыв сертификата(ов)" \ "4" "Выход" 3>&1 1>&2 2>&3 ) exitstatus=$? if [[ "$exitstatus" = "1" || "$exitstatus" = "255" ]] then break fi case $CHOICE in "1") bash $CCRT ;; "2") bash $MCRT ;; "3") bash $RCRT ;; "4") exit ;; esac done exit |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | #!/bin/bash #переменные A="$(date +%F)" B="3030-01-01" #folder for users certs UC=/home/admin1/user_certs ERSA=/home/admin1/easy-rsa FPFX=/home/admin1/pfx_certs ISS=$ERSA/pki/issued #вычисление даты для запись в vars easy-rsa CDATE="$(( ($(date -d $B +%s) - $(date -d $A +%s)) / 86400))" CRTD="$(tail -n 1 $ERSA/vars | awk {'print $3'} | tr -d \")" #функции #проверка срока сертификата в днях function F_CERT_DATE { if [[ "$CRTD" == "$CDATE" ]] then echo "OK" else sed -i -E '9 s/"([0-9]+)"/\"'$CDATE'\"/g' $ERSA/vars cat -n $ERSA/vars fi } #создание ключа сертификата function F_KEY { openssl genrsa -out $UC/$CERT1.key } #создание запроса на сертификат function F_REQ { openssl req -new -key $UC/$CERT1.key -out $UC/$CERT1.req -subj /C=RU/CN=$CERT1 if (whiptail --title "Вывод данных запроса" --yesno "Хотите проверить данные запроса?" 10 60) then RQY=$(openssl req -in $UC/$CERT1.req -noout -subject) whiptail --title "Просмотр запроса (req-файл)" --msgbox "$RQY" 10 60 else echo "Продолжаю выполнение" fi } #импорт запроса и подпись сертификата function F_SIGN { cd $ERSA ./easyrsa import-req $UC/$CERT1.req $CERT1 ./easyrsa sign-req client $CERT1 cat $ERSA/pki/issued/$CERT1.crt | head } function F_PFX { openssl pkcs12 -export -out $FPFX/$CERT1.pfx -inkey $UC/$CERT1.key -in $ERSA/pki/issued/$CERT1.crt -passout pass:TEST1234 chmod 777 $FPFX/* LSC=$(ls -l $FPFX/$CERT1.pfx) whiptail --title "СЕРТИФИКАТ СОЗДАН" --msgbox "Сертификат $CERT1 создан, располагается в certs\pfx_certs и \ содержит следующие атрибуты (носят исключительно информационный характер): $LSC" 10 60 } #выполнение #whiptail --title "Информационное сообщение" --msgbox "Нажмите OK для продолжения" 10 60 #запрос ввода имени серта + перевод в верхний регистр (требование) CERT0=$(whiptail --title "Название (имя) сертификата" --inputbox "Введите название (имя) сертификата (например, TEST001): " 10 60 3>&1 1>&2 2>&3) #exitstatus=$? if [[ -z $CERT0 ]] then whiptail --title "ОШИБКА ВВОДА" --msgbox "Имя не может быть пустым. Повторите ввод." 10 60 exit else CERT1="${CERT0^^}" fi if (whiptail --title "Подтверждение запроса" --yesno "Вы ввели - $CERT1. Проверьте корректность данных и подтвердите запрос или отмените." 10 60) then echo "OK" else echo "NO" exit fi F_CERT_DATE F_KEY F_REQ F_SIGN F_PFX |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | #!/bin/bash #переменные A="$(date +%F)" B="3030-01-01" #folder for users certs UC=/home/admin1/user_certs ERSA=/home/admin1/easy-rsa CF=/home/admin1/lst_crt.txt FPFX=/home/admin1/pfx_certs #вычисление даты для запись в vars easy-rsa CDATE="$(( ($(date -d $B +%s) - $(date -d $A +%s)) / 86400))" CRTD="$(tail -n 1 $ERSA/vars | awk {'print $3'} | tr -d \")" #перевод из нижнего регистра в верхний sed -i 's/./\U&/g' $CF #проверка наличия файла lst_crt.txt function LST_FRMT { if [[ $(file lst_crt.txt) == "lst_crt.txt: ASCII text, with CRLF line terminators" ]] then dos2unix lst_crt.txt && file lst_crt.txt whiptail --title "ФАЙЛ БЫЛ КОНВЕРТИРОВАН" --msgbox "Файл lst_crt.txt был некорректного формата (DOS), в результате чего был переконвертирован в нужный формат (UNIX). На данный момент необходимо проверить кол-во созданных сертификатов, т.к. скорее всего, последний из списка не был создан" 10 80 # return 100 fi } function F_FLS { whiptail --title "Информация" --msgbox "Будет произведена проверка наличия файла на сервере, его размера и соответствию формата" 10 60 if [ -f $CF ] then LST_FRMT if [ -s $CF ] then if (whiptail --title "Проверка - OK" --yesno --scrolltext "Файл на сервере существует и имеет следующее содержимое $(head $CF) информация показана частично. Проверьте содержимое и подтвердите обработку" 20 60) then return 10 else return 9 fi else whiptail --title "ОШИБКА: размер файла" --msgbox "Файл lst_crt.txt имеет нулевой размер. Проверьте содержимое файла" 10 60 exit fi else whiptail --title "ОШИБКА: проверка наличия файла" --msgbox "Файл lst_crt.txt не найден. Проверьте наличие файла на сервере" 10 60 exit fi } #изменение даты для срока сертификатов function F_CERT_DATE { if [[ "$CRTD" == "$CDATE" ]] then echo "OK" else sed -i -E '9 s/"([0-9]+)"/\"'$CDATE'\"/g' $ERSA/vars cat -n $ERSA/vars fi } #создание ключа сертификата function F_KEY { while read i; do openssl genrsa -out $UC/$i.key done < $CF } #создание запроса на сертификат function F_REQ { while read i; do openssl req -new -key $UC/$i.key -out $UC/$i.req -subj /C=RU/CN=$i done < $CF } #импорт запроса и подпись сертификата function F_SIGN { cd $ERSA while read i; do ./easyrsa import-req $UC/$i.req $i done < $CF while read i; do echo "yes" | ./easyrsa sign-req client $i done < $CF #cat $ERSA/pki/issued/$CERT1.crt | head } #упаковка в pfx function F_PFX { while read i; do openssl pkcs12 -export -out $FPFX/$i.pfx -inkey $UC/$i.key -in $ERSA/pki/issued/$i.crt -passout pass:TEST1234 done < $CF chmod 777 $FPFX/* LSC=$(ls -l $FPFX/ | head) whiptail --title "СЕРТИФИКАТЫ СОЗДАНЫ" --msgbox --scrolltext "Сертификаты созданы, располагаются в certs\pfx_certs и \ содержат следующие атрибуты (носят исключительно информационный характер): $LSC" 20 60 } #выполнение F_CERT_DATE F_FLS if [ "$?" != "10" ] then exit fi F_KEY F_REQ F_SIGN F_PFX |
В предыдущем и в этом скрипте как раз идет изменение даты (срока) действия сертификата. Здесь она вычисляемая, скажем так. Но это всё индивидуально и это можно убрать из скриптов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | #!/bin/bash ERSA=/home/admin1/easy-rsa SMBF=/home/admin1/pfx_certs RVLST=/home/admin1/lst_rev.txt #------------------------------------------------------------------------------------------------- #Отзыв сертификата function F_REV { REV0=$(whiptail --title "Отзыв одного сертификата" --inputbox "Для отзыва сертификата необходимо указать его имя (например, TEST001):" 10 60 3>&1 1>&2 2>&3) #read REV0 if [[ -z $REV0 ]] then whiptail --title "Ошибка ввода" --msgbox "Имя не может быть пустым. Повторите ввод." 10 60 exit fi REV1="${REV0^^}" if (whiptail --title "Подтверждение запроса" --yesno "Вы ввели - $REV1. Проверьте корректность данных и подтвердите запрос или отмените." 10 60) then echo "OK" else echo "NO" exit fi pushd $ERSA ./easyrsa revoke $REV1 popd whiptail --title "Информация" --msgbox "После отзыва сертификата будет перегенерирован список отозванных сертификатов (CRL)" 10 60 } #перегенрация списка отзывов (CRL) function F_CRL { pushd $ERSA ./easyrsa gen-crl cp $ERSA/pki/crl.pem $SMBF chmod 777 $SMBF/* popd whiptail --title "Информация" --msgbox "Список перегенирован и скопирован в каталог $CRTS" 10 60 } function F_LSTCRL { whiptail --title "Запрос просмотра" --yesno "Хотите просмотреть список отозванных сертификатов?" 10 60 exitstatus=$? if [[ "$exitstatus" = "0" ]] then whiptail --title "Список отозванных сертификатов" --msgbox --scrolltext "$(openssl crl -in $ERSA/pki/crl.pem -noout -text)" 20 60 else exit fi } #------------------------------------------------------------------------------------------------- function F_FLS { whiptail --title "Информация" --msgbox "Будет произведена проверка наличия файла на сервере и его размера" 10 60 if [ -f $RVLST ] then if [ -s $RVLST ] then if (whiptail --title "Проверка - OK" --yesno --scrolltext "Файл на сервере существует и имеет следующее содержимое $(head $RVLST) информация показана частично. Проверьте содержимое и подтвердите обработку" 20 60) then return 10 else return 9 fi else whiptail --title "ОШИБКА: размер файла" --msgbox "Файл lst_rev.txt имеет нулевой размер. Проверьте содержимое файла" 10 60 exit fi else whiptail --title "ОШИБКА: проверка наличия файла" --msgbox "Файл lst_rev.txt не найден. Проверьте наличие файла на сервере" 10 60 exit fi } #функция отзыва сертификатов по списку function F_REV_M { cd $ERSA while read i; do echo "yes" | ./easyrsa revoke $i done < $RVLST whiptail --title "Информация" --msgbox "После отзыва сертификата будет перегенерирован список отозванных сертификатов (CRL)" 10 60 } #------------------------------------------------------------------------------------------------- function F_ONE { F_REV F_CRL F_LSTCRL } function F_MULTI { F_FLS if [ "$?" != "10" ] then exit fi F_REV_M F_CRL F_LSTCRL } #------------------------------------------------------------------------------------------------- while [ 1 ] do RVKC=$(whiptail --title "Меню отзыва" --menu "Сколько сертификатов будет отозвано" 10 60 3 \ "1" "Отозвать один сертификат" \ "2" "Отозвать сертификаты из файла" \ "3" "Выход" 3>&1 1>&2 2>&3) exitstatus=$? if [[ "$exitstatus" = "255" || "$exitstatus" = "1" ]] then break fi case $RVKC in "1") F_ONE ;; "2") F_MULTI ;; "3") exit ;; esac done exit |
В данном случае файл должен называться lst_rev.txt и иметь следующее содержание:
Cert1
cert2
cert5
….
CertN
Главное не забыть создать нужные каталоги или переписать переменные.
В скриптах возможны ошибки, т.ч. будьте внимательны!
Ну это некоторые скрины как выглядит в работе.
Ссылки (links):
If you found an error, highlight it and press Shift + Enter or to inform us.