java线程之四 SSL加密传输
网络传输是存在风险的,因此对服服务端和客户端进行安全校验和传输信息的加密就显得非常的重要。
上面一句有点拗口,简单解释如下文:
当客户使用SSL向站点服务器发送请求时,服务器向客户端发送一个证书,客户使用已安装的证书,验证服务器身份,然后检查IP地址(主机名)与客户端连接的主机是否匹配。客户生成可以用来对话的私钥(称为会话密钥),然后用服务者的公钥对它进行加密并将它发送到服务者。服务者用自己的私钥解密,然后用该信息和客户端一样的私有会话密钥。通常在这个阶段使用RSA算法。
随后,客户端和服务器端使用私有会话密钥和私钥算法(通常是RC4)进行通信。使用另一个密钥的消息认证码来确保消息的完整性。
接下来,就一一介绍下如何进行SSL加密的socket通信开发
一、创建服务端密钥
命令行执行
keytool.exe -genkeypair -v -alias sslsocket -keyalg RSA -keystore e:\sslsocket.keystore
出现提示输入密码
输入keystore密码: 再次输入新密码: 您的名字与姓氏是什么? [Unknown]: lwx 您的组织单位名称是什么? [Unknown]: newland 您的组织名称是什么? [Unknown]: bomc 您所在的城市或区域名称是什么? [Unknown]: fz 您所在的州或省份名称是什么? [Unknown]: fj 该单位的两字母国家代码是什么 [Unknown]: zh CN=lwx, OU=newland, O=bomc, L=fz, ST=fj, C=zh 正确吗? [否]: y
上述信息只是为了帮助客户端校验服务端证书的信息,测试的时候只需要注意最后提示是否正确的时候 输入y 即可。
接着出现下面信息
正在为以下对象生成 1,024 位 RSA 密钥对和自签名证书 (SHA1withRSA)(有效期为 90 天 ): CN=lwx, OU=newland, O=bomc, L=fz, ST=fj, C=zh 输入<sslsocket>的主密码 (如果和 keystore 密码相同,按回车): [正在存储 e:\sslsocket.keystore]
生成密钥sslsocket.keystore后,可以通过下面的命令来查看
keytool -list -v -keystore e:\sslsocket.keystore -storepass 123456
看到的信息就是之前我们输入的内容了
Keystore 类型: JKS Keystore 提供者: SUN 您的 keystore 包含 1 输入 别名名称: sslsocket 创建日期: 2013-5-8 项类型: PrivateKeyEntry 认证链长度: 1 认证 [1]: 所有者:CN=lwx, OU=newland, O=bomc, L=fz, ST=fj, C=zh 签发人:CN=lwx, OU=newland, O=bomc, L=fz, ST=fj, C=zh 序列号:5189a30d 有效期: Wed May 08 08:57:49 CST 2013 至Tue Aug 06 08:57:49 CST 2013 证书指纹: MD5:51:5E:1A:57:1B:B9:18:3A:9B:05:F7:13:E5:06:AB:F0 SHA1:11:0E:C8:8B:46:1F:27:FA:12:95:95:4E:1E:29:E7:27:50:2E:E9:48 签名算法名称:SHA1withRSA 版本: 3
二、生成服务端证书
keytool.exe -exportcert -v -alias sslsocket -file e:\sslsocket.cer -keystore e:\sslsocket.keystore
e:\sslsocket.cer 即我们服务端的证书,到这里应该就比较熟悉了
查看证书信息的命令
keytool.exe -printcert -v -file e:\sslsocket.cer
出现的结果如下
所有者:CN=lwx, OU=newland, O=bomc, L=fz, ST=fj, C=zh 签发人:CN=lwx, OU=newland, O=bomc, L=fz, ST=fj, C=zh 序列号:5189a30d 有效期: Wed May 08 08:57:49 CST 2013 至Tue Aug 06 08:57:49 CST 2013 证书指纹: MD5:51:5E:1A:57:1B:B9:18:3A:9B:05:F7:13:E5:06:AB:F0 SHA1:11:0E:C8:8B:46:1F:27:FA:12:95:95:4E:1E:29:E7:27:50:2E:E9:48 签名算法名称:SHA1withRSA 版本: 3
三、生成客户端密钥
有了服务端证书之后,自然就是通过密码来生成客户端的密钥了,命令如下
keytool.exe -importcert -v -alias sslsocketcer -file e:\sslsocket.cer -keystore e:\sslclient.keystore
e:\sslclient.keystore 就是客户端的密钥了。
关于keytool的更多信息可以参考这里:http://blog.chinaunix.net/uid-17102734-id-2830223.html
四、开发程序
为了测试 我将服务端和客户端的证书放到工程目录下
服务端代码
1 /** 2 * @author draem0507@gmail.com 3 * @TODO java线程开发之四 SSL加密 4 * 开发步骤 5 * 1.生成服务端密钥 6 * 2.导出服务端证书 7 * 3.生成客户端密钥 8 * 4.程序开发测试 9 * 关于证书的生成请参考readme.txt 10 * 参考资料:http://chrui.iteye.com/blog/1018778 11 * @version 1.0 12 * @date 2013-5-7 23:22:45 13 * @update 2013-5-8 10:22:45 14 * @blgos http://www.cnblogs.com/draem0507 15 */ 16 17 public class ServerTest { 18 private ServerSocket serverSocket; 19 private final static char[] password="123456".toCharArray(); 20 private SSLContext context; 21 URL url = Thread.currentThread().getContextClassLoader().getResource("sslsocket.keystore"); 22 String path = url.toString(); 23 private InputStream inputStream; 24 25 26 public ServerTest() { 27 inputStream=this.getClass().getResourceAsStream("/sslsocket.keystore"); 28 initContext(); 29 try { 30 //直接运行会报 javax.net.ssl.SSLException: 31 //ServerSocketFactory factory= SSLServerSocketFactory.getDefault(); 32 ServerSocketFactory factory= context.getServerSocketFactory(); 33 // serverSocket = new ServerSocket(10000); 34 serverSocket=factory.createServerSocket(10000); 35 while (true) { 36 Socket socket = serverSocket.accept(); 37 new ReceiveSocket(socket).start(); 38 } 39 } catch (IOException e) { 40 // TODO Auto-generated catch block 41 e.printStackTrace(); 42 } 43 44 } 45 46 //ssl 上下文对象的初始化 47 private void initContext() { 48 try { 49 KeyStore store=KeyStore.getInstance("JKS"); 50 store.load(inputStream, password); 51 KeyManagerFactory factory=KeyManagerFactory.getInstance("SunX509"); 52 factory.init(store,password); 53 KeyManager []keyManagers=factory.getKeyManagers(); 54 context=SSLContext.getInstance("SSL"); 55 context.init(keyManagers, null , null); 56 } catch (KeyStoreException e) { 57 // TODO Auto-generated catch block 58 e.printStackTrace(); 59 } catch (NoSuchAlgorithmException e) { 60 // TODO Auto-generated catch block 61 e.printStackTrace(); 62 } catch (CertificateException e) { 63 // TODO Auto-generated catch block 64 e.printStackTrace(); 65 } catch (FileNotFoundException e) { 66 // TODO Auto-generated catch block 67 e.printStackTrace(); 68 } catch (IOException e) { 69 // TODO Auto-generated catch block 70 e.printStackTrace(); 71 } catch (UnrecoverableKeyException e) { 72 // TODO Auto-generated catch block 73 e.printStackTrace(); 74 } catch (KeyManagementException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 79 } 80 81 public static void main(String[] args) { 82 new ServerTest(); 83 84 } 85 86 private class ReceiveSocket extends Thread { 87 private Socket socket; 88 89 public ReceiveSocket(Socket socket) { 90 this.socket = socket; 91 } 92 93 private ObjectInputStream reader; 94 private ObjectOutputStream writer; 95 96 @Override 97 public void run() { 98 99 try { 100 reader=new ObjectInputStream(new BufferedInputStream(socket.getInputStream())); 101 //writer=new ObjectOutputStream(socket.getOutputStream()); 102 // 开启无限循环 监控消息 103 104 //java.io.EOFException 105 Object obj= reader.readObject(); 106 if(obj!=null) 107 { 108 User user =(User)obj; 109 System.out.println("id=="+user.getId()+"\tname=="+user.getName()); 110 } 111 // while (true) {} 112 113 } catch (IOException e) { 114 // TODO Auto-generated catch block 115 e.printStackTrace(); 116 } catch (ClassNotFoundException e) { 117 // TODO Auto-generated catch block 118 e.printStackTrace(); 119 } finally { 120 if (null != reader) { 121 try { 122 reader.close(); 123 } catch (IOException e) { 124 // TODO Auto-generated catch block 125 e.printStackTrace(); 126 } 127 } 128 if (null != writer) { 129 try { 130 reader.close(); 131 } catch (IOException e) { 132 // TODO Auto-generated catch block 133 e.printStackTrace(); 134 } 135 } 136 try { 137 socket.close(); 138 } catch (IOException e) { 139 // TODO Auto-generated catch block 140 e.printStackTrace(); 141 } 142 } 143 144 } 145 146 } 147 148 }
客户端代码
1 public class ClientTest { 2 private final static char[] password="123456".toCharArray(); 3 private static SSLContext context; 4 static InputStream inputStream=ClientTest.class.getResourceAsStream("/sslclient.keystore"); 5 6 public static void main(String[] args) throws Exception { 7 8 KeyStore ts = KeyStore.getInstance("JKS"); 9 ts.load(inputStream, password); 10 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 11 tmf.init(ts); 12 TrustManager [] tm = tmf.getTrustManagers(); 13 context = SSLContext.getInstance("SSL"); 14 context.init(null, tm, null); 15 16 //SocketFactory factory= SSLSocketFactory.getDefault(); 17 //Socket socket =factory.createSocket("localhost", 10000); 18 SocketFactory factory= context.getSocketFactory(); 19 SSLSocket socket=(SSLSocket) factory.createSocket("localhost", 10000); 20 21 22 //ObjectInputStream in=new ObjectInputStream(socket.getInputStream()); 23 ObjectOutputStream out=new ObjectOutputStream(socket.getOutputStream()); 24 25 26 User user =new User(); 27 user.setId(1); 28 user.setName("lwx_"+1); 29 out.writeObject(user); 30 out.flush(); 31 32 33 socket.close(); 34 35 36 } 37 }
如果想要源码的话,可以到这里来下载 http://download.csdn.net/detail/draem0507/5343534