SSL介绍与Java实例(五)

2014-11-24 08:46:48 · 作者: · 浏览: 2
er;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

public class SSLClient {

private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";

public static void main(String[] args) throws Exception {
// Set the key store to use for validating the server cert.
System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);

System.setProperty("javax.net.debug", "ssl,handshake");

SSLClient client = new SSLClient();
Socket s = client.clientWithoutCert();

PrintWriter writer = new PrintWriter(s.getOutputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(s
.getInputStream()));
writer.println("hello");
writer.flush();
System.out.println(reader.readLine());
s.close();
}

private Socket clientWithoutCert() throws Exception {
SocketFactory sf = SSLSocketFactory.getDefault();
Socket s = sf.createSocket("localhost", 8443);
return s;
}
}


可以看到,除了把一些类变成SSL通信类以外,客户端也多出了使用信任证书仓库的代码。以上,我们便完成了SSL单向握手通信。即:客户端验证服务端的证书,服务端不认证客户端的证书。

以上便是Java环境下SSL单向握手的全过程。因为我们在客户端设置了日志输出级别为DEBUG:


Java代码
1.System.setProperty("javax.net.debug", "ssl,handshake");
System.setProperty("javax.net.debug", "ssl,handshake");

因此我们可以看到SSL通信的全过程,这些日志可以帮助我们更具体地了解通过SSL协议建立网络连接时的全过程。

结合日志,我们来看一下SSL双向认证的全过程:

第一步: 客户端发送ClientHello消息,发起SSL连接请求,告诉服务器自己支持的SSL选项(加密方式等)。


Bash代码
1.*** ClientHello, TLSv1
*** ClientHello, TLSv1


第二步: 服务器响应请求,回复ServerHello消息,和客户端确认SSL加密方式:


Bash代码
1.*** ServerHello, TLSv1
*** ServerHello, TLSv1


第三步: 服务端向客户端发布自己的公钥。

第四步: 客户端与服务端的协通沟通完毕,服务端发送ServerHelloDone消息:


Bash代码
1.*** ServerHelloDone
*** ServerHelloDone


第五步: 客户端使用服务端给予的公钥,创建会话用密钥(SSL证书认证完成后,为了提高性能,所有的信息交互就可能会使用对称加密算法),并通过ClientKeyExchange消息发给服务器:


Bash代码
1.*** ClientKeyExchange, RSA PreMasterSecret, TLSv1
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1


第六步: 客户端通知服务器改变加密算法,通过ChangeCipherSpec消息发给服务端:


Bash代码
1.main, WRITE: TLSv1 Change Cipher Spec, length = 1
main, WRITE: TLSv1 Change Cipher Spec, length = 1


第七步: 客户端发送Finished消息,告知服务器请检查加密算法的变更请求:


Bash代码
1.*** Finished
*** Finished


第八步:服务端确认算法变更,返回ChangeCipherSpec消息


Bash代码
1.main, READ: TLSv1 Change Cipher Spec, length = 1
main, READ: TLSv1 Change Cipher Spec, length = 1


第九步:服务端发送Finished消息,加密算法生效:


Bash代码
1.*** Finished
*** Finished


那么如何让服务端也认证客户端的身份,即双向握手呢?其实很简单,在服务端代码中,把这一行:


Java代码
1.((SSLServerSocket) _socket).setNeedClientAuth(false);
((SSLServerSocket) _socket).setNeedClientAuth(false);

改成:


Java代码
1.((SSLServerSocket) _socket).setNeedClientAuth(true);
((SSLServerSocket) _socket).setNeedClientAuth(true);

就可以了。但是,同样的道理,现在服务端并没有信任客户端的证书,因为客户端的证书也是自己生成的。所以,对于服务端,需要做同样的工作:把客户端的证书导出来,并导入到服务端的证书仓库:


Bash代码
1.keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer
2.Enter keystore password: client
3.Certificate stored in file
keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer
Enter keystore password: client
Certificate stored in file


Bash代码
1.keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks
2.Enter keystore password: server
3.Owner