Настройка SSL в Apache и Tomcat

Date March 2nd, 2010 Author Vitaly Agapov

“—Я вам посылку принёс, только я вам её не отдам, потому как у вас докУментов нету
—Усы, лапы, хвост — вот мои документы!”
— «Каникулы в Простоквашино»

Сегодня посмотрим, как обеспечить поддержку шифрованного протокола https для контейнера сервлетов Tomcat. Причём рассмотрим проблему с двух сторон: как обеспечить поддержку https, если фронтэндом является Apache, и как её обеспечить, если Tomcat сам выполняет функции web-сервера. В целом, это достаточно несвязанные задачи, и их просто можно в отрыве от контекста рассматривать как настройку SSL для двух различных приложений.

Вообще, SSL (Secure Socket Layers) – это протокол шифрования трафика с использованием криптографии с открытым ключом (асимметричной криптографии). Непосредственно для шифрования требуется только открытый ключ и закрытый ключ сервера. А для аутентификации сервера, то есть для проверки, что сервер, с которым контактирует клиент и передаёт ему некоторые конфиденциальные данные тот, за который он себя выдаёт – требуется сертификат. Сертификат, подписанный доверенным Certificate Authority (CA) даёт такую гарантию. При более низких требованиях к безопасности можно использовать самоподписанные сертификаты.

Всё нижеописанное производилось и проверялось с Apache 2.2.12 и Tomcat 6.0.24 на операционных системах CentOS 5.4 и Ubuntu 9.10 c JDK 1.6.0.18.

Tomcat

Для создания самоподписанного (self-signed) сертификата сначала создадим хранилище – файл .keystore. Куда его положить – дело сугубо личное. Например, в /opt/tomcat6:

keytool -genkey -alias tomcat -keyalg RSA -keystore /opt/tomcat6/.keystore

В некоторых случаях, если на сервере несколько раз обновлялась версия Java с сохранением старых версий, то может так быть, что команда keytool окажется алиасом к бинарнику из более старой версии. Тогда либо надо это починить, либо воспользоваться такой командой:

$JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA -keystore /opt/tomcat6/.keystore

При создании хранилища keytool задаст несколько вопросов:

Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]:  localhost
What is the name of your organizational unit?
[Unknown]:  MyUnit
What is the name of your organization?
[Unknown]:  MyOrganization
What is the name of your City or Locality?
[Unknown]:  SPb
What is the name of your State or Province?
[Unknown]:  MyState
What is the two-letter country code for this unit?
[Unknown]:  RU
Is CN=localhost, OU=MyUnit, O=MyOrganization, L=SPb, ST=MyState, C=RU correct?
[no]:  yes

Enter key password for <tomcat>
(RETURN if same as keystore password):

Здесь главное – это запомнить введённый пароль от хранилища, а также в вопросе про first and last name ввести корректное доменное имя сайта, например www.mysite.com или mysite.com. Если это значение не будет совпадать с доменным именем сайта, отдающим сертификат из этого хранилища, то браузер начнёт ругаться и выводить предупреждения о возможных злонамеренных действиях, а чересчур мнительный Chrome вообще может отказаться заходить на https. Причём варианты доменных имён c www и без него не являеются взаимозаменяемыми, так что здесь либо придется создавать сертификаты для обоих вариантов, либо создавать правила mod_rewrite для одного из вариантов.
Напоследок, кстати, keytool спрашивает конкретно пароль для сертификата tomcat. Если его не вводить, то для него будет использоваться пароль самого хранилища.

Сразу после создания хранилища в нём уже будет один сертификат с алиасом tomcat. Просмотреть содержимое хранилища можно такой командой:

$ keytool -list -keystore /opt/tomcat6/.keystore
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

tomcat, 02.03.2010, PrivateKeyEntry,
Certificate fingerprint (MD5): 17:7E:FA:7F:57:5D:D1:4A:26:C4:A5:D6:CF:8B:E9:C6

