2012-02-01 23 views
3

我已經閱讀了一些相關的問題,但他們都沒有幫助我。所以,讓我給你一個小背景故事: 我正在寫一個投票系統,其中有幾個服務器管理註冊,交換投票卡和投票。在註冊用戶提交自己的個人數據時,服務器會檢查數據匹配的數據庫,然後接受用戶的證書,並把它添加到其信任這樣的:在java(自定義)truststore中管理多個條目

KeyStore.Entry entry = new KeyStore.TrustedCertificateEntry(cert); 
trustKeyStore.setEntry(alias, entry, null); 

,然後存儲在信任庫文件中。在註冊期間,許多用戶註冊他們的證書,然後他們連接到請求客戶端和服務器身份驗證的另一臺服務器,並且在這裏出現問題。我認爲這個「其他服務器」可以使用前面提到的trustStore來與用戶一起執行SSLHandshake,但看起來trustStore'只記住最後一個註冊的用戶。我喜歡這個創建SSLServerSocket:

tmf.init(trustKeyStore); 
    kmf.init(keyStore, password); 

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

    // create SSLSocketFactory and SSLSocket 
    SSLServerSocketFactory sslSocketFactory = (SSLServerSocketFactory)sslCtx.getServerSocketFactory(); 
    sslSocket = (SSLServerSocket)sslSocketFactory.createServerSocket(1234); 

正如我前面提到的,兩面認證工作正常,但僅限於信任庫中的最後一項。所以,現在最後我的問題是 - 我必須爲每個用戶創建一個單獨的trustStore嗎?我必須重載TrustManager嗎?基本上,有什麼辦法可以讓SSLContext/SSLEngine遍歷我的所有可信證書,並且如果找到匹配的證書,可以通過握手進行處理?

UPDATE

我想我需要澄清一些事情。首先,這不是一個web應用程序,只是一個普通的客戶端服務器java swing應用程序。每一個使用的證書(服務器或客戶端)都是自簽名的,但是服務器的證書被構建到客戶端應用程序中,所以當客戶端連接到服務器時,他可以比較證書(作品)。我也試圖像Bruno所建議的那樣解決它 - 在用戶註冊後,服務器檢查他的數據是否有效,是否爲客戶端發佈新證書,將其添加到他的信任庫併發送給客戶端。但即使是最後一個註冊客戶也沒有效果。

+0

您是否爲每個用戶使用* *別名?或者每次覆蓋信任庫條目? – Borealid 2012-02-01 00:26:12

+0

想知道,這些用戶證書是自籤嗎?他們如何獲得他們的證書? – Bruno 2012-02-01 00:31:05

+0

當然,我使用不同的別名:)我檢查了,trsutStore.aliases()確實產生了整個列表。所有用戶證書都是自簽名的。 – michauwilliam 2012-02-01 00:35:10

回答

3

我已經解決了這個問題。經常發生,我身邊出現了一個愚蠢的錯誤 - 其中一臺服務器覆蓋了整個密鑰庫。我花了很多時間去研究如何建立這個ssl通信,我在網上沒有發現太多的東西,所以我希望這對未來有幫助。

爲了設置僅服務器通信,您需要執行以下操作: 1.在客戶端獲取(以某種方式,在我的情況下,它是在客戶端應用程序中構建的)服務器的證書並將其添加到您的信任庫這樣的:

 KeyStore clientTrustedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
     clientTrustedKeyStore.load(null, "password".toCharArray()); 
     KeyStore.Entry entry = new KeyStore.TrustedCertificateEntry(cert); 
     clientTrustedKeyStore.setEntry("alias", entry, null); 
  1. 在創建客戶端或服務器端上的SSLSockets,你需要「喂」了的SSLContext與密鑰庫(服務器),或的trustStore(客戶端)到的SSLContext:

    tmf = TrustManagerFactory.getInstance("SunX509"); 
    kmf = KeyManagerFactory.getInstance("SunX509"); 
    tmf.init(trustKeyStore); 
    kmf.init(keyStore, password); 
    
    // create, init SSLContext 
        SSLContext sslCtx = SSLContext.getInstance("TLS"); 
        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 
    

然後創建sslsocketfactory和套接字。

  1. 創建套接字(僅在服務器端是有用的)組的認證方式後:

    sslSocket.setNeedClientAuth(布爾值);

在設置雙面驗證的情況下,事情你必須改變是:身份驗證模式(明顯),將證書上的客戶端和服務器端的信任庫。

雙方驗證的另一個問題是,下面的方案不起作用(儘管至少對我而言似乎是合乎邏輯的): 1.服務器頒發自己的自簽名證書,將其添加到它的然後將它發送給客戶端,供他在將來的連接中進行身份驗證。 爲什麼它不工作?因爲當客戶端獲取證書,他只能把它添加到自己的密鑰庫這樣的:

ks.setCertificateEntry(alias,cert); 

whichjust不會做,當談到SSLHandshake,你必須自己進行驗證與證書添加到與參數的setEntry密鑰庫:

ks.setKeyEntry(alias,keyPair.getPrivate(),keyStorePassword,certChain); 

其中certChain是即

Certificate[] certChain = {myCert}; 

這將是所有的,我敢肯定,對某些人來說,這一切都是比較明顯的,但我希望這將有助於一些像我這樣的SSL初學者:)

+0

