2012-03-07 61 views
7

我的任務是實現可以在同一端口上處理SSL和非SSL消息的自定義/獨立Java Web服務器。使用自簽名證書和SSLEngine(JSSE)的SSL握手

我已經實現了一個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作爲客戶端,以測試服務器。兩個Web客戶端的結果相同。
  • 我正在使用附帶的Sun/Oracle JDK 6和Java安全套接字擴展(JSSE)。

我期待着您可能有的任何指導,但請不要告訴我我堅果或使用Netty或Grizzly或其他現有解決方案。目前它不是一種選擇。我只是想明白我做錯了什麼。

在此先感謝!

+0

只是一個愚蠢的問題:你有沒有產生並安裝客戶端證書,例如Firefox通過HTTPS連接到Web服務器時? – Robert 2012-03-07 16:26:40

+0

是的。 FF促使我接受/拒絕證書。我點擊「我接受技術風險」,我可以在工具 - >選項 - >高級 - >加密 - >查看證書 - >服務器下看到證書。事實上,沒有這個,FF不會發送第二個握手消息。 – Peter 2012-03-07 17:01:51

+0

這不是我問的問題。我正在要求客戶證書。 SSL客戶端身份驗證是可選的,主要用於企業環境中,通常與芯片卡結合使用。如果客戶端沒有安裝證書(Firefox部分「您的證書」),則不會發送證書。 – Robert 2012-03-07 20:10:58

回答

9

你有NEED_UNWRAP,所以做一個展開。這反過來可能會給你BUFFER_UNDERFLOW,這意味着你必須做一個閱讀並重試解包。

同樣,當你得到NEED_WRAP,做一個總結:這又可能會給你BUFFER_OVERFLOW,這意味着你必須做一個寫,然後重試包。

這包裝或解包可能反過來可能會告訴你做其他操作:包裝或解包。

只是做它告訴你這樣做。

+0

是的,我想看到NEED_UNWRAP後解開,但有沒有別的閱讀。我可以看到第二個客戶端的握手完整地通過了線路,而SSLEngine解析了它的全部內容。不幸的是,它只是不會產生任何輸出給我發回客戶端。客戶端等待一會兒,最後掛斷電話。 – Peter 2012-03-07 22:29:27

+1

@Peter可能沒有什麼可讀的,但仍可能有東西需要解開。按照我說的方式來做,讓它告訴你什麼時候閱讀,而不是假設每個解包都需要閱讀。通常情況下,服務器在握手過程中的第一個響應包含三個單獨的消息,例如,您將在一行中獲得3個NEED_WRAPs;相反,客戶端可能會在單次讀取中讀取所有內容,但會連續獲取三個NEED_UNWRAPS。在整個握手過程中也是如此。 – EJP 2012-03-08 00:17:36

+0

好,很酷。感謝EJP和更多的調試,我明白現在發生了什麼。 SSLEngine一次打開一條記錄。所以在第二次客戶端握手中,實際上有3條記錄:handshake,change_cipher_spec和另一次握手。我只打開一次,因此SSLEngine只解析了第一條記錄。它需要在生成任何字節發送回客戶端之前處理所有記錄。我終於找回應用程序數據! – Peter 2012-03-08 18:47:32