Programming, electronics, lifestyle

26 Apr 2023

Создание CA, сертификатов сервера и клиента с помощью OpenSSL для RabbitMQ

Коротко зачем это все? Протокол TLS решает две проблемы, первая – то что клиент и сервер уверены что они никто из них не подменен, второе – все передаваемые данные - зашифрованы. При использовании MQTT данные могут быть перехвачены и подменены. А главное могут быть перехвачены учетные данные брокера сообщений (RabbitMQ) и в случае широких прав, злоумышленник может получить доступ к данным других клиентов брокера.

Для тех кто мало знаком с терминами SSL и TLS - это одно и тоже, в прошлом был только SSL, затем его переименовали в TLS. На практике люди до сих называют TLS как SSL и наоборот.

Также можно включить не только шифрование и проверку подписи через TLS, но и аутентификацию пользователя Authentication using Client TLS (x.509) Certificate Data (я это не использую, далее речь пойдет только о шифровании). Другими словами пользователю не нужно будет вводить логин и пароль, а достаточно просто использовать сертификат. Для этого требуется прописать пару настроек в основном конфигурационном файле брокера, а также в соответствии с именами пользователей задать соответствующие поля (Subject Alternative Name / Common Name ) в сертификате клиента, подробнее в README модуля SSL аутентификации.

Требования к сертификатам

В целом большая часть информации касаемо критериев к сертификатам содержится в официальной документации RabbitMQ. Однако там нет практических примеров.

Вкратце:

От сертификата сервера требуется совпадение CN (Common Name) (имя сертификата) с именем хоста сервера, иначе при TSL соединении будет ошибка, что сертификат выписан не на тот хост. Также можно в сертификате сервера определить алиасы (SAN (Subject Alternative Name)) или дополнительные доменные имена и тогда будет проверять не только имя сертификата, но еще и этот список алиасов. Подробнее здесь Enabling Peer Verification.

Теперь пройдемся по X509v3 Extensions - это что-то вроде определенных функций (из перечня) для которых может использоваться сертификат.

Требованием X509v3 Key Usage к сертификату CA - собственно иметь разрешение на выписку других сертификатов, а также я добавил возможность создавать Certificate Revocation List (CRL). В конфигурационном файле это выглядит вот так:

[ root_ca_extensions ]
basicConstraints        = CA:true
keyUsage                = keyCertSign, cRLSign

Требованием X509v3 Key Usage к сертификату сервера это право пользоваться им для цифровой подписи, а также для участия в шифровании симметричного ключа (Difference between key encipherment and data encipherment?). Хотя вот тут Extensions and Their Effect on Accepted Cipher Suites (Cipher Suite Filtering) немного другая логика, поправьте если я не прав:

[ ${SR_NAME}_ca_extensions ]
basicConstraints        = CA:false
keyUsage                = digitalSignature, keyEncipherment
extendedKeyUsage        = 1.3.6.1.5.5.7.3.1

Требованием Extensions к сертификату клиента это только право пользоваться им для цифровой подписи:

[ ${CL_NAME}_ca_extensions ]
basicConstraints        = CA:false
keyUsage                = digitalSignature
extendedKeyUsage        = 1.3.6.1.5.5.7.3.2

Создание сертификатов и ключей шифрования

Создание конфигурационного файла и директорий CA

Для удобства работы и сокращения кода я определю ряд переменных для использования в качестве путей к файлам:

CA_NAME=urpylka.com
CL_NAME=mqttclient_1
SR_NAME=mqtt.urpylka.com
ORG=mqtt

DIR_CA=./ca
DIR_EX=./export
D_PRI=${DIR_CA}/private
D_CRT=${DIR_CA}/certs
D_CRL=${DIR_CA}/crls
D_CSR=${DIR_CA}/csrs

rm -r ${DIR_CA} 2> /dev/null
rm -r ${DIR_EX} 2> /dev/null
mkdir ${DIR_CA} ${DIR_EX} ${D_PRI} ${D_CRT} ${D_CRL} ${D_CSR}

