#!/bin/bash
# Этот скрипт предназначен для генерации серверных сертификатов для указанных доменов или IP-адресов.
# Он создает приватный ключ, запрос на подпись сертификата (CSR) и сам сертификат,
# используя конфигурационные файлы и инфраструктуру центра сертификации (CA).
# Скрипт также выводит информацию о сгенерированных ключах и сертификатах.
source ./config.sh
# Определяем, установлена ли русская локаль
if [ [ " ${ LANG ,, } " = = ru* ] ] || [ [ " ${ LC_MESSAGES ,, } " = = ru* ] ] ; then
LANG_RU = 1
else
LANG_RU = 0
fi
# Detect language and define error function
LANGUAGE = "en"
if [ [ " $LANG " = = ru* || " $LANG " = = *ru_* || " $LC_ALL " = = ru* || " $LC_ALL " = = *ru_* ] ] ; then
LANGUAGE = "ru"
fi
msg( ) {
local en_msg = " $1 "
local ru_msg = " $2 "
if [ [ " $LANGUAGE " = = "ru" ] ] ; then
echo " $ru_msg "
else
echo " $en_msg "
fi
exit 1
}
# Handle -h option for help message
if [ " $1 " = = "-h" ] ; then
if [ " $LANG_RU " -eq 1 ] ; then
echo " Использование: $0 [-h] [-t|--days DAYS] [domain1, domain2, ...] "
echo "Создает сертификаты сервера для указанных доменов или IP."
echo
echo "Опции:"
echo " -h Показать это сообщение"
echo " -t|--days DAYS Установить срок действия сертификата (по умолчанию: 3650 дней)"
else
echo " Usage: $0 [-h] [-t|--days DAYS] [domain1, domain2, ...] "
echo "Generate server certificates for the given domains or IPs."
echo
echo "Options:"
echo " -h Display this help message"
echo " -t|--days DAYS Set the number of days the certificate is valid for (default: 3650)"
fi
exit 0
fi
# Чтение параметра -t через getopt для установки числа дней действия сертификата
while true; do
case " $1 " in
-t | --days)
CERT_DAYS = " $2 "
shift 2
break
; ;
-h | --help)
if [ " $LANG_RU " -eq 1 ] ; then
echo " Использование: $0 [-h] [-t|--days DAYS] [domain1, domain2, ...] "
echo "Создает сертификаты сервера для указанных доменов или IP."
echo
echo "Опции:"
echo " -h Показать это сообщение"
echo " -t|--days DAYS Установить срок действия сертификата (по умолчанию: 3650 дней)"
else
echo " Usage: $0 [-h] [-t|--days DAYS] [domain1, domain2, ...] "
echo "Generate server certificates for the given domains or IPs."
echo
echo "Options:"
echo " -h Display this help message"
echo " -t|--days DAYS Set the number of days the certificate is valid for (default: 3650)"
fi
exit 0
; ;
*) break ; ;
esac
done
# Если параметр не указан, используем значение по умолчанию
CERT_DAYS = ${ CERT_DAYS :- 3650 }
pushd $PATH_TO_CA || exit
# Проверка, предоставлен ли первый параметр и не пуст ли он
if [ -z " $1 " ] ; then
if [ " $LANG_RU " -eq 1 ] ; then
echo "Нет входных данных"
else
echo "No input provided"
fi
exit 0
fi
# Разделение входной строки в массив с использованием запятых или пробелов как разделителей
IFS = ', ' read -r -a items <<< " $1 "
# Проверка, есть ли хотя бы один элемент в списке
if [ " ${# items [@] } " -eq 0 ] ; then
if [ " $LANG_RU " -eq 1 ] ; then
echo "Входные данные пусты"
else
echo "No elements found in the input"
fi
popd || exit
exit 0
fi
SEQ = "1"
# Извлечение первого элемента списка
fst_elem = " ${ items [0] } "
IMM_CA = " $PATH_TO_CA / $fst_elem "
# Создаем промежуточный CA
if [ ! -d " $IMM_CA " ] ; then
# Список каталогов, для которых создается структура
DIRECTORIES = ( " $fst_elem " )
# Создание необходимых директорий и настройка их прав доступа
for dir in " ${ DIRECTORIES [@] } " ; do
# Создание поддиректорий в каждой директории из списка
mkdir -p " $dir /certs " " $dir /crl " " $dir /newcerts " " $dir /private " " $dir /csr "
chmod 700 " $dir /private "
# Создание файлов базы CA
touch " $dir /index.txt "
echo -n 100000 >" $dir /serial "
# Настройка файла для CRL (список отозванных сертификатов)
echo -n 100000 >" $dir /crlnumber "
done
cat >$IMM_CA /immissuer.conf <<EOL
[ ca]
default_ca = CA_default
[ CA_default]
dir = $IMM_CA
certs = \$ dir/certs
crl_dir = \$ dir/crl
database = \$ dir/index.txt
new_certs_dir = \$ dir/newcerts
serial = \$ dir/serial
certificate = \$ dir/certs/intermediate.cert.pem
private_key = \$ dir/private/intermediate.key.pem
crlnumber = \$ dir/crlnumber
crl = \$ dir/crl/intermediate.crl.pem
crl_extensions = crl_ext
default_crl_days = 7
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 825
preserve = no
policy = policy_loose
[ policy_loose]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req]
default_bits = 4096
default_md = sha256
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
string_mask = utf8only
x509_extensions = v3_intermediate_ca
prompt = no
[ req_distinguished_name]
countryName = $COUNTRY_NAME
organizationName = $ORG_NAME
commonName = $fst_elem
[ v3_intermediate_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, keyCertSign, cRLSign
[ server_cert]
basicConstraints = CA:false
nsCertType = server
nsComment = " $COMM_NAME TLS server cert "
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ client_cert]
basicConstraints = CA:false
nsCertType = client
nsComment = "Brepo client cert"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
[ crl_ext]
authorityKeyIdentifier = keyid:always
EOL
# Перейти в директорию промежуточного ЦА или выйти с ошибкой, если это не удалось
pushd " $IMM_CA " || { msg " Error: Failed to change directory to $IMM_CA " " Ошибка: Н е удалось перейти в каталог $IMM_CA " ; }
# Генерация RSA ключа для промежуточного ЦА с шифрованием AES-256
openssl genrsa -aes256 -out private/intermediate.key.pem -passout " pass: $SERT_PASS " 4096 || { msg "Error: Failed to generate RSA key for intermediate CA" "Ошибка: Н е удалось создать RSA‑ ключ для промежуточного ЦА" ; }
# Установка прав доступа для ключа промежуточного ЦА
chmod 400 private/intermediate.key.pem
# Создание CSR для промежуточного ЦА
openssl req -config immissuer.conf -new -sha256 -key private/intermediate.key.pem -out csr/intermediate.csr.pem -passin " pass: $SERT_PASS " || { msg "Error: Failed to create CSR for intermediate CA" "Ошибка: Н е удалось создать запрос на сертификат для промежуточного ЦА" ; }
# Вернуться в исходную директорию или выйти с ошибкой, если это не удалось
popd || { msg "Can't return to old directory" "Невозможно вернуться к старому каталогу" ; }
# Перейти в директорию корневого ЦА или выйти с ошибкой, если это не удалось
pushd " $ROOT_CA " || { msg " Error: Failed to change directory to $ROOT_CA " " Ошибка: Н е удалось перейти в каталог $ROOT_CA " ; }
# Подпись сертификата промежуточного ЦА корневым ЦА
openssl ca -batch -config sertissuer.conf -extensions v3_inter -days 3550 -notext -md sha256 -in $IMM_CA /csr/intermediate.csr.pem -out $IMM_CA /certs/intermediate.cert.pem -passin " pass: $SERT_PASS " || { msg "Error: Failed to sign intermediate CA certificate" "Ошибка: Н е удалось подписать сертификат промежуточного ЦА корневым ЦА" ; }
# Установка прав доступа для сертификата промежуточного ЦА
chmod 444 " $IMM_CA /certs/intermediate.cert.pem "
openssl ca -config "sertissuer.conf" -gencrl -out crl/ca.crl.pem -passin " pass: $SERT_PASS "
# Вернуться в исходную директорию или выйти с ошибкой, если это не удалось
popd || { msg "Can't return to old directory" "Невозможно вернуться к старому каталогу" ; }
# Перейти в директорию промежуточного ЦА или выйти с ошибкой, если это не удалось
pushd " $IMM_CA " || { msg " Error: Failed to change directory to $IMM_CA " " Ошибка: Н е удалось перейти в каталог $IMM_CA " ; }
openssl ca -config "immissuer.conf" -gencrl -out crl/intermediate.crl.pem -passin " pass: $SERT_PASS "
cat $ROOT_CA /crl/ca.crl.pem " $IMM_CA /crl/intermediate.crl.pem " >" $IMM_CA /crl/ca-full.crl.pem "
# Вернуться в исходную директорию или выйти с ошибкой, если это не удалось
popd || { msg "Can't return to old directory" "Невозможно вернуться к старому каталогу" ; }
# Создание цепочки сертификатов
cat " $IMM_CA /certs/intermediate.cert.pem " " $ROOT_CA /certs/ca.cert.pem " >" $IMM_CA /certs/ca-chain.cert.pem " || { msg "Error: Failed to create CA chain certificate" "Ошибка: Н е удалось создать цепочку сертификатов ЦА" ; }
# Проверка сертификата промежуточного ЦА с использованием корневого центра сертификации
openssl verify -CAfile " $ROOT_CA /certs/ca.cert.pem " " $IMM_CA /certs/intermediate.cert.pem " || { msg "Error: Failed to verify intermediate CA certificate" "Ошибка: Н е удалось проверить сертификат промежуточного ЦА" ; }
fi
# Конец создания промежуточного CA
mkdir -p server_certs
pushd server_certs || exit
# Создание директории с именем первого элемента, если она не существует
mkdir -p " $fst_elem " || true
if [ ! -e " $fst_elem /csr_req.cnf " ] ; then
echo -n "2" >" $fst_elem / ${ fst_elem } _seq.seq "
# Создание файла csr_api.cnf с необходимым содержимым
cat <<EOF >" $fst_elem /csr_req.cnf "
[ req]
default_bits = 2048
default_md = sha256
prompt = no
distinguished_name = dn
req_extensions = req_ext
[ dn]
CN = $fst_elem
O = ${ ORG_NAME } :$SEQ
[ req_ext]
subjectAltName = @alt_names
[ alt_names]
EOF
# Добавление записей DNS для доменных имен и записей IP для IP-адресов
dns_count = 1
ip_count = 1
for item in " ${ items [@] } " ; do
if [ [ " $item " = ~ ^[ 0-9] +\. [ 0-9] +\. [ 0-9] +\. [ 0-9] +$ ] ] ; then
echo " IP. $ip_count = $item " >>" $fst_elem /csr_req.cnf "
( ( ip_count++) )
else
echo " DNS. $dns_count = $item " >>" $fst_elem /csr_req.cnf "
( ( dns_count++) )
fi
done
popd || exit
pushd " $IMM_CA " || {
if [ " $LANG_RU " -eq 1 ] ; then
echo " Ошибка: не удалось перейти в каталог $IMM_CA "
else
echo " Error: Could not change directory to $IMM_CA "
fi
exit 1
}
openssl genrsa -out " private/ $fst_elem .key.pem " -passout " pass: $SERT_PASS " 2048
chmod 400 " private/ $fst_elem .key.pem "
else
SEQ = $( cat " $fst_elem / ${ fst_elem } _seq.seq " )
echo $(( SEQ + 1 )) >" $fst_elem / ${ fst_elem } _seq.seq "
sed -i " s/^O = .*/O = ${ ORG_NAME } : ${ SEQ } / " " ${ fst_elem } /csr_req.cnf "
pushd " $IMM_CA " || {
if [ " $LANG_RU " -eq 1 ] ; then
echo " Ошибка: не удалось перейти в каталог $IMM_CA "
else
echo " Error: Could not change directory to $IMM_CA "
fi
exit 1
}
fi
# Генерация запроса на подпись сертификата (CSR)
openssl req -new -sha256 -key " private/ $fst_elem .key.pem " -out " csr/ $fst_elem .csr.pem " -config " ../server_certs/ $fst_elem /csr_req.cnf "
# Подписание CSR и создание сертификата
openssl ca -batch -config "immissuer.conf" -extensions server_cert -days " $CERT_DAYS " -notext -md sha256 -in " csr/ $fst_elem .csr.pem " -out " certs/ $fst_elem .cert.pem. $SEQ " -passin " pass: $SERT_PASS "
chmod 444 " certs/ $fst_elem .cert.pem. $SEQ "
# Вывод информации о сертификате
openssl x509 -noout -text -in " certs/ $fst_elem .cert.pem. $SEQ "
# Информирование пользователя о сгенерированных ключах и сертификатах
if [ " $LANG_RU " -eq 1 ] ; then
cat <<EOF
Сгенерированный набор ключей для установки на сервер:
- [ OUTPUTDATA] приватный ключ: \` $IMM_CA /private/$fst_elem .key.pem\` ;
- [ OUTPUTDATA_CERT] сертификат сервера: \` $IMM_CA /certs/$fst_elem .cert.pem.$SEQ \` ;
- [ OUTPUTDATA] цепочка CA: \` $IMM_CA /certs/ca-chain.cert.pem\` ;
- [ OUTPUTDATA] список отмененных сертификатов: \` $IMM_CA /crl/ca-full.crl.pem\`
EOF
else
cat <<EOF
Generated key set for server installation:
- [ OUTPUTDATA] private key: \` $IMM_CA /private/$fst_elem .key.pem\` ;
- [ OUTPUTDATA_CERT] server certificate: \` $IMM_CA /certs/$fst_elem .cert.pem.$SEQ \` ;
- [ OUTPUTDATA] CA chain: \` $IMM_CA /certs/ca-chain.cert.pem\` ;
- [ OUTPUTDATA] revoked certificates list: \` $IMM_CA /crl/ca-full.crl.pem\`
EOF
fi
popd || exit
popd || exit