Как разрешены имена серверов сертификатов SSL/Можно ли добавлять альтернативные имена с помощью keytool?

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

Как разрешены имена серверов сертификатов SSL?

Почему браузеры, похоже, используют поле CN сертификата, но механизм Java, похоже, только смотрит только на "субъектные альтернативные имена"?

Можно ли добавлять альтернативные имена сертификату SSL с помощью keytool? Если нет, использует openSSL вместо этого хороший вариант

Немного фона: Мне нужно, чтобы главный сервер связывался с несколькими серверами с помощью HTTPS. Очевидно, что мы не хотим покупать SSL-сертификаты для каждого сервера (их может быть много), поэтому я хочу использовать самозаверяющие сертификаты (я использую keytool для их создания). После того, как я добавлю сертификаты как доверенные в ОС, браузеры (IE и Chrome) с радостью согласуют соединение как надежное. Однако даже после добавления сертификатов в Java cacerts Java по-прежнему не будет принимать соединение как доверенное и выдает следующее исключение:

Вызывает: java.security.cert.CertificateException: нет альтернативных имен объектов  настоящее время         at sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:142)         at sun.security.util.HostnameChecker.match(HostnameChecker.java:75)         at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(X509T rustManagerImpl.java:264)         at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted( X509TrustManagerImpl.java:250)         at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Clien tHandshaker.java:1185)         ... еще 14

Я обнаружил, что могу заставить Java доверять сертификату, реализующему мой собственный HostNameVerifier, который я скопировал здесь: com.sun.jbi.internal.security.https.DefaultHostnameVerifier просто проверить (кстати, имя хоста, переданное в качестве аргумента в HostnameVerifier, правильно, поэтому я думаю, что это должно было быть принято).

Я использую поле сертификата CN как имя хоста (обычно IP-адрес).

Может кто-нибудь, пожалуйста, скажите мне, что я делаю что-то неправильно и указываю мне в правильном направлении?

Ответ 1

Как должно быть проверено имя хоста определено в RFC 6125, которое довольно недавно и обобщает практику для всех протоколов и заменяет RFC 2818, что было характерно для HTTPS. (Я даже не уверен, что Java 7 использует RFC 6125, который может быть слишком недавним для этого.)

Из RFC 2818 (Раздел 3.1):

Если присутствует расширение subjectAltName типа dNSName, это ДОЛЖНО использовать в качестве тождества. В противном случае (наиболее конкретное) общее имя поле в поле "Тема" сертификата ДОЛЖНО использоваться. Несмотря на то что использование Common Name - существующая практика, она устарела и Органам сертификации рекомендуется использовать dNSName.

[...]

В некоторых случаях URI указывается как IP-адрес, а не Имя хоста. В этом случае должно присутствовать имя субъекта iPAddressAltName в сертификате и должен точно соответствовать IP в URI.

По существу, конкретная проблема возникает из-за того, что вы используете IP-адреса в своем CN, а не в имени хоста. Некоторые браузеры могут работать, потому что не все инструменты строго следуют этой спецификации, в частности, потому что "наиболее конкретный" в RFC 2818 не определен четко (см. Обсуждения в RFC 6215).

Если вы используете keytool, на Java 7, keytool имеет возможность включить альтернативное имя объекта (см. таблицу в документации для -ext): вы можете использовать -ext san=dns:www.example.com или -ext san=ip:10.0.0.1.

EDIT:

Вы можете запросить SAN в OpenSSL, изменив openssl.cnf (он выберет копию в текущем каталоге, если вы не хотите редактировать глобальную конфигурацию, насколько я помню, или вы можете выбрать явное местоположение используя переменную среды OPENSSL_CONF).

Задайте следующие параметры (сначала найдите соответствующие разделы в скобках):

[req]
req_extensions = v3_req

[ v3_req ]
subjectAltName=IP:10.0.0.1
# or subjectAltName=DNS:www.example.com

Также неплохо использовать переменную окружения для этого (вместо того, чтобы фиксировать ее в файле конфигурации): http://www.crsr.net/Notes/SSL.html