chmod 0750 ${D_PRI}
echo 01 > ${DIR_CA}/serial
touch ${DIR_CA}/index.txt

CA_CFG=${DIR_CA}/openssl.cnf

CA_KEY=${D_PRI}/ca_key.pem
CA_CRT=${DIR_CA}/ca_crt.pem

SR_KEY=${DIR_EX}/${SR_NAME}_key.pem
SR_CSR=${D_CSR}/${SR_NAME}_csr.pem
SR_CRT=${DIR_EX}/${SR_NAME}_crt.pem

CL_KEY=${DIR_EX}/${CL_NAME}_key.pem
CL_CSR=${D_CSR}/${CL_NAME}_csr.pem
CL_CRT=${DIR_EX}/${CL_NAME}_crt.pem

Далее определим конфигурационный файл.

  • Секция [ req ] отвечает как раз за генерацию CA сертификата.
  • [ ca ] – используется для CSR запроса.
  • [ ${CA_NAME} ] отвечает за создание файловой структуры CA.
  • Секции ${SR_NAME}_ca_extensions и ${CL_NAME}_ca_extensions отвечают за выбор расширений для сертификата сервера и клиента соответственно.
cat << EOF > $CA_CFG
[ ca ]
default_ca              = ${CA_NAME}

[ ${CA_NAME} ]
dir                     = ${DIR_CA}
certificate             = \$dir/ca_crt.pem
private_key             = \$dir/private/ca_key.pem
database                = \$dir/index.txt
serial                  = \$dir/serial
new_certs_dir           = \$dir/certs

default_days            = 30
default_md              = sha256
default_crl_days        = 7

# Set to 'no' to allow creation of several certs with same subject.
unique_subject          = yes

policy                  = ca_policy
x509_extensions         = certificate_extensions

# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)

[ ca_policy ]
commonName              = supplied
stateOrProvinceName     = optional
countryName             = optional
emailAddress            = optional
organizationName        = optional
organizationalUnitName  = optional
domainComponent         = optional

[ certificate_extensions ]
basicConstraints        = CA:false
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer

[ req ]
# default_bits          = 2048
default_keyfile         = ${D_PRI}/ca_key.pem
# default_md              = sha512
default_md              = sha256
# encrypt_key           = no

# accept information from '-subj' arg 'openssl req'
# if prompt = no -> will use 'root_ca_distinguished_name'

prompt                  = yes
distinguished_name      = root_ca_distinguished_name
x509_extensions         = root_ca_extensions

[ root_ca_distinguished_name ]
commonName              = ${CA_NAME}

[ root_ca_extensions ]
basicConstraints        = CA:true
keyUsage                = keyCertSign, cRLSign

[ ${SR_NAME}_ca_extensions ]
basicConstraints        = CA:false
keyUsage                = digitalSignature, keyEncipherment
extendedKeyUsage        = 1.3.6.1.5.5.7.3.1

[ ${CL_NAME}_ca_extensions ]
basicConstraints        = CA:false
keyUsage                = digitalSignature
extendedKeyUsage        = 1.3.6.1.5.5.7.3.2
EOF

Без использования digitalSignature на стороне сервера, многие MQTT клиенты откажутся работать выдав ошибку вида:

Error: write EPROTO 140372507405416:error:10000090:SSL routines:OPENSSL_internal:ECC_CERT_NOT_FOR_SIGNING:../../third_party/boringssl/src/ssl/ssl_cert.cc:596:

Подробнее про возможные параметры openssl.cnf.

Создание ключей:

# генерация пары ключей алгоритмом ec длиной 521 bits для CA
openssl ecparam -genkey -name secp521r1 -noout -outform PEM -out $CA_KEY

Пару ключей для сервера и клиента я создам с помощью более простого алгоритма. Тут нужно исходить из того, с каими алгоритмами может работать реализации TLS на стороне сервера и клиента.

