2012-03-07 5 views
7

Мне поручено реализовать пользовательский/автономный веб-сервер Java, который может обрабатывать сообщения SSL и non-SSL на одном и том же порту.Подтверждение SSL с помощью самоподписанных сертификатов и SSLEngine (JSSE)

Я реализовал NIO-сервер, и он отлично работает для запросов без SSL. У меня есть чертовски время с частью SSL и действительно можно использовать некоторые рекомендации.

Вот что я сделал до сих пор.

Чтобы различать сообщения SSL и не SSL, я проверяю первый байт входящего запроса, чтобы узнать, является ли это сообщением SSL/TLS. Пример:

byte a = read(buf); 
    if (totalBytesRead==1 && (a>19 && a<25)){ 
     parseTLS(buf); 
    } 

В parseTLS() метод, который я экземпляр SSLEngine так:

java.security.KeyStore ks = java.security.KeyStore.getInstance("JKS"); 
    java.security.KeyStore ts = java.security.KeyStore.getInstance("JKS"); 

    ks.load(new java.io.FileInputStream(keyStoreFile), passphrase); 
    ts.load(new java.io.FileInputStream(trustStoreFile), passphrase); 

    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 
    kmf.init(ks, passphrase); 

    TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 
    tmf.init(ts); 

    SSLContext sslc = SSLContext.getInstance("TLS");  
    sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 


    SSLEngine serverEngine = sslc.createSSLEngine(); 
    serverEngine.setUseClientMode(false); 
    serverEngine.setEnableSessionCreation(true); 
    serverEngine.setWantClientAuth(true); 

После SSLEngine экземпляра, я обрабатывать входящие данные с использованием методов разворачивать/обертывание с помощью кода прямо из официального JSSE Samples:

log("----"); 

    serverResult = serverEngine.unwrap(inNetData, inAppData); 
    log("server unwrap: ", serverResult); 
    runDelegatedTasks(serverResult, serverEngine); 

    log("----"); 

    serverResult = serverEngine.wrap(outAppData, outNetData); 
    log("server wrap: ", serverResult); 
    runDelegatedTasks(serverResult, serverEngine); 

Первая часть рукопожатием, кажется, работает нормально. Клиент посылает сообщение квитирования и сервер отвечает сообщением с 4 записей:

handshake (22) 
- server_hello (2) 
- certificate (11) 
- server_key_exchange (12) 
- certificate_request (13) 
- server_hello_done (14) 

Далее, клиент посылает сообщение с тремя частями:

handshake (22) 
- certificate (11) 
- client_key_exchange (16) 

change_cipher_spec (20) 
- client_hello (1) 

handshake (22) 
*** Encrypted Message **** 

SSLEngine разворачивает запрос клиента и разборов записи, но метод wrap создает 0 байт с подтверждением состояния OK/NEED_UNWRAP. Другими словами, мне нечего возвращать клиенту, и рукопожатие доходит до визжащей остановки.

Здесь я застреваю.

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

Я могу только предположить, что с ответом HelloServer что-то не так, но я не могу поверить пальцем в него. Кажется, сервер отправляет действительный сертификат, но клиент ничего не отправляет. Есть ли проблема с моим хранилищем ключей? Или это траст-магазин? Или это имеет какое-то отношение к тому, как я создаю SSLEngine? Я в тупике.

Пара другие точки:

  • Хранилища ключей и доверенные ссылки в коде Snippit выше были созданы с помощью следующего урока: http://www.techbrainwave.com/?p=953
  • Я использую Firefox 10 и IE 9 в качестве клиента к испытанию сервер. Те же результаты для обоих веб-клиентов.
  • Я использую Sun/Oracle JDK 6 и Java Secure Socket Extension (JSSE), входящие в комплект поставки.

Я с нетерпением жду любых указаний, которые могут возникнуть у вас, но, пожалуйста, не говорите мне, что я орехи или использование Netty или Grizzly или другого существующего решения. Его просто не вариант в это время. Я просто хочу понять, что я делаю неправильно.

Спасибо заранее!

+0

Просто глупый вопрос: вы создали и установили клиентские сертификаты, например. Firefox при подключении к вашему веб-серверу через HTTPS? – Robert

+0

Да. FF попросил меня принять/отклонить сертификат. Я нажал «Я принимаю технические риски», и я вижу сертификат в разделе «Сервис-> Параметры-> Дополнительно-> Шифрование-> Просмотр сертификатов-> Сервер. Фактически, FF не отправит второе сообщение подтверждения без этого. – Peter

+0

Это не то, о чем я просил. Я просил сертификат клиента. Аутентификация SSL-клиента является необязательной и в основном используется в корпоративных средах, часто в сочетании с чип-картами. Если у клиента нет установленного сертификата (раздел Firefox «Ваш сертификат»), он не отправит его. – Robert

ответ

9

У вас NEED_UNWRAP, так что разворачивайте. Это, в свою очередь, может дать вам BUFFER_UNDERFLOW, что означает, что вам нужно прочитать и повторить разворот.

Аналогично, когда вы получаете NEED_WRAP, сделайте обертку: это, в свою очередь, может дать вам BUFFER_OVERFLOW, что означает, что вам нужно написать и повторить обертку.

Эта обертка или разворот, в свою очередь, может сказать вам сделать еще одну операцию: обернуть или развернуть.

Просто сделайте то, что он вам скажет.

+0

Да, я попытался развернуть, увидев NEED_UNWRAP, но читать нечего. Я вижу, что второе клиентское рукопожатие полностью встречает провод, и SSLEngine разбирает все это. К сожалению, он просто не дает никаких результатов для отправки обратно клиенту. Клиент ждет немного и в конце концов вешает трубку. – Peter

+1

@Peter Не может быть ничего, что можно прочитать, но может быть что-то еще развернуть. Сделайте это так, как я сказал, пусть расскажет вам, когда читать, а не предполагать, что каждый разворот нуждается в чтении. Как правило, первый ответ сервера в рукопожатии состоит из трех отдельных сообщений, например, поэтому вы получите 3 NEED_WRAP в строке: наоборот, клиент, вероятно, прочитает все это в одном чтении, но получит три последовательных NEED_UNWRAPS. То же самое относится к рукопожатию. – EJP

+0

OK, прохладно. Благодаря EJP и немного дополнительной отладке, я понимаю, что происходит сейчас. SSLEngine разворачивает одну запись за раз. Таким образом, во втором рукопожатии клиента есть фактически 3 записи: рукопожатие, change_cipher_spec и другое рукопожатие. Однажды я вызывал разворот, поэтому SSLEngine только разобрал первую запись. Ему необходимо было обработать все записи, прежде чем генерировать любые байты для отправки обратно клиенту. Я, наконец, возвращаю данные приложения! – Peter