2012-10-15 41 views
5

在使用JSSE和TLS的Java中。我在服務器和客戶端之間創建了一個安全套接字。在最終讓套接字安全連接之後,我仍然對我現有的代碼的安全性有一個基本的問題。我遵循教程中的說明,有時JavaDoc中的文檔非常精確,但有點模糊,除非您使用Swaheli的術語方言......Java JSSE TLS - 此連接是否在雙向安全加密?

我一直在使用C++進行網絡編程很長一段時間。向Java的過渡很容易。然而,最近我發現審慎做出交通安全。這就是說:

我想創建一個安全的套接字,就像網頁瀏覽器創建一個安全的套接字一樣,所以雙向的流量都是加密的。客戶端可以看到他們從服務器發送的個人帳戶信息(如果攔截的話非常糟糕),並且客戶端可以安全地將他們的用戶名和密碼發送到服務器(如果截獲也很糟糕)。

我完全知道公鑰密碼系統是如何工作的,但是公鑰密碼系統只有一個副作用。您將您的公鑰發送到客戶端,客戶端使用公鑰進行加密,並將數據發送到服務器,但服務器只能解密。現在據我所知,服務器使用私鑰來加密發送到客戶端的消息,並且需要添加另一層安全性以防止任何具有公鑰的人都能夠解密它。

  1. 我有一個公共/存儲在文件中public.key和private.key私鑰對(我使用JSSE的keytool實用
  2. 我包含在客戶端public.key
  3. 我包括私人做這些。在服務器密鑰

客戶端類:

KeyStore keyStore; 
    TrustManagerFactory tmf; 
    KeyManagerFactory kmf; 
    SSLContext sslContext; 
    SecureRandom secureRandom = new SecureRandom(); 
    secureRandom.nextInt(); 

     keyStore = KeyStore.getInstance("JKS"); 
     keyStore.load(this.getClass().getClassLoader().getResourceAsStream("server.public"),"public".toCharArray()); 
     tmf = TrustManagerFactory.getInstance("SunX509"); 
     tmf.init(keyStore); 
     kmf = KeyManagerFactory.getInstance("SunX509"); 
     kmf.init(keyStore, "public".toCharArray()); 
     sslContext = SSLContext.getInstance("TLS"); 
     sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), secureRandom); 
     SSLSocketFactory sslsocketfactory = sslContext.getSocketFactory(); 
     SSLSocket sslsocket = (SSLSocket)sslsocketfactory.createSocket("localhost", 9999); 

服務器類:

String passphrase = "secret" 
    KeyStore keyStore; 
    TrustManagerFactory tmf; 
    KeyManagerFactory kmf; 
    SSLContext sslContext; 
    SecureRandom secureRandom = new SecureRandom(); 
    secureRandom.nextInt(); 

     keyStore = KeyStore.getInstance("JKS"); 
     keyStore.load(this.getClass().getClassLoader().getResourceAsStream("server.private"),passphrase.toCharArray()); 
     tmf = TrustManagerFactory.getInstance("SunX509"); 
     tmf.init(keyStore); 
     kmf = KeyManagerFactory.getInstance("SunX509"); 
     kmf.init(keyStore, passphrase.toCharArray()); 
     sslContext = SSLContext.getInstance("TLS"); 
     sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), secureRandom); 
     SSLServerSocketFactory sslserversocketfactory = sslContext.getServerSocketFactory(); 
     SSLServerSocket sslserversocket = 
     (SSLServerSocket)sslserversocketfactory.createServerSocket(9999); 

/* ** * ** *問題* ** * ** * */

一切正常!我將套接字連接到BufferedReader和BufferedWriter,並在accept()之後開始精美地來回交談。從客戶端連接並啓動我的客戶端和服務器發送/接收循環。

現在我知道在這一點上客戶端到服務器的通信是安全的。只有服務器密鑰才能解密來自客戶端的流量。但是,服務器到客戶端的通信呢?客戶端密鑰可以解密來自服務器的消息,但在Public Key Crypto 101中,您將瞭解到客戶端現在應該向服務器發送公鑰。這是在這個代碼幕後發生的嗎? SSLContext是否處理這個問題?或者現在我有一個從客戶端到服務器的加密連接,我現在是否希望爲客戶端生成一個私鑰/公鑰對?

讓我知道在上面的代碼中發送和接收的流量是否真的在兩個方向都是安全的。

+0

你是如何「包括」在客戶端和private.key服務器上public.key? – raghav

回答

1

,你的確有PKI如何工作的理解,但你缺少SSL實施的兩個關鍵部分。首先大多數PKI算法允許以兩種方式加密通信量。您可以使用公鑰發送加密消息,只有擁有私鑰的人才能讀取,這稱爲加密。您還可以使用私鑰對消息進行加密,任何擁有公鑰的人都可以對其進行解密,這稱爲數字簽名。