# генерация пары ключей алгоритмом rsa
# openssl genrsa -out $CA_KEY 2048
openssl genrsa -out $SR_KEY 4096
openssl genrsa -out $CL_KEY 4096

# или с помощью алгоритма основанного на эллептических кривых
openssl ecparam -genkey -name secp521r1 -noout -outform PEM -out $SR_KEY
openssl ecparam -genkey -name secp384r1 -noout -outform PEM -out $CL_KEY

Разные параметры ключей How to generate RSA and EC keys with OpenSSL.

Генерация и подписание ключом сертификата центра сертицикации

# генерация сертификата, параметры вы должны ввести в процессе создания
openssl req -new -key $CA_KEY -out $CA_CRT -x509 -days 100

# генерацию ключа (rsa:2048, без пароля) и сертификата в один файл
openssl req -x509 -config $CA_CFG -days 3650 -outform PEM -out $CA_KEY -subj /CN=$CA_NAME/ -newkey rsa:2048 -nodes

# генерация сертификата на основе конфига, файла ключей и аргумента '-subj'
openssl req -x509 -config $CA_CFG -days 3650 -outform PEM -out $CA_CRT -subj /CN=$CA_NAME/ -new -key $CA_KEY
  • -noout – this option prevents output of the encoded version of the request.
  • -inform DER|PEM - This specifies the input format. The DER option uses an ASN1 DER encoded form compatible with the PKCS#10. The PEM form is the default format: it consists of the DER format base64 encoded with additional header and footer lines.
  • -outform DER|PEM - This specifies the output format, the options have the same meaning as the -inform option.
  • -nodes – if this option is specified then if a private key is created it will not be encrypted.
  • -days n – when the -x509 option is being used this specifies the number of days to certify the certificate for. The default is 30 days.

Полное описание параметров openssl.org.

Просмотр сгенерированного сертификата

openssl x509 -in $CA_CRT -text -noout

Создание сертификатов сервера и клиента

# Создание CSR (Certificate Signing Request)
openssl req -new -key $SR_KEY -outform PEM -subj /CN=$SR_NAME/O=$ORG/ -out $SR_CSR
openssl req -new -key $CL_KEY -outform PEM -subj /CN=$CL_NAME/O=$ORG/ -out $CL_CSR
# Генерация сертификата по CSR запросу
openssl ca -config $CA_CFG -in $SR_CSR -keyform PEM -notext -out $SR_CRT -batch -extensions ${SR_NAME}_ca_extensions -days 1000
openssl ca -config $CA_CFG -in $CL_CSR -keyform PEM -notext -out $CL_CRT -batch -extensions ${CL_NAME}_ca_extensions
  • -days – you can skip it (by default 30).
  • -notext – don’t output the text form of a certificate to the output file.
  • -batch – this sets the batch mode. In this mode no questions will be asked and all certificates will be certified automatically.

Подробнее о openssl ca.

openssl x509 -in $SR_CRT -text -noout
openssl x509 -in $CL_CRT -text -noout

Проверка

openssl s_server -accept 8883 -CAfile ${CA_CRT} -cert ${SR_CRT} -key ${SR_KEY}
openssl s_client -connect localhost:8883 -CAfile ${CA_CRT} -cert ${CL_CRT} -key ${CL_KEY}

Конвертация из PEM в DER формат

Для конвертации сертификата:

openssl x509 -inform pem -in ca_crt.pem -outform der -out ca_crt.der
openssl x509 -inform pem -in s_crt.pem -outform der -out s_crt.der
openssl x509 -inform pem -in c_crt.pem -outform der -out c_crt.der

Для конвертации ключа:

openssl rsa -inform pem -in s_key.pem -outform der -out s_key.der
openssl rsa -inform pem -in c_key.pem -outform der -out c_key.der

Источник: HOWTO convert PEM certificates and keys to DER format for emSSL.

Иcточники информации

Cсылки по настройке клиента ESP8266 / ESP32

На эту темы я аккумулировал информацию и, пока, у меня не удалось завести это. Как будет какой-то прогресс – напишу в отдельной статье.