参考:http://www.ibm.com/developerworks/cn/java/j-lo-ssltls/
在 Java SDK 中有一个叫 JSSE(javax.net.ssl)包,这个包中提供了一些类来建立 SSL/TLS 连接。通过这些类,开发者就可以忽略复杂的协议建立流程,较为简单地在网络上建成安全的通讯通道。JSSE 包中主要包括以下一些部分:
安全套接字(secure socket)和安全服务器端套接字
非阻塞式 SSL/TLS 数据处理引擎(SSLEngine)
套接字创建工厂 , 用来产生 SSL 套接字和服务器端套接字
套接字上下文 , 用来保存用于创建和数据引擎处理过程中的信息
符合 X.509 规范密码匙和安全管理接口
package ssl2; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import java.io.*; /** * Created with IntelliJ IDEA. * User: ASUS * Date: 14-5-20 * Time: 下午12:06 * To change this template use File | Settings | File Templates. */ public 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 ssl2; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; import java.io.*; /** * Created with IntelliJ IDEA. * User: ASUS * Date: 14-5-20 * Time: 下午12:07 * To change this template use File | Settings | File Templates. */ public 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(); } }
这样还不够,还需要作进一步ssl的配置工作。
E:\javassl>keytool -genkey -alias sslclient -keystore sslclientkeys 输入密钥库口令: 再次输入新口令: 您的名字与姓氏是什么? [Unknown]: client 您的组织单位名称是什么? [Unknown]: dev 您的组织名称是什么? [Unknown]: rd 您所在的城市或区域名称是什么? [Unknown]: sy 您所在的省/市/自治区名称是什么? [Unknown]: sy 该单位的双字母国家/地区代码是什么? [Unknown]: cn CN=client, OU=dev, O=rd, L=sy, ST=sy, C=cn是否正确? [否]: y 输入 <sslclient> 的密钥口令 (如果和密钥库口令相同, 按回车): E:\javassl>
E:\javassl>keytool -export -alias sslclient -keystore sslclientkeys -file sslclient.cer 输入密钥库口令: 存储在文件 <sslclient.cer> 中的证书
E:\javassl>keytool -genkey -alias sslserver -keystore sslserverkeys 输入密钥库口令: 再次输入新口令: 您的名字与姓氏是什么? [Unknown]: lyx 您的组织单位名称是什么? [Unknown]: rj 您的组织名称是什么? [Unknown]: gc 您所在的城市或区域名称是什么? [Unknown]: cn 您所在的省/市/自治区名称是什么? [Unknown]: cn 该单位的双字母国家/地区代码是什么? [Unknown]: cn CN=lyx, OU=rj, O=gc, L=cn, ST=cn, C=cn是否正确? [否]: y 输入 <sslserver> 的密钥口令 (如果和密钥库口令相同, 按回车):
E:\javassl>keytool -export -alias sslserver -keystore sslserverkeys -file sslserver.cer 输入密钥库口令: 存储在文件 <sslserver.cer> 中的证书
E:\javassl>keytool -import -alias sslclient -keystore sslservertrust -file sslclient.cer 输入密钥库口令: 再次输入新口令: 所有者: CN=client, OU=dev, O=rd, L=sy, ST=sy, C=cn 发布者: CN=client, OU=dev, O=rd, L=sy, ST=sy, C=cn 序列号: 23a77f2b 有效期开始日期: Tue May 20 12:18:19 CST 2014, 截止日期: Mon Aug 18 12:18:19 CST 2014 证书指纹: MD5: E6:4E:2D:67:5F:E3:46:36:EB:A0:69:8A:79:BE:92:9B SHA1: 8F:40:AC:71:84:3A:03:64:63:22:E6:29:91:DD:77:9B:B9:E2:F1:C6 SHA256: D9:DB:0D:86:44:1B:06:B2:89:29:C1:6A:84:50:C0:A7:F2:BD:A1:DD:11:EC:54:F5:50:AF:11:73 :C9:DE:D3:37 签名算法名称: SHA1withDSA 版本: 3 扩展: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 75 31 80 52 AA C7 6B 36 3E E5 73 23 3F 2D 67 54 u1.R..k6>.s#?-gT 0010: 0D 60 4D 18 .`M. ] ] 是否信任此证书? [no]: y 证书已添加到密钥库中
注:由于服务端的证书是我们自己生成的,没有任何受信任机构的签名,所以客户端是无法验证服务端证书的有效性的,通信必然会失败。所以我们需要为客户端创建一个保存所有信任证书的仓库,然后把服务端证书导进这个仓库。这样,当客户端连接服务端时,会发现服务端的证书在自己的信任列表中,就可以正常通信了。
这就是trustStore的作用。
同理,服务器端的trustStore也是一样。
E:\javassl>keytool -import -alias sslserver -keystore sslclienttrust -file sslserver.cer 输入密钥库口令: 再次输入新口令: 所有者: CN=lyx, OU=rj, O=gc, L=cn, ST=cn, C=cn 发布者: CN=lyx, OU=rj, O=gc, L=cn, ST=cn, C=cn 序列号: 2cbfdf00 有效期开始日期: Tue May 20 12:25:00 CST 2014, 截止日期: Mon Aug 18 12:25:00 CST 2014 证书指纹: MD5: 83:30:0D:45:F5:45:7D:D8:E7:4C:56:99:A1:08:C8:C6 SHA1: B1:C0:D8:AE:CA:5E:C0:16:14:28:4E:75:6A:09:80:BC:9D:90:E5:E8 SHA256: 3E:73:A5:3C:54:EC:3C:79:20:23:7A:82:70:83:B2:5B:2A:99:0E:52:B8:0B:9C:E5:8A:03:B2:10 :9C:74:90:00 签名算法名称: SHA1withDSA 版本: 3 扩展: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: F3 1F DE CA 36 41 A2 5A F5 A1 1F 4E 98 DF 7A 04 ....6A.Z...N..z. 0010: 8B 13 1B 23 ...# ] ] 是否信任此证书? [no]: y 证书已添加到密钥库中 E:\javassl>
E:\javassl>keytool -list -keystore sslclienttrust 输入密钥库口令: 密钥库类型: JKS 密钥库提供方: SUN 您的密钥库包含 1 个条目 sslserver, 2014-5-20, trustedCertEntry, 证书指纹 (SHA1): B1:C0:D8:AE:CA:5E:C0:16:14:28:4E:75:6A:09:80:BC:9D:90:E5:E8 E:\javassl>
准备工作完成,运行SSL的客户端和服务器端
idea的集成开发环境,使用以下参数
client端,上图:(相对路径报错找不到文件,直接就用绝对路径了)
-Djavax.net.ssl.keyStore=E:\javassl\sslclientkeys -Djavax.net.ssl.keyStorePassword=123456 -Djavax.net.ssl.trustStore=E:\javassl\sslclienttrust -Djavax.net.ssl.trustStorePassword=123456
server端,上图
-Djavax.net.ssl.keyStore=E:\javassl\sslserverkeys -Djavax.net.ssl.keyStorePassword=123456 -Djavax.net.ssl.trustStore=E:\javasll\sslservertrust -Djavax.net.ssl.trustStorePassword=123456
启动server端和client端,运行结果是client端打印信息:
Welcome, principal This is confidential content from server X, for your eye! Process finished with exit code 0
server端继续等待连接。
Waiting for connection... Waiting for connection...