Если нам потребуется добавить новый сертификат в уже имеющееся хранилище .keystore, то выполним такую команду:

$ keytool -genkey -alias tomcat -keyalg RSA -keystore .keystore

При этом нам зададут вопросы, такие же, как и при создании хранилища. разница лишь в том, что пароль хранилища нам надо будет не придумать, а указать верный.

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

$ keytool -delete -alias tomcat -keystore .keystore

Необходимость удалить сертификат может возникнуть, например, при истечении его срока действия.

Если надо добавить в хранилище сертификаты, выданные центром сертификации, то выполняем для каждого из них команды:

keytool -import -trustcacerts -alias root -file RootCertFileName.crt -keystore .keystore 
keytool -import -trustcacerts -alias intermediate -file IntermediateCertFileName.crt -keystore .keystore 
keytool -import -trustcacerts -alias tomcat -file PrimaryCertFileName.crt -keystore .keystore 

После этого корневой, промежуточный и основной сертификаты окажутся в хранилище. Остяётся настроить Tomcat на использование файла хранилища.

Теперь открываем server.xml конфигурации Tomcat (в разных дистрибутивах он лежит в разных местах) и добавляем атрибуты keystoreFile и keystorePass в коннектор на порту 8443:

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
         maxThreads="150" scheme="https" secure="true"
         keystoreFile="/opt/tomcat6/.keystore" keystorePass="somepassword"
         clientAuth="false" sslProtocol="TLS"
         compression="on"
         compressionMinSize="1024"
         compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
         URIEncoding="UTF-8"
         />

Всё, можно идти на порт 8443 и радоваться зашифрованному подключению.

Apache

В Apache для работы с SSL потребуется модуль mod_ssl, так что либо Apache сразу компилируется с поддержкой соответствующего модуля, либо доустанавливаем mod_ssl из репозиториев (в Ubuntu это пакет libapache2-mod-gnutls). Затем нам потребуется создать SSL-сертификат:

$ openssl genrsa -des3 -out mysite.key 1024

Generating RSA private key, 1024 bit long modulus
.++++++
................................++++++
e is 65537 (0x10001)
Enter pass phrase for mysite.key:
Verifying - Enter pass phrase for mysite.key:


$ openssl req -new -key mysite.key -out mysite.csr

Enter pass phrase for mysite.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:RU
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:SPb
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:mysite.com
Email Address []:webmaster@mysite.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:


$ openssl x509 -req -days 365 -in mysite.csr -signkey mysite.key -out mysite.crt

Signature ok
subject=/C=RU/ST=Some-State/L=SPb/O=Internet Widgits Pty Ltd/CN=mysite.com/emailAddress=webmaster@mysite.com
Getting Private key
Enter pass phrase for mysite.key:


$ openssl rsa -in mysite.key -out mysite_selfsigned.key

Enter pass phrase for mysite.key:
writing RSA key

Первая команда генерирует случайный ключ mysite.key. Он нужен для подписи сертификата.
Вторая команда создаёт файл mysite.csr – здесь надо ввести правильный пароль от ключа, а также в поле Common Name указать полное доменное имя сайта. Если имя сайта не указать, то при получении сертификата, по аналогии с сертификатом для Tomcat, браузер будет настойчиво просить пользователя покинуть страницу.
Третья команда создаст непосредственно X.509 сертификат mysite.crt.
Четвёртая команда снимает с ключа пароль.

В принципе, этого уже достаточно. Осталось сконфигурить Apache. Создаём соответствующий virtual host:

<VirtualHost *:443>
    DocumentRoot /var/www/
    <Directory />
        Options FollowSymLinks
        AllowOverride All
    </Directory>
    SSLEngine On
    SSLCertificateFile /etc/apache2/mysite.crt
    SSLCertificateKeyFile /etc/apache2/mysite_selfsigned.key
    SetEnvIf User-Agent ".*MSIE.*" \
      nokeepalive ssl-unclean-shutdown \
      downgrade-1.0 force-response-1.0