另一個失落的一角是,SSL不使用PKI來發送客戶端和服務器之間的網絡流量。它使用對稱加密算法。然而,對稱加密(稱爲會話密鑰)的關鍵是使用相當複雜的使用PKI的挑戰 - 響應協議和證書來建立。在此階段,服務器向客戶端證明它不在中間,如果客戶端有更強的身份驗證並且已建立對稱會話密鑰,則客戶端可以選擇向服務器證明其證書。更多細節在這裏http://tools.ietf.org/html/rfc5246

對稱密鑰用於加密使用諸如RC5或AES算法的交通

+0

感謝您的回覆,但是我想知道的是,如果這個針對TLS的JSEE API的實現是加密從服務器到客戶端的流量(我發佈的代碼),或者是任何使用公鑰的客戶端都能夠解密從服務器到客戶端的流量? – SigSeg

+1

Nope,TLS和SSL意味着點對點隱私。在握手過程中生成的對稱會話密鑰包含客戶端生成的一個隨機數據,並使用服務器公鑰進行加密。所以只有那些服務器的私鑰才能解密它。該隨機數據用於生成用於實際加密流量的會話密鑰。因此,那些沒有服務器私鑰的服務器將無法解密這些數據到服務器,並且隨後將不會有會話密鑰來窺探加密流量。 – Vlad

+0

那是個好消息。在文檔中沒有真正的「這種方法生成會話密鑰來保護雙向通信」,或者它確實很微妙。在讀完PKI並找到本教程後,我想我必須在設置初始套接字併爲不同客戶端管理不同密鑰之後,從客戶端到服務器協商另一個套接字......很高興看到這一切都已處理完畢。順便說一句,我發現,最好的教程呢,http://www.cs.wmich.edu/~alfuqaha/Spring07/cs6030/lectures/jsse.pdf – SigSeg

6

在SSL/TLS證書(及他們的私有密鑰)僅用於SSL/TLS認證方(通常,只有服務器使用證書)。

實際加密使用本握手期間協商共享/對稱密鑰進行,從預主密鑰導出用認證密鑰交換的形式進行交換(參照TLS Specification, Section F.1.1

如何這個認證密鑰交換完成取決於密碼套件,但最終結果是相同的:雙方之間共享的預主祕密,保證只有客戶端和服務器以及其證書的私鑰才能知道。

接下來預主祕密交換,主祕密本身被計算,從中導出一對祕密密鑰(如中所述):一個用於客戶端寫入(以及服務器讀取),另一個用於服務器寫入(以及客戶端讀取)。 (MAC祕密也會產生,以保證連接的完整性。)

原則上,不是所有的加密套件提供加密和認證密鑰交換(見Cipher Suite Definitions section),但在JSSE默認啓用與SunJSSE提供商所有那些做(請參閱SunJSSE提供商文檔中的Cipher Suite tables)。簡而言之,請勿啓用anonNULL的密碼套件名稱。

關於你的代碼:

  • 有代碼圍繞解決重點/的TrustManagerFactory算法是這樣的( 「SunX509」)的多個實例。這通常是硬編碼Java 1.4默認值的代碼。自Java 5以來,默認的TMF算法是PKIX(請參閱Customization section of the JSSE Reference Guide)。解決此問題的最佳方法是使用TrustManagerFactory.getDefaultAlgorithm()(對於KMF也是如此),這也將允許您的代碼在不支持SunX509(例如IBM's)的其他JRE上運行。

  • 由於您沒有使用客戶端證書身份驗證,所以在客戶端沒有KeyManagerFactory沒有意義。你用一個可能沒有私鑰的密鑰庫來初始化它,這使得它毫無意義。你不妨使用sslContext.init(null, tmf.getTrustManagers(), null)。 (對於這兩種情況下的安全隨機,同樣的事情,讓JSSE使用它的默認值。)

+0

:)謝謝你的解釋。我從教程中留下了那段代碼,因爲當時我還不確定在談判初始連接之後是否需要添加另一個握手。事實上,我完全不清楚JSSE公鑰密碼系統的實現。您鏈接的資源非常有用。 – SigSeg

-1

對於由配置的安全提供者實現什麼它的價值其實這麼多年後,(和更具體到Java)的SSL套接字句柄握手。每當發生對SSL Socket的寫入時,都會暗示握手,並應由安全提供程序處理。然而,爲了使握手在代碼中更加明顯,你可以使用的方法是startHandshake並記錄在這裏:

https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLSocket.html#startHandshake()

+1

握手由* connect *步驟隱含,而不是'每當寫入發生時'。 – EJP