2012-10-15 42 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