</VirtualHost>

Блок SetEnvIf нужен для обхода бага с SSL в некоторых версиях IE. Директива заставит Apache использовать для IE протокол HTTP1.0.

Затем рестартуем Apache, заходим на https://mysite.com, принимаем сертификат и наслаждаемся шифрованным соединением.



Валидные несамоподписанные сертификаты

Для того, чтобы отвечать всем требованиям безопасности, бывает необходимо получить сертификат, выданный сторонней организацией. Это довольно легко сделать за деньги, но и бесплатно получить тоже несложно – в интернете можно найти достаточно соответствующих сервисов. Один из них – это CAcert.
Чтобы заполучить сертификат, идём на сайт, регистрируемся там, получаем ссылку на почту, подтверждаем с её помощью регистрацию, логинимся и попадаем на сайт. Итого на подготовку к процессу у нас ушло полторы минуты.
Теперь надо добавить домен в свой список. Идём в Domains, жмём на Add и следуем инструкциям. Чтобы всё получилось, надо либо быть владельцем домена, либо иметь доступ к служебным почтовым ящикам на этом домене, потому что подтверждающая ссылка будет отослана либо на е-мейл, взятый из whois об этом домене, либо на один из нескольких вариантов адресов на запрашиваемом домене (root, webmaster, admin etc.).
Далее идём в Server Certificates и копируем содержимое сгенерированного ранее файла csr (в моём примере – mysite.csr) и вставляем его в открытое окошко на сайте. Жмём Submit. CAcert сверяет значение Common Name из файла с записанными на нас доменами и генерирует сертификат, который надо скопировать и поместить в файл с расширением crt.

Всё, сертификат получен. Далее можно действовать как и в случае с самоподписанным сертификатом. Однако для того, чтобы не вводить при старте Apache вручную пароль, надо в конфиге Apache заменить строку

SSLPassPhraseDialog  builtin

на

SSLPassPhraseDialog exec:/etc/apache2/echo

Этот скрипт echo должен по запросу от Apache возвращать пароль от сертификата:

#!/bin/sh
/bin/echo somepassword

Ссылки:

http://httpd.apache.org/docs/2.0/ssl/ssl_faq.html
http://httpd.apache.org/docs/2.0/mod/mod_ssl.html
http://www.lissyara.su/articles/freebsd/www/apache_2.2.0+ssl/

Tags: , ,
Category: Apache, Security, Web-dev | 19 Comments »

Comments

