Универсальный https c использованием ГОСТ сертификата

При попытках организовать https-соединения для различных web-сервисов с использованием ГОСТ-шифрования всегда оставались вопросы с посетителями, браузеры которых не поддерживают ГОСТ-алгоритмы. Логичным казалось решение при установке https-соединения отдавать клиенту сертификат в зависимости от поддерживаемых его системой алгоритмов, но до недавнего времени практические реализации такого подхода мне не встречались.

Когда-то я прочитал статью kyprizel «Как и зачем мы делаем TLS в Яндексе», где упоминалось о том, что OpenSSL начиная с версии 1.0.2 позволяет назначать сертификат сервера в зависимости от параметров клиента, но реализации на стороне Web-сервера нет. В Nginx 1.11.0 появилась такая возможность:
директивы ssl_certificate и ssl_certificate_key теперь можно указывать несколько раз для загрузки сертификатов разных типов (например, RSA и ECDSA).

Я решил собрать стенд с целью протестировать возможность организации https web-сервера c сертификатами ГОСТ для посетителей с установленным крипопровайдером ГОСТ-шифрования и сертификатами ECDSA для остальных.

В качестве тестового стенда выступила VM с Ubuntu 16.04.1 LTS

Собираем nginx


Я собирал nginx со статической библиотекой OpenSSL 1.0.2h

cd /opt/src/
#Скачиваем nginx
wget http://nginx.org/download/nginx-1.11.2.tar.gz
#Скачиваем openssl
wget https://openssl.org/source/openssl-1.0.2h.tar.gz
#Распаковываем
tar -zxvf nginx-1.11.2.tar.gz
tar -zxvf openssl-1.0.2h.tar.gz
cd nginx-1.11.2
#И собираем
./configure --prefix=/opt/work/nginx2 --user=nginx --group=nginx --with-http_ssl_module --with-openssl=/opt/src/openssl-1.0.2h/
make
make install

Далее необходимо сконфигурировать OpenSSL для поддержки алгоритмов ГОСТ. В сети даже ленивый сможет найти материалы по настройке.

мой openssl.cnf
cat /opt/src/openssl-1.0.2h/.openssl/ssl/openssl.cnf
openssl_conf=openssl_def
HOME                    = .
RANDFILE                = $ENV::HOME/.rnd
oid_section             = new_oids
[ new_oids ]
tsa_policy1 = 1.2.3.4.1
tsa_policy2 = 1.2.3.4.5.6
tsa_policy3 = 1.2.3.4.5.7
[ ca ]
default_ca      = CA_default
[ CA_default ]
dir             = ./demoCA
certs           = $dir/certs
crl_dir         = $dir/crl
database        = $dir/index.txt

new_certs_dir   = $dir/newcerts
certificate     = $dir/cacert.pem
serial          = $dir/serial
crlnumber       = $dir/crlnumber

crl             = $dir/crl.pem
private_key     = $dir/private/cakey.pem
RANDFILE        = $dir/private/.rand
x509_extensions = usr_cert
name_opt        = ca_default
cert_opt        = ca_default
default_days    = 365
default_crl_days= 30
default_md      = default
preserve        = no
policy          = policy_match
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional
[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional
[ req ]
default_bits            = 2048
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
req_extensions = v3_req
x509_extensions = v3_ca
string_mask = utf8only
[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = RU
countryName_min                 = 2
countryName_max                 = 2
stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = Moscow region
localityName                    = Locality Name (eg, city)
localityName_default            = Moscow
0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = JSC Example
organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_default  = It Department
commonName                      = Common Name (e.g. server FQDN or YOUR name)
commonName_max                  = 64
emailAddress                    = Email Address
emailAddress_max                = 64
[ req_attributes ]
challengePassword               = A challenge password
challengePassword_min           = 4
challengePassword_max           = 20
unstructuredName                = An optional company name
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = test.example.ru
DNS.2 = gost.example.ru

[ usr_cert ]
basicConstraints=CA:FALSE
nsComment                       = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = CA:true
[ crl_ext ]
authorityKeyIdentifier=keyid:always
[ proxy_cert_ext ]
basicConstraints=CA:FALSE
nsComment                       = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
[ tsa ]
default_tsa = tsa_config1
[ tsa_config1 ]
dir             = ./demoCA
serial          = $dir/tsaserial
crypto_device   = builtin
signer_cert     = $dir/tsacert.pem

certs           = $dir/cacert.pem

signer_key      = $dir/private/tsakey.pem
default_policy  = tsa_policy1

other_policies  = tsa_policy2, tsa_policy3
digests         = md5, sha1
accuracy        = secs:1, millisecs:500, microsecs:100
clock_precision_digits  = 0
ordering                = yes

tsa_name                = yes

ess_cert_id_chain       = no

[openssl_def]
engines = engine_section
[engine_section]
gost = gost_section
[gost_section]
engine_id = gost
default_algorithms = ALL
CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet

Выпускаем сертификаты для тестового web-ресурса. Я не стал отягощать себя самоподписанными сертификатами, а создал запросы и подписал их в Тестовом УЦ КриптоПРО и у Китайских «друзей», уже давно выдающих бесплатные сертификаты.

#Формируем закрытый ключ
openssl genrsa -out test.example.ru.key 2048
#генерируем запрос
openssl req -new -sha256 -key test.example.ru.key -out test.example.ru.csr
#генерирум закрытый ключ по алгоритму ГОСТ
openssl genpkey -algorithm gost2001 -pkeyopt paramset:A -out gost.example.ru.key
#генерируем запрос
openssl req -engine gost -new -key gost.example.ru.key -out gost.example.ru.csr

Выгружаем полученные запросы и подписываем в УЦ.

Конфигурирование Nginx


Ниже приведен мой конфигурационный файл Web-сервера Nginx. Следует обратить внимание на дублирующиеся директивы ssl_certificate и ssl_certificate_key, в которых задаются 2 сертификата для одного https сервера, а также на строку ssl_ciphers GOST2001-GOST89-GOST89:HIGH:MEDIUM , где определяется список и порядок применяемых алгоритмов шифрования.

user  nginx;
worker_processes  1;
events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       443 ssl;
        server_name  gost.example.ru;
        ssl_certificate      keys/gost.example.ru_bundle.crt;
        ssl_certificate_key  keys/gost.example.ru.key;
        ssl_certificate      keys/test.example.ru_bundle.crt;
        ssl_certificate_key  keys/test.example.ru.key;
        ssl_ciphers GOST2001-GOST89-GOST89:HIGH:MEDIUM;
        ssl_protocols   TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers  on;
        location / {
            proxy_pass http://192.168.1.249;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

}



На просторах паутины я нашел init script для более удобного запуска:

/etc/init.d/nginx
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="Nginx Daemon"
NAME=nginx
PREFIX=/opt/work/nginx2
DAEMON=$PREFIX/sbin/$NAME
CONF=$PREFIX/conf/$NAME.conf
PID=$PREFIX/logs/$NAME.pid
SCRIPT=/etc/init.d/$NAME

if [ ! -x "$DAEMON" ] || [ ! -f "$CONF" ]; then
    echo -e "\033[33m $DAEMON has no permission to run. \033[0m"
    echo -e "\033[33m Or $CONF doesn't exist. \033[0m"
    sleep 1
    exit 1
fi

do_start() {
    if [ -f $PID ]; then
        echo -e "\033[33m $PID already exists. \033[0m"
        echo -e "\033[33m $DESC is already running or crashed. \033[0m"
        echo -e "\033[32m $DESC Reopening $CONF ... \033[0m"
        $DAEMON -s reopen -c $CONF
        sleep 1
        echo -e "\033[36m $DESC reopened. \033[0m"
    else
        echo -e "\033[32m $DESC Starting $CONF ... \033[0m"
        $DAEMON -c $CONF
        sleep 1
        echo -e "\033[36m $DESC started. \033[0m"
    fi
}

do_stop() {
    if [ ! -f $PID ]; then
        echo -e "\033[33m $PID doesn't exist. \033[0m"
        echo -e "\033[33m $DESC isn't running. \033[0m"
    else
        echo -e "\033[32m $DESC Stopping $CONF ... \033[0m"
        $DAEMON -s stop -c $CONF
        sleep 1
        echo -e "\033[36m $DESC stopped. \033[0m"
    fi
}

do_reload() {
    if [ ! -f $PID ]; then
        echo -e "\033[33m $PID doesn't exist. \033[0m"
        echo -e "\033[33m $DESC isn't running. \033[0m"
        echo -e "\033[32m $DESC Starting $CONF ... \033[0m"
        $DAEMON -c $CONF
        sleep 1
        echo -e "\033[36m $DESC started. \033[0m"
    else
        echo -e "\033[32m $DESC Reloading $CONF ... \033[0m"
        $DAEMON -s reload -c $CONF
        sleep 1
        echo -e "\033[36m $DESC reloaded. \033[0m"
    fi
}

do_quit() {
    if [ ! -f $PID ]; then
        echo -e "\033[33m $PID doesn't exist. \033[0m"
        echo -e "\033[33m $DESC isn't running. \033[0m"
    else
        echo -e "\033[32m $DESC Quitting $CONF ... \033[0m"
        $DAEMON -s quit -c $CONF
        sleep 1
        echo -e "\033[36m $DESC quitted. \033[0m"
    fi
}

do_test() {
    echo -e "\033[32m $DESC Testing $CONF ... \033[0m"
    $DAEMON -t -c $CONF
}

do_info() {
    $DAEMON -V
}

case "$1" in
 start)
 do_start
 ;;
 stop)
 do_stop
 ;;
 reload)
 do_reload
 ;;
 restart)
 do_stop
 do_start
 ;;
 quit)
 do_quit
 ;;
 test)
 do_test
 ;;
 info)
 do_info
 ;;
 *)
 echo "Usage: $SCRIPT {start|stop|reload|restart|quit|test|info}"
 exit 2
 ;;
esac

exit 0


Все, запускаем веб-сервер и проверяем. В браузере Internet Explorer 11 c установленным криптопровайдером КриптоПро CSP посетителю отдается ГОСТ сертификат, подписанный CRYPTO-PRO Test Center 2 при обращении к gost.example.ru, в Mozilla Firefox нет поддержки алгоритмов ГОСТ и посетитель получает «обычный» сертификат, подписанный WoSign CA Limited.

Internet Explorer



Mozilla Firefox



С моей точки зрения получился достаточно интересный вариант использования технологии, хочется надеяться, что скоро появится поддержка в openresty ssl_certificate_by_lua_block.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 35

    +1
    Не холивара ради, а образования для:
    Гостовские сертификаты, кроме как на винде под IE, ещё где-нибудь поддерживаются?
      0
      К примеру если поставить плагин КриптоПро ЭЦП Browser plug-in
        +1
        Хм, а есть открытый (пускай и сторонний) плагин? Как-то немного нелогично в погоне за безопасностью ставить такие вот плагины…
          +2
          А кто утверждал, что гостовские сертификаты ставят для безопасности?
          Тут скорее требования властей и силовых служб.
            +1
            Думаю маловероятно. ГОСТ ставят не для прикола, тут вся штука в слове 'сертификация', а на открытом откуда она появится? Кому не надо — тем и не надо :) используйте RSA-like и не парьте голову
          +1
          Не задумываясь могу сказать, что браузеры использующие библиотеки openssl должны поддерживать ГОСТ сертификаты. А так же есть такой продукт как КриптоПРО Fox
          0
          Опционально гост уже давно входит в состав openssl, а по-опыту — «вживую» я пользовал гостовское шифрование, вместе с выпиской и подписями сертификатов для организации vpn-тунелей (на том же openvpn — и под win и под фрёй и под прочими центосями :) ) — это обязательное условие при работе/передаче с персональных данных на территории РФ
            0
            А за границей сервер/концентратор с ГОСТ можно ставить?
              0
              Если этот сервер не будет участвовать в обработке/хранении или передаче персональных данных наших граждан, то никто не запрещает :)
                0
                А если как раз будет при трансграничной передаче?
                  0
                  Успокойтесь ;) У openssl нет сертификата ФСТЭК и ФСБ, так что хоть формально это и гост, но по бумагам это решение ничуть не лучше обычного https. Т.е. штрафануть вас могут в обоих случаях. Но если гост — ФСБшники могут закрыть глаза. Поможет только модель угроз/нарушителя где будет написано что ПДн общедоступные, например…
                  По поводу трансграничной передачи:
                  — делаете перевалочный сервак (если у вас, например, несколько отделений, где будет информация со всех отделений) на территории РФ, и шлите себе спокойно за границу.
                    0
                    Просто есть какое-то устойчивое мнение, что экспорт ГОСТ криптографии за границу запрещен, поэтому можно спокойно передавать ПДн трансгранично через обычный https (TLS), и этого в силу вышеупомянутого запрета достаточно.

                    Пытался найти обоснование — не нашел. Так и не понимаю до конца, можно ли передавать ПДн за границу через обычный https, если первичный ввод производится в БД в России, и данные никак не являются общедоступными.
                      0
                      Мое мнение:
                      есть 1119 пп и пункт 13г:
                      г) использование средств защиты информации, прошедших процедуру оценки соответствия
                      требованиям законодательства Российской Федерации в области обеспечения безопасности информации,
                      в случае, когда применение таких средств необходимо для нейтрализации актуальных угроз.

                      Т.е. если у вас актуальна угроза перехвата ПДн при передаче за границу, то вы сами себе злобные буратины и тогда привет сертифицированные СЗИ и СКЗИ в том числе. и повторюсь openssl вас в этом случае не спасет — бумажки нет.
                      Если угроза не актуальна (нет методики определения ущерба при разглашении, данные не очень критичные — типа ФИО и телефон и т.д. ) то речи о сертифицированных СЗИ не идет. таким образом и ФСБ требовать от вас госта не имеет права…
                      Может быть есть какие-то запреты за экспорт самой реализации ГОСТ за границу (как например в США во времена диффи и хелмана такое было) но тут я, увы, не подскажу…
                      Повторюсь, по возможности берите согласие что ПДн общедоступные… и не забудьте согласие на трансграничную передачу 3м лицам… и вы избавите себя от кучи бумажных проблем. Ну а защиту сделайте нормальными средствами и нормально :))) РЖД, ксттаи, так и поступает… ваши данные общедоступные… почему им можно а нам нет?
                        0
                        Вам могут сделать ата-та когда ФСБ придет вас проверять (если придет) и выяснит, что модель угроз написана некорректно. Но в этом случае у вас будет время на доработку и вы всегда можете поспорить — предъяви, например, некое экспертное заключение о том что эти угрозы признаны не актуальными, где экспертами может быть кто угодно…
                        Нет, в целом направление деятельности хорошее и правильное… реализация, как всегда, подкачала…
          0
          Использовать криптосредства можно, но в с соответствии указом президента России от 5 мая 2004 г. № 580 необходимо согласовать с ФСБ
            –2
            Так вот как ключи теперь будут ФСБ передаваться) Понятненько
              0
              Поясните ход ваших мыслей, мистер Коломбо
              0
              А где в конфигах указан альтернативный сертификат от «китайских друзей»?
                0
                Вот же
                        ssl_certificate      keys/test.example.ru_bundle.crt;
                        ssl_certificate_key  keys/test.example.ru.key;
                
                0
                Так и не понял, по каким алгоритмам Nginx выбирает, какой из сертификатов подходит, и где прописан ssl_ciphers для нормального сертификата.
                  +1
                  Постараюсь пояснить:
                  В строке директиве ssl_ciphers с лева на право перечисляются допустимые алгоритмы шифрования. В данном случае самым приоритетным (левым) указан алгоритм GOST2001-GOST89-GOST89, далее указаны алгоритмы HIGH — это ключевое слово указывает алгоритмы с длиной ключа более 128 бит, и MEDIUM — алгоритмы с длиной ключа 128 бит. Получается, что сначала NGINX предложит построить https соединение по ГОСТ, если отказались то RSA-like.
                    0

                    Ещё: при SSL-хэндшейке браузер отправляет серверу список алгоритмов шифрования, которые он поддерживает и браузер с сервером выбирают алгоритм, из пересечения этих списков. Тут в дело вступает директива ssl_prefer_server_ciphers on;, которая и заставляет выбирать GOST2001-GOST89-GOST89, если браузер его поддерживает, поскольку в директиве ssl_ciphers он стоит первым, т.е. имеет наивысший приоритет. Если браузер его не поддерживает, то выбирается первый алгоритм из списка HIGH, который поддерживает браузер.

                      0
                      mrdoger, Envek, благодарю, как я и предполагал. Просто забыл про ключевое слово HIGH, привык к простыням генератора Mozilla, и поэтому показалось, что ssl_ciphers слишком короткий для работы на RSA-like сертификатах ))
                    0

                    Кстати, а что про ваш сервер говорит SSL Test? Не падает ли случаем, какую оценку выдаёт, проверьте :-) https://www.ssllabs.com/ssltest/

                      0
                      С тестовым стендом все в порядке, а вот ssllabs не смог закончить тестирование на 98% Assessment failed: Unsupported key algorithm: ECGOST3410
                      0
                      >Далее необходимо сконфигурировать OpenSSL для поддержки алгоритмов ГОСТ

                      правильно я понял, что вы использовали «чистый» OpenSSL, без отечественных модулей типа «КриптоПро CSP для nginx »?

                        0
                        Да. Но использование таких модулей возможно. Я уже протестировал работу с крипто про engine
                        0
                        А openssl из коробки, который в ubuntu 16.04 — openssl-1.0.2g-1ubuntu4.1, чем то не устроил? Чем вызвана необходимость сборки из исходников?
                          0
                          Да ничем, просто привычка свежие версии по на тестовом стенде всегда собирать из исходников. В статье я хотел осветить новые возможности появившиеся в связке nginx 1.11.0+openssl 1.0.2g, и я надеюсь, что статью не будут использовать как пошаговое руководство при пром. внедрении.
                            0

                            Если настроить системный OpenSSL на использование ГОСТовых алгоритмов, то глюки и странные сообщения об ошибках могут полезть из всех углов системы (я такое видел, хотя ничего страшного и не случилось), так что лучше системный OpenSSL не трогать.

                              0
                              Если настроить системный OpenSSL на использование ГОСТовых алгоритмов

                              как это настроить системный openssl на использование ГОСТовых алгоритмов? Я всегда думал, что алгоритмы, и поведение в целом, вы задаете в конкретной программе, которая использует openssl библиотеку. Например, postfix/nginx/apache/ssh и т.п. Т.е. вам никто не мешает в nginx отсавить возможность использовать sslv3, а в postfix запретить, конечно при условии что сама поддержка есть в openssl

                              Разве у openssl есть какой то глобальный конфигурационный файл, который влияет на работу всех программ, его использующих?
                                0

                                Да, есть. В Ubuntu находится по пути /etc/ssl/openssl.cnf и влияет на всю систему и все программы.

                                  0

                                  Этот файл конфигурирует утилиты командной строки, а не библиотеку.

                          Only users with full accounts can post comments. Log in, please.