Ошибка при открытии и закрытии SSLSocket без записи каких-либо данных - PullRequest
3 голосов
/ 30 июня 2011

Простой сервер

listen = getServer();
Logger.getAnonymousLogger().info("Listening to "+listen.toString());
SSLSocket client = (SSLSocket)listen.accept();
// adding this line fixes everything - client.write(42);
client.close();

и простой клиент

SocketFactory sockMaker = SSLSocketFactory.getDefault();
Socket server = sockMaker.createSocket("localhost", 1443);
int retval = server.getInputStream().read();
assert retval == -1;
server.close();

Если я ничего не пишу в сокет SSL, на стороне клиента выдается исключение:

Exception in thread "main" javax.net.ssl.SSLException:\
  Received close_notify during handshake
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:190)

Я не понимаю, почему это так. Требует ли спецификация SSL / TLS для записи чего-либо в сокет?

См. полный пример .

Ответы [ 2 ]

9 голосов
/ 30 июня 2011

Вам не нужно ничего записывать в сокет как таковой, но если вы закроете его сразу же, он сгенерирует предупреждение close_notify (хотя это и называется «предупреждение», это часть обычного способа закрытия Сокет TLS / SSL).

Кроме того, сокеты SSL / TLS спроектированы так, чтобы вести себя «почти», как обычные сокеты TCP, но есть детали, где они не (и не могут) из-за того, как работает SSL / TLS. В частности, в начале соединения SSL / TLS происходит рукопожатие SSL / TLS, которое включает в себя несколько операций чтения / записи с каждой стороны перед отправкой любых данных приложения.

Документация для SSLSocket гласит:

Начальное рукопожатие на этом соединение может быть инициировано в одном из три способа:

  • вызов startHandshake, который явно начинает рукопожатие, или
  • любая попытка чтения или записи данных приложения на этом сокете вызывает неявное рукопожатие, или
  • вызов getSession пытается установить сеанс, если в данный момент его нет действительный сеанс и неявный рукопожатие сделано.

По сути, getInputStream().read() клиентом в вашем примере инициирует рукопожатие, в результате чего сервер продолжает работу с accept() и выполняет рукопожатие на его стороне . Однако, так как вы закрываете его (обычно, но сразу) на стороне сервера, вы даже не оставляете время для завершения рукопожатия. Следовательно, close_notify отправляется во время рукопожатия, что вызывает исключение, которое вы получаете. Если бы вы пытались читать или писать со стороны сервера, рукопожатие, по крайней мере, было бы завершено.

РЕДАКТИРОВАТЬ : После комментария @ EJP я должен уточнить, что я имел в виду:

  • createSocket("localhost", 1443) на стороне клиента устанавливает соединение, и сервер принимает его через accept().
  • getInputStream().read() на стороне клиента заставляет его инициировать рукопожатие. Таким образом, он отправляет на сервер сообщение ClientHello TLS.
  • Поскольку сервер использует close() сразу после принятия сокета, он отправляет предупреждение close_notify. Поскольку сервер не начал чтение / запись, он не начал рукопожатие (и, следовательно, не завершает его).

Обратите внимание, что цель ServerSocket.accept(), которую реализует SSLServerSocket, состоит в том, чтобы создать SSLSocket, не обязательно что-либо делать с ним. SSLServerSocket настраивает его, но продолжение рукопожатия выходит за рамки. С одной стороны, это может звучать так, что SSLSocket ведет себя более прозрачно, как обычный TCP-сокет; с другой стороны, это будет означать чтение из основного потока TCP, поэтому у него будут побочные эффекты. Я не пробовал, но SSLSocket, созданный SSLServerSocket, все еще может быть настроен в клиентском сокете . В конце концов, как гласит RFC 2246 : "client: прикладная сущность, которая инициирует соединение TLS с сервером. Это может означать или не означать, что клиент инициировал базовое транспортное соединение." Это определенно будет иметь последствия в отношении прозрачности и когда делать рукопожатие с точки зрения API.

(Написание API для сокетов SSL / TLS, которое сопоставляется с API обычных сокетов TCP, является непростым делом, и Java не делает для этого слишком плохую работу. Настоящее «удовольствие» начинается с асинхронного TLS с использованием * Каналы 1066 * и NIO. Еще лучше, если учесть, что любая из сторон может инициировать новое рукопожатие в любое время: последствия для уровня выше не определены в отношении TLS, что может привести к неловким проблемам .)

1 голос
/ 01 июля 2011

Ситуация неверна. Вы пытаетесь прочитать данные, которые не отправлены. Это ошибка протокола приложения. Все утверждения в ответе Бруно также применимы. Клиент пытается сделать рукопожатие; сервер пытается закрыть соединение. Возможно, закрытие сервера может инициировать рукопожатие, если это еще не было сделано, но это не так.

Как вы уже заметили, другой обходной путь - вызвать startHandshake () на любом конце.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...