Шрифт:
private void useSaneCiperSuites(SSLSocket s) {
s.setEnabledCipherSuites({"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA"});
}
Проверка сертификата
Разные API в разной степени поддерживают базовую проверку сертификата. Некоторые по умолчанию проверяют дату и цепочку доверия, в других вообще не реализовано ни то, ни другое. Большинство же находятся где–то посередине, например включают средства проверки, но не выполняют ее по умолчанию.
Обычно (хотя и не всегда) для выполнения проверки нужно получить ссылку на сертификат сервера (часто его называют сертификатом «партнера» (peer certificate)). Например, в Java до инициализации SSL–соединения можно зарегистрировать объект–слушатель HandShakeCompletedListener для объекта SSLSocket. Слушатель должен реализовать такой метод:
public void handshakeCompleted(HandShakeCompletedEvent event);
Получив объект, описывающий событие, вы можете далее написать:
event.getPeerCertificates ;
В результате будет возвращен массив объектов типа javasecuritycert.Certi–ficate. Certificate – это базовый класс, фактический тип полученных объектов обычно представлен производным классом java.security.cert.X509Extension, хотя иногда встречаются и устаревшие сертификаты (типа java.security.cert.X509, которому наследует X509Extension).
Первым в массиве идет сертификат партнера, а за ним – сертификаты удостоверяющих центров по цепочке вплоть до корневого. При вызове этого метода Java API выполняет некоторые проверки сертификатов с целью убедиться в поддержке выбранного семейства шифров, но цепочка доверия не контролируется. Выбрав такой подход, вы должны самостоятельно произвести все проверки, используя открытый ключ (п+1)-го сертификата для контроля n–го, а дойдя до корневого сертификата, сравнить его со списком известных корневых УЦ. (В Java есть и другие способы проверки сертификатов, но они не менее сложны.) Например, чтобы проверить сертификат партнера, когда уже установлено, что вторым в массиве идет доверенный сертификат, нужно сделать следующее:
try {
((X509Extension)(certificate[0])).verify(certificate[1].getPublicKey);
} catch (Exception e) {
/* Проверка сертификата завершилась неудачно. */
}
Отметим, что здесь не проверяется корректность даты каждого сертификата. Это можно было бы сделать так:
try {
((X509Extension)(certificate[0])).checkValidity;
} catch (Exception e) {
/* Проверка сертификата завершилась неудачно. */
}
В каркасе .NET имеются аналогичные средства:
X509Certificate2 cert = new X509Certificate2(@"c:\certs\server.cer");
X509Chain chain = new X509Chain;
chain.Build(cert);
if (chain.ChainStatus.Length > 0) {
// Были ошибки
}
Проверка имени хоста
Предпочтительный способ проверить имя хоста – воспользоваться полем dnsName из расширения subjectAltName, если оно имеется и заполнено. Но часто имя хоста записывается в поле DN. API для проверки этих полей варьируются в широких пределах.
В JavaJSSE в предположении, что мы имеем дело с сертификатом X509Exten–sion, можно следующим образом проверить значение subjectAltName, а в случае неудачи обратиться к полю DN:
private Boolean validateHost(X509Extension cert) {
String s = "";
String EXPECTED_HOST = "www.example.com";
try {
/* 2.5.29.17 – это OID, стандартное числовое представление имени
расширения */
s = new String(cert.getExtensions("2.5.29.17"));
if (s.equals(EXPECTED_HOST)) {
return true;
}
else { /* если расширение есть, но не соответствует
* ожидаемому значению, не будем проверять поле DN,
* которое НЕ ДОЛЖНО иметь другое значение. */
return false;
}
} catch (Exception e) {} /* Такого расширения нет, проверим DN */
if (cert.getSubjectDN.getName.equals(EXPECTED_HOST)) {
return true;
} else {
return false;
}
}
В каркасе .NET имя хоста проверяется автоматически при вызове метода SslStream. AuthenticateAsClient.
Проверка отзыва сертификата
Самым популярным способом проверки факта отзыва сертификата (если вообще можно говорить о популярности столь нечасто применяемой методики) по–прежнему остается сверка с CRL–списком. Следовало бы рекомендовать протокол OCSP, но УЦ не торопятся с его поддержкой. Компания VeriSign поддерживает его, пожалуй, лучше других, она готова отвечать на запрос о статусе каждого когда–либо выпущенного ей сертификата (включая также сертификаты, выпущенные компаниями RSA и Thawte). Ее сервер находится по адресу(если вы пользуетесь библиотекой, поддерживающей протокол OCSP).
Но обратимся к CRL–спискам. Во–первых, для сверки вы должны иметь много CRL–списков. Необходимо узнать адрес точки распространения CRL, которая (если существует) может быть доступна по протоколам HTTP или LDAP. Иногда адрес указан в сертификате, а иногда – нет. В табл. 10.1 приведен список известных точек распространения CRL, работающих по протоколу HTTP. Можете использовать этот список в случае, когда адрес отсутствует в самом сертификате.
Во–вторых, нужно решить, как часто загружать CRL–списки. Обычно УЦ регулярно обновляют списки отозванных сертификатов, даже если никаких новых записей в них не появилось. Мы рекомендуем загружать новую версию с точно такой же периодичностью, не позже чем через 24 ч после обновления.