在Java中使用带有TLS的JSSE。我在服务器和客户端之间创建了一个安全套接字。在最终安全地连接套接字之后,我仍然有一个关于我现有代码安全性的基本问题。我按照教程中的说明进行操作,有时JavaDoc中的文档非常精确但有点含糊,除非你说的是术语的Swaheli方言....
我现在已经在C ++中进行了一段时间的网络编程。过渡到Java很容易。然而,最近我发现使交通安全是明智的。这就是说:
我想以与Web浏览器创建安全套接字相同的方式创建安全套接字,因此双向流量都是加密的。客户端可以看到他们从服务器发送的个人帐户信息(如果被截获则非常糟糕),客户端可以安全地将他们的用户名和密码发送到服务器(如果被截获也非常糟糕)。
我知道公钥加密的工作原理,但仅对公钥加密有副作用。您将公钥发送到客户端,客户端使用公钥加密,并将数据发送到服务器,只有服务器可以解密。根据我的理解,服务器使用私钥来加密发往客户端的消息,并且需要添加另一层安全性以防止任何拥有公钥的人能够解密它。
客户类:
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 :(得分:6)
SSL / TLS中的证书(及其私钥)仅用于验证SSL / TLS中的各方(通常,只有服务器使用证书)。
实际加密是使用在握手期间协商的共享/对称密钥完成的,这些密钥是从使用经过身份验证的密钥交换形式交换的预主密钥派生的(参见TLS Specification, Section F.1.1。
如何进行此经过身份验证的密钥交换取决于密码套件,但最终结果是相同的:双方之间共享的预主密钥,保证只有客户端和具有私钥的服务器才知道证书。
在预主密钥交换之后,计算主密钥本身,从中导出一对密钥(如Key Calculation section中所述):一个用于客户端写入(以及用于服务器)阅读)和一个服务器写(和客户端阅读)。 (还会生成MAC机密,以保证连接的完整性。)
原则上,并非所有密码套件都提供加密和经过身份验证的密钥交换(请参阅Cipher Suite Definitions section),但所有在JSSE中与SunJSSE提供程序一起启用的密钥交换都会执行(请参阅SunJSSE提供程序文档中的Cipher Suite tables) )。简而言之,不要在其名称中使用anon
或NULL
启用密码套件。
关于您的代码:
有多个代码示例修复了Key / TrustManagerFactory算法(" SunX509")。这通常是对Java 1.4默认值进行硬编码的代码。从Java 5开始,默认的TMF算法为PKIX
(参见Customization section of the JSSE Reference Guide)。解决此问题的最佳方法是使用TrustManagerFactory.getDefaultAlgorithm()
(对于KMF也是如此),这也将允许您的代码在不支持SunX509
的其他JRE上运行(例如IBM'或多个)。
由于您未使用客户端证书身份验证,因此客户端没有KeyManagerFactory
。你用密钥库初始化它可能没有私钥,这使得它毫无意义。您也可以使用sslContext.init(null, tmf.getTrustManagers(), null)
。 (对于两种情况下的安全随机,同样的事情,让JSSE使用其默认值。)
答案 1 :(得分:1)
您确实了解PKI的工作原理,但您缺少SSL实施的两个关键部分。首先,大多数PKI算法允许双向加密流量。您可以使用公钥发送加密消息,只有拥有私钥的人才能读取它,这称为加密。您还可以使用私钥加密邮件,任何拥有公钥的人都可以对其进行解密,这称为数字签名。
另一个缺失的部分是SSL不使用PKI在客户端和服务器之间发送网络流量。它使用对称加密算法。然而,对称加密(称为会话密钥)的关键是使用相当复杂的挑战 - 响应协议来建立,该协议使用PKI和证书。在此阶段,服务器向客户端证明它不是中间的人,如果客户端具有更强的认证,并且建立了对称会话密钥,则客户端可以选择性地向服务器证明它是证书。更多详情请访问http://tools.ietf.org/html/rfc5246
对称密钥用于使用RC5或AES
等算法加密流量