不檢查應用程序中的實際證書是什麼,肯定會給你帶來很多問題,因爲你沒有任何東西需要比較或引用(甚至只是爲了審計)。除非您掌握了用戶的公鑰,否則您將沒有任何東西將任何請求與任何特定用戶綁定,因此您不知道他們是否已經使用過該系統。您將無法以合理的方式將所獲得的主題DN綁定到任何實際用戶(所有證書都是自簽名的),並且您有時可能會與相同的DN發生衝突。 – Bruno 2012-02-01 12:27:01

+1

你是什麼意思?我確實擁有用戶的公鑰,它位於我保存在服務器上的證書中。無論如何,在這個項目中,用戶可以多次投票或登記他/她想要的次數,但第二次不會做任何事情,每個用戶都有一個通過安全渠道(不涉及計算機)獲得的唯一ID,並且投票是基於RSA致盲簽名,可防止多次投票。 – michauwilliam 2012-02-01 12:39:16

+0

然後,通過信任存儲在TLS級別進行身份驗證的目的不甚清楚。 (我猜你必須在握手之後在應用程序級別上使用cert/pub key來做一些事情。)很高興看到有關使用證書的步驟的更多細節可以多說。 – Bruno 2012-02-01 13:19:28

2

(修改後的問題編輯。)

方法1:要解決這將是使用CA,可能是您自己

的一種方式。

方法2:

接受在握手級別的任何客戶證書。雖然接受從一個客戶端點的任何服務器證書是一個壞主意,因爲它引入了MITM攻擊的可能性(見herehere,例如),在握手期間接受任何客戶證書不存在同樣的問題。

在握手結束時,提供的服務器證書仍然驗證,你就會知道:

  • 客戶端和服務器之間的通信安全地建立,因爲它會使用HTTPS無客戶端 - 證書認證。
  • 客戶端擁有在握手期間提供的證書的私鑰。這由TLS CertificateVerify消息來保證,該消息使用客戶端的私鑰簽署到目前爲止握手過程中交換的所有消息的串聯,包括服務器證書和客戶端證書。這實際上並不取決於客戶端證書本身的(信任)驗證,並且只有在客戶端證書中的公鑰可以驗證TLS CertificateVerify消息中的簽名時纔有效。

有了這個,您將知道服務器獲取的客戶端證書被具有其匹配的私鑰的人使用。 你不知道的是誰公鑰證書所屬的,即另一端的用戶是誰,因爲你缺少通常使用CA執行的驗證步驟,驗證步驟本身應該依賴一個out在頒發證書之前使用帶外機制。由於他們使用的是自簽名證書,因此無論如何您都必須執行此帶外步驟,可能是通過註冊期間的某些信息執行的。

這樣做可以讓您更輕鬆地管理您的用戶以及他們使用證書的方式。您可以簡單地在服務器通用的數據庫中存儲和/或查找當前用戶的證書。您可以通過查看應用程序中的SSLSession來訪問證書。您將在由getPeerCertificates()返回的數組的位置0中找到用戶證書。然後,您可以將您看到的憑證(再次,因爲雙向握手成功)與您以前見過的憑證進行比較,以便在應用程序中進行身份驗證(和授權)。

請注意,即使您剛剛接受了您繼續添加的自簽名證書,除了主題DN之外,您仍然必須將此公鑰信息作爲系統的一部分進行跟蹤,因爲您無法控制主題DN(兩個用戶可以選擇使用相同的,有意或無意的)。

要實現這一點,則需要與使用的X509TrustManager不扔在其checkClientTrusted方法的任何異常的SSLContext配置您的服務器。 (如果您爲其他目的使用相同的SSLContext,我將獲得默認的PKIX TrustManager並將呼叫委派給它checkServerTrusted)。由於您可能不會知道這些自簽名的Subject/Issuer DN證書將會是,您應該通過返回getAcceptedIssuers()(參見示例here)中的空數組發送空的接受CA(*)的列表。 (*)TLS 1.0對此主題沒有提及,但TLS 1.1明確允許使用空列表(未指定行爲)。實際上,即使使用SSLv3/TLSv1.0,它也可以與大多數瀏覽器一起工作:在這種情況下,瀏覽器將顯示可供選擇的完整證書列表(或選擇第一個發現它的配置爲自動選擇)。


(我要走了什麼更具體的瞭解HTTP在這裏...有點斷章取義了,但是這可能會感興趣的人。)

方法1:

您可以將證書頒發作爲初始註冊的一部分進行整合。當用戶註冊他們的詳細信息時,他們也會申請一個由您的註冊服務器立即發佈到他們的瀏覽器中的證書。 (如果您對此不熟悉,可以在瀏覽器中使用<keygen />,CRMF(在Firefox上)或IE上的ActiveX控件(這取決於Windows的版本)頒發證書。)

方法2:

在一個Servlet環境中,你可以得到它在servlet請求javax.servlet.request.X509Certificate屬性

此外,這往往會改善用戶體驗,因爲拒絕一個客戶端證書不一定會終止連接。您仍然可以爲網頁提供服務(HTTP 401狀態可能,儘管從技術上講,它需要伴隨一個質詢機制,並且沒有一個用於證書)至少會告訴用戶有什麼不對。握手失敗(握手過程中的客戶端證書驗證會導致問題)可能會讓那些不太瞭解證書的用戶感到困惑。缺點是很難「註銷」(類似於HTTP基本認證),因爲大多數瀏覽器缺乏用戶界面支持。

對於實現,如果你正在使用Apache Tomcat,你可以,如果你使用Apache Tomcat 6.0.33或以上有興趣在this SSLImplementationthis answer

+0

謝謝你的努力:) – michauwilliam 2012-02-01 12:17:04