Защита сайта сертификатом p12
Задача: обеспечить вход на сайт только при наличии действующего сертификата формата p12.
Решение:
Инструкция создана на основе нескольких источников, в т.ч. этого и этого, но с внесением поправок что год уже конец 2022, и появились некоторые нюансы, что старые инструкции не работают по умолчанию для новых дистрибутивов.
Сначала создадим корневой сертификат сервера и необходимые для дальнейшей работы файлы и папки. Для этого создадим в любой папке файл root_cer.sh с содержимым вида:
| 1 2 3 4 5 6 7 8 9 10 11 | mkdir db                # БД сертификатов mkdir certs             # ключ и сертификат сервера mkdir db/certs          # пользовательские сертификаты mkdir db/newcerts       # новые сертификаты mkdir p12               # сертификаты для передачи клиентам (p12) touch db/index.txt echo "01" > db/serial openssl req -new -newkey rsa:2048 -nodes -keyout certs/server.key -x509 -days 1024 \           -subj /C=RU/ST=Vol/L=Vol/O=LTD\ ElkiPalki/OU=Sale/CN=zz/emailAddress=wefwerfwe@gmail.com \           -out certs/server.crt | 
, где:
rsa:2048- длина ключа, -days — сколько дней действителен сертификат, -x509 — создаём самоподписаный сертификат, /C — страна, /ST — область, /L — город, /O — организация, /OU — отдел,
Результатом работы будут два файла в папке certs: servert.crt (сертификат) и serverk.key (закрытый ключ). Данные закрытого ключа можно посмотреть при помощи:
| 1 2 | openssl rsa -noout -text -in server.key   openssl x509 -noout -text -in server.crt            | 
В файле db/serial записывается текущий серийный номер
подписываемого сертификата в шестнадцатиричном формате. 
В файл db/index.txt сохраняются данные о подписываемых сертификатах.
Далее необходимо создать файл с настройками для генерации пользовательских сертификатов ca.config:
| 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 |  [ ca ]         default_ca             = CA_CLIENT       # При подписи сертификатов  использовать секцию CA_CLIENT         [ CA_CLIENT ]         dir                    = ./db            # Каталог для служебных файлов         certs                  = $dir/certs      # Каталог для сертификатов         new_certs_dir          = $dir/newcerts   # Каталог для новых сертификатов         database               = $dir/index.txt  # Файл с базой данных         # подписанных сертификатов         serial                 = $dir/serial     # Файл содержащий серийный номер         # сертификата         # (в шестнадцатиричном формате)         certificate            = ./certs/server.crt        # Файл сертификата CA         private_key            = ./certs/server.key        # Файл закрытого ключа CA         default_days           = 365             # Срок действия подписываемого         # сертификата         default_crl_days       = 7               # Срок действия CRL (см. $4)         default_md             = sha256          # Алгоритм подписи         policy                 = policy_anything # Название секции с описанием политики в отношении данных сертификата         [ policy_anything ]         countryName            = optional        # Код страны - не обязателен         stateOrProvinceName    = optional        # ......         localityName           = optional        # ......         organizationName       = optional        # ......         organizationalUnitName = optional        # ......         commonName             = supplied        # ...... - обязателен         emailAddress           = optional        # ...... | 
Для автоматизации генерации клиентских сертификатов можно собрать небольшой bash скрипт:
| 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 | #!/bin/bash NO_ARGS=0 BASE_DIR="" P12_DIR="$BASE_DIR/p12" CA_CFG="$BASE_DIR/ca.config"            # Конфигурационный файл для подписи CERTS="$BASE_DIR/db/certs"              # Каталог для хранения сертификатов CA_FILE="$BASE_DIR/certs/server.crt"    # Доверенный сертификат (Им подписывам) usage () {   echo "Скрипт `basename $0` предназначен для создания клиентских SSL сертификатов."   echo ""   echo "Использование: `basename $0` email name password" } if [ $# -eq "$NO_ARGS" ]  # Сценарий вызван без аргументов? then   usage                   # Если запущен без "аргуменотов" - вывести справку   exit $E_OPTERROR        # и выйти с кодом ошибки fi EMAIL=$1 echo "Email:$EMAIL" NAME=$2 echo "Name:$NAME" PASSWORD=$3 echo "Password:$PASSWORD" # создаём запрос на клиентский сертификат openssl req -new -newkey rsa:2048 -nodes -keyout "$CERTS/$NAME.key" \         -subj /C=RU/CN=usr/emailAddress="$EMAIL" \         -out "$CERT/$NAME.csr" # подписываем запрос на сертификат при помощи доверенного сертификата openssl ca -config "$CA_CFG" -in "$CERT/$NAME.csr" -out "$CERT/$NAME.crt" -batch # создаём файл p12 для передачи клиенту openssl pkcs12 -export -in "$CERT/$NAME.crt" -inkey "$CERTS/$NAME.key" \         -certfile "$CA_FILE" -out "$P12_DIR/$NAME.p12" -passout pass:"$PASSWORD" | 
На входе скрипта необходимо задать email, имя пользователя без пробелов и пароль на установку сертификата. На выходе будет запись в БД сертификатов и непосредственно сам контейнер p12 в папке p12 для передачи клиенту.
И ещё один полезный скрипт, для автоматизации отзыва сертификатов revote.sh:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #!/bin/bash NO_ARGS=0 BASE_DIR="" CA_CFG="$BASE_DIR/ca.config" CERTS="$BASE_DIR/db/newcerts" usage () {   echo "Скрипт `basename $0` предназначен для отзыва клиентских SSL сертификатов."   echo ""   echo "Использование: `basename $0` serial" } if [ $# -eq "$NO_ARGS" ]  # Сценарий вызван без аргументов? then   usage                   # Если запущен без "аргуменотов" - вывести справку   exit $E_OPTERROR        # и выйти с кодом ошибки fi SERIAL=$1 echo "Отзываю сертификат: $CERTS/$SERIAL.pem" openssl ca -config "$CA_CFG" -revoke "$CERTS/$SERIAL.pem" # составим список отозваных в файл revoked.crl openssl ca -gencrl -config "$CA_CFG" -out revoked.crl | 
Посмотреть список отозваных сертификатов можно при помощи команды:
| 1 | openssl crl -in revoked.crl -text -noout | 
Осталось только настроить apache для того что бы он пускал на сайт, только при наличии сертификата, добавив:
| 1 2 | SSLCACertificateFile /path/to/server.crt SSLVerifyClient require |