19 комментариев на “Настройка SSL в Apache и Tomcat”

  1. Олим

    Здравствуйте! Очень помогли. У меня такой вопрос: можно использовать другой алгоритм вместо RSA? Например, алгоритм обмена ключа Диффи-Хеллман? В этом, алгоритм Диффи-Хеллмана я сам буду писать. Написанный алгоритм в Яве куда добавить, что делать? Можете посоветовать?

  2. Даниил

    Здравстувйте.
    Возникает вопрос: а что делать, если есть связка Apache + Tomcat, причем поддержку SSL обеспечивает Tomcat? Нигде не нашел внятного рецепта, каким образом Apache можно заставить проксировать как HTTP, так и HTTPS запросы.

  3. Vitaly Agapov

    Не сталкивался с такой задачей, но первое, что приходит в голову – это осознание того, что никакой фронтэнд не сможет при проксировании обрабатывать зашифрованный https-трафик без соответствующего сертификата. А даже имея сертификат, он по идее должен расшифровывать получаемый от Томката трафик и зашифровывать его для юзер-агента. В общем, глупость получается.
    На досуге попробую копнуть глубже.

  4. Игорь

    эх мне как всегда не везет, делаю все для апача как тут сказано, но в итоге получаю что браузер не может отобразить эту страницу…

  5. Vitaly Agapov

    Для траблешутинга мало данных. Что пишется в error.log и access.log? Как выглядит конфиг виртуал хоста?

  6. Игорь

    эм да я решил прошлую проблему, на роутере просто порт не прокинулся нормально … (глюканул) вот я прокинул заработало, теперь вот что выдает в браузере :
    Ошибка при установлении защищённого соединения
    При соединении с sitetest.pp.ua произошла ошибка.

    SSL получило запись, длина которой превышает максимально допустимую.

    (Код ошибки: ssl_error_rx_record_too_long)

  7. Игорь

    а вот и вирт хост :

    ServerName sitetest.pp.ua
    DocumentRoot /home/sitetest
    #ErrorLog /home/sitetest/log/g_error.log
    SSLEngine On
    SSLCertificateFile /etc/apache2/ssl/mysite.crt
    SSLCertificateKeyFile /etc/apache2/ssl/mysite_selfsigned.key

  8. Игорь

    эм извеняюсь за то что побеспокоил вас, но я уже сам решил проблему )))

    у меня к вам остался 1 вопросик а вы случайно не подскажите можно ли на 1 ip использовать несколько само подписных сертификатов ?

  9. Vitaly Agapov

    Конечно, можно. Но есть одно НО. Виртуал хосты должны быть name-based. То есть у каждого виртуал хоста должно быть своё доменное имя.

  10. Игорь

    да она не вилезла потому что я поправил порт в конфиге, он у меня с какогото перепуга был 80.
    шяс лог ошибок выложу.

  11. Игорь

    вобшем я выложил лог ошибок за сегодняшний день по адресу
    http://sitetest.pp.ua

    З.Ы. как посмотрите скажите я его тогда удалю )))
    и вопрос который я задавал уже но все же :
    ” а вы случайно не подскажите можно ли на 1 ip использовать несколько само подписных сертификатов ? “

  12. Игорь

    извеняюсь за повтор в предыдушем посте просматрел что вы мне уже ответили )) эи тоесть надо указывать в вирт хоте так :

    либо вот так

    ну как бы это они эквивалентны, правильно я вас понял?

  13. Игорь

    блин теги удалились :
    VirtualHost domain.ru:443
    либо вот так :
    VirtualHost *:443

    я правильно вас понял ?

  14. Игорь

    тоесть я имею виду несколько разных сервитификатов, сгенереных для каждого домена ….

  15. Vitaly Agapov

    Посмотрел лог. Главное, что бросилось в глаза – это то, что Common Name не совпадает с именем сайта. Об этом в статье выше тоже написано. То есть при генерации сертификата в поле CN надо указывать доменное имя, на котором сертификат планируется использовать, то есть в данном случае sitetest.pp.ua.
    Впрочем, когда я сам зашёл на https://sitetest.pp.ua, ошибки не вылезло, и сертификат нормально съелся.

    А по поводу нескольких сайтов с https на одном Апаче, здесь всё немного сложнее и выглядеть будет примерно так:

    <VirtualHost *:80>
    ServerName site1.domain.com

    </VirtualHost>
    <VirtualHost *:443>
    ServerName site1.domain.com

    </VirtualHost>
    <VirtualHost *:80>
    ServerName site2.domain.com

    </VirtualHost>
    Listen 543
    <VirtualHost *:543>
    ServerName site2.domain.com

    </VirtualHost>

    То есть порты для ssl должны быть разными.

  16. Игорь

    эм интересно насчет портов, я вот тестил и у меня вроди 1 порте все пашет … ну по краней мере в браузере ошибок нет никаких над шяс логи посмотреть…

  17. Игорь

    так ну что перещапустил апачь чтоб посмотреть какие он ошибки в лог мне выложит, и то что он мне выложил из свежил ошибок я выложил по адресу
    https://sitetest.pp.ua/error.log
    ах да как бы я проверял сертификат 2 созданый на сайте
    https://studlife.pp.ua

  18. Vitaly Agapov

    На одном порту ошибок не будет вылезать. Но надо посмотреть, действительно ли по https отдаёт нужные сайты по их доменному имени.

  19. Игорь

    да отдает, проверял …

Leave a comment

 Comment Form