首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

【转】Java SSL/TLS 保险通讯协议介绍

2013-12-02 
【转】Java SSL/TLS 安全通讯协议介绍Java SSL/TLS 安全通讯协议介绍刘 进, 高级软件工程师 , IBM2013 年 5

【转】Java SSL/TLS 安全通讯协议介绍

Java SSL/TLS 安全通讯协议介绍

刘 进, 高级软件工程师 , IBM2013 年 5 月 23 日

?

简介

本文主要介绍了网络安全通讯协议 SSL/TLS 和 Java 中关于安全通讯的实现部分。并通过一个简单的样例程序实现,来展示如何在 Java 平台上正确建立安全通讯。

?

在人类建立了通信系统之后,如何保证通信的安全始终是一个重要的问题。伴随着现代化通信系统的建立,人们利用数学理论找到了一些行之有效的方法来保证数字通信的安全。简单来说就是把两方通信的过程进行保密处理,比如对双方通信的内容进行加密,这样就可以有效防止偷听者轻易截获通信的内容。目前 SSL(Secure Sockets Layer) 及其后续版本 TLS(Transport Layer Security)是比较成熟的通信加密协议,它们常被用于在客户端和服务器之间建立加密通信通道。各种开发语言都给出 SSL/TLS 协议的具体实现,Java 也不例外。在 JDK 中有一个 JSSE(javax.net.ssl)包,提供了对 SSL 和 TLS 的支持。通过其所提供的一系列 API,开发者可以像使用普通 Socket 一样使用基于 SSL 或 TLS 的安全套接字,而不用关心 SSL 和 TLS 协议的细节,例如握手的流程等等。这使得利用 Java 开发安全的 SSL/TLS 服务器或客户端非常容易,本文将通过具体的例子来说明如何用 Java 语言来开发 SSL/TLS 应用。

?

SSL/TLS 协议的介绍

SSL/TLS 协议(RFC2246 RFC4346)处于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。

从协议内部的功能层面上来看,SSL/TLS 协议可分为两层:

1. SSL/TLS 记录协议(SSL/TLS Record Protocol),它建立在可靠的传输层协议(如 TCP)之上,为上层协议提供数据封装、压缩、加密等基本功能。

2. SSL/TLS 握手协议(SSL/TLS Handshake Protocol),它建立在 SSL/TLS 记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等初始化协商功能。

从协议使用方式来看,又可以分成两种类型:

1. SSL/TLS 单向认证,就是用户到服务器之间只存在单方面的认证,即客户端会认证服务器端身份,而服务器端不会去对客户端身份进行验证。首先,客户端发起握手请求,服务器收到握手请求后,会选择适合双方的协议版本和加密方式。然后,再将协商的结果和服务器端的公钥一起发送给客户端。客户端利用服务器端的公钥,对要发送的数据进行加密,并发送给服务器端。服务器端收到后,会用本地私钥对收到的客户端加密数据进行解密。然后,通讯双方都会使用这些数据来产生双方之间通讯的加密密钥。接下来,双方就可以开始安全通讯过程了。

2.SSL/TLS 双向认证,就是双方都会互相认证,也就是两者之间将会交换证书。基本的过程和单向认证完全一样,只是在协商阶段多了几个步骤。在服务器端将协商的结果和服务器端的公钥一起发送给客户端后,会请求客户端的证书,客户端则会将证书发送给服务器端。然后,在客户端给服务器端发送加密数据后,客户端会将私钥生成的数字签名发送给服务器端。而服务器端则会用客户端证书中的公钥来验证数字签名的合法性。建立握手之后过程则和单向通讯完全保持一致。

SSL/TLS 协议建立通讯的基本流程如图 1 所示,

图 1. SSL/TLS 基本流程图【转】Java SSL/TLS 保险通讯协议介绍

步骤 1.package example.ssl.codes; import java.io.*; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; class SSLClient { private SSLSocket socket = null; public SSLClient() throws IOException { // 通过套接字工厂,获取一个客户端套接字 SSLSocketFactory socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); socket = (SSLSocket) socketFactory.createSocket("localhost", 7070); } public void connect() { try { // 获取客户端套接字输出流 PrintWriter output = new PrintWriter( new OutputStreamWriter(socket.getOutputStream())); // 将用户名和密码通过输出流发送到服务器端 String userName = "principal"; output.println(userName); String password = "credential"; output.println(password); output.flush(); // 获取客户端套接字输入流 BufferedReader input = new BufferedReader( new InputStreamReader(socket.getInputStream())); // 从输入流中读取服务器端传送的数据内容,并打印出来 String response = input.readLine(); response += "\n " + input.readLine(); System.out.println(response); // 关闭流资源和套接字资源 output.close(); input.close(); socket.close(); } catch (IOException ioException) { ioException.printStackTrace(); } finally { System.exit(0); } } public static void main(String args[]) throws IOException { new SSLClient().connect(); } }

package example.ssl.codes;  import java.io.*;  import javax.net.ssl.SSLServerSocket;  import javax.net.ssl.SSLServerSocketFactory;  import javax.net.ssl.SSLSocket;  class SSLServer {  // 服务器端授权的用户名和密码 private static final String USER_NAME = "principal";  private static final String PASSWORD = "credential";  // 服务器端保密内容 private static final String SECRET_CONTENT = "This is confidential content from server X, for your eye!";  private SSLServerSocket serverSocket = null;  public SSLServer() throws Exception {  // 通过套接字工厂,获取一个服务器端套接字 SSLServerSocketFactory socketFactory = (SSLServerSocketFactory)  SSLServerSocketFactory.getDefault();  serverSocket = (SSLServerSocket)socketFactory.createServerSocket(7070);  }  private void runServer() {  while (true) {  try {  System.out.println("Waiting for connection...");  // 服务器端套接字进入阻塞状态,等待来自客户端的连接请求 SSLSocket socket = (SSLSocket) serverSocket.accept();  // 获取服务器端套接字输入流 BufferedReader input = new BufferedReader(         new InputStreamReader(socket.getInputStream()));  // 从输入流中读取客户端用户名和密码 String userName = input.readLine();  String password = input.readLine();  // 获取服务器端套接字输出流 PrintWriter output = new PrintWriter(         new OutputStreamWriter(socket.getOutputStream()));  // 对请求进行认证,如果通过则将保密内容发送给客户端 if (userName.equals(USER_NAME) && password.equals(PASSWORD)) {  output.println("Welcome, " + userName);  output.println(SECRET_CONTENT);  } else {  output.println("Authentication failed, you have no  access to server X...");  }  // 关闭流资源和套接字资源 output.close();  input.close();  socket.close();  } catch (IOException ioException) {  ioException.printStackTrace();  }  }  }  public static void main(String args[]) throws Exception {  SSLServer server = new SSLServer();  server.runServer();  }  }
java -cp ./build/classes example.ssl.codes.SSLServer  java -cp ./build/classes example.ssl.codes.SSLClient
Waiting for connection...  javax.net.ssl.SSLHandshakeException: no cipher suites in common  Waiting for connection...  at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)  at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1836)  at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276)  at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:266)
 javax.net.ssl.SSLException: Connection has been shutdown:      javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure  at sun.security.ssl.SSLSocketImpl.checkEOF(SSLSocketImpl.java:1426)  at sun.security.ssl.AppInputStream.read(AppInputStream.java:92)  at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)  at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)  at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)  at java.io.InputStreamReader.read(InputStreamReader.java:184)  at java.io.BufferedReader.fill(BufferedReader.java:154)  at java.io.BufferedReader.readLine(BufferedReader.java:317)  at java.io.BufferedReader.readLine(BufferedReader.java:382)  at example.ssl.codes.SSLClient.connect(SSLClient.java:29)  at example.ssl.codes.SSLClient.main(SSLClient.java:44)  Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure  at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)  at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)  at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1911)  at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1027)  at sun.security.ssl.SSLSocketImpl.performInitialHandshake  (SSLSocketImpl.java:1262)  at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:680)  at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:85)  at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)  at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)  at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295)  at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)  at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)  at java.io.PrintWriter.flush(PrintWriter.java:320)  at example.ssl.codes.SSLClient.connect(SSLClient.java:25)  ... 1 more

3. 将客户端 keystore 文件导出成证书格式

将前面创建的所有 keystore 文件从 java 的 bin 目录中剪切出来,移动到样例程序的执行目录中,通过运行程序时候的系统属性来指定这些文件,重新执行一遍样例程序。

java -cp ./build/classes -Djavax.net.ssl.keyStore=sslserverkeys -Djavax.net.ssl.keyStorePassword=123456 -Djavax.net.ssl.trustStore=sslservertrust -Djavax.net.ssl.trustStorePassword=123456 example.ssl.codes.SSLServer java -cp ./build/classes -Djavax.net.ssl.keyStore=sslclientkeys -Djavax.net.ssl.keyStorePassword=123456 -Djavax.net.ssl.trustStore=sslclienttrust -Djavax.net.ssl.trustStorePassword=123456 example.ssl.codes.SSLClient
?

执行结果如下:

客户端输出

Welcome, principal  This is confidential content from server X, for your eye!

客户端与服务器端成功建立起 SSL 的连接,然后服务器端成功将字符串发送给客户端,客户端将其打印出来。

?

热点排行