浅谈基于SSL的Socket通信
在 上一篇 中提到的是客户端对于服务器的验证,即服务器是否可信。若希望配置双向认证的话,即当客户端信任服务器,向服务器发送数据,同样服务器也要判断客户端是否可信,则客户端也需要将其数字证书导入到服务器的密钥库中。
在 上一篇 中我们已经创建了服务器端的密钥库,并从其中导出了数字证书,客户端将服务器的数字证书导入到了自己的密钥库中,连接服务器的时候用来验证服务器的身份。
在这里我们只需要创建客户端的密钥库,并从中导出数字证书,并将该证书导入到服务器端的密钥库中,当客户端连接服务器时验证客户端的身份。
基于 上一篇 执行如下三个步骤的操作:
(a) 生成客户端密钥库
(b) 从客户端密钥库导出数字证书
(c) 将客户端数字证书导入到服务器的密钥库中
(d) 创建服务器端Socket对象
(e) 创建客户端Socket对象
//以下为上述三个方法,来自JDK1.6
在 上一篇 中提到的是客户端对于服务器的验证,即服务器是否可信。若希望配置双向认证的话,即当客户端信任服务器,向服务器发送数据,同样服务器也要判断客户端是否可信,则客户端也需要将其数字证书导入到服务器的密钥库中。
在 上一篇 中我们已经创建了服务器端的密钥库,并从其中导出了数字证书,客户端将服务器的数字证书导入到了自己的密钥库中,连接服务器的时候用来验证服务器的身份。
在这里我们只需要创建客户端的密钥库,并从中导出数字证书,并将该证书导入到服务器端的密钥库中,当客户端连接服务器时验证客户端的身份。
基于 上一篇 执行如下三个步骤的操作:
(a) 生成客户端密钥库
- keytool.exe -genkeypair -v -alias sslsockettwo -keyalg RSA -keystore e:\keytool\sslclienttwo.keystore
- 输入keystore密码:
- 再次输入新密码:
- 您的名字与姓氏是什么?
- [Unknown]: cr
- 您的组织单位名称是什么?
- [Unknown]: han
- 您的组织名称是什么?
- [Unknown]: s
- 您所在的城市或区域名称是什么?
- [Unknown]: bj
- 您所在的州或省份名称是什么?
- [Unknown]: bj
- 该单位的两字母国家代码是什么
- [Unknown]: zh
- CN=cr, OU=han, O=s, L=bj, ST=bj, C=zh 正确吗?
- [否]: y
- 正在为以下对象生成 1,024 位 RSA 密钥对和自签名证书 (SHA1withRSA)(有效期为 90 天):
- CN=cr, OU=han, O=s, L=bj, ST=bj, C=zh
- 输入<sslsockettwo>的主密码
- (如果和 keystore 密码相同,按回车):
- [正在存储 e:\keytool\sslclienttwo.keystore]
(b) 从客户端密钥库导出数字证书
- keytool.exe -exportcert -v -alias sslsockettwo -file e:\keytool\sslsockettwo.cer -keystore e:\keytool\sslclienttwo.keystore
- 输入keystore密码:
- 保存在文件中的认证 <e:\keytool\sslsockettwo.cer>
(c) 将客户端数字证书导入到服务器的密钥库中
- keytool.exe -importcert -v -alias sslsockettwocer -file e:\keytool\sslsockettwo.cer -keystore e:\keytool\sslsocketserver.keystore
- 输入keystore密码:
- 再次输入新密码:
- 所有者:CN=cr, OU=han, O=s, L=bj, ST=bj, C=zh
- 签发人:CN=cr, OU=han, O=s, L=bj, ST=bj, C=zh
- 序列号:4ce61d58
- 有效期: Fri Nov 19 14:46:48 CST 2010 至Thu Feb 17 14:46:48 CST 2011
- 证书指纹:
- MD5:54:84:28:A5:C8:E5:4D:18:7E:A7:56:6B:16:3E:10:62
- SHA1:1D:36:F0:6E:CC:FF:86:15:D4:9F:28:B6:85:95:BA:79:23:29:A6:CF
- 签名算法名称:SHA1withRSA
- 版本: 3
- 信任这个认证? [否]: y
- 认证已添加至keystore中
- [正在存储 e:\keytool\sslsocketserver.keystore]
(d) 创建服务器端Socket对象
- public class TestSSLSocketServer {
- private static String kpath = "e:\\keytool\\sslsocket.keystore";
- private static String tpath = "e:\\keytool\\sslsocketserver.keystore";
- private static char[] password = "aaaaaaa".toCharArray();
- public static void main(String[] args) {
- boolean flag = true;
- SSLContext context = null;
- try {
- KeyStore ks = KeyStore.getInstance("JKS");
- ks.load(new FileInputStream(kpath), password);
- KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
- kmf.init(ks, password);
- KeyManager[] km = kmf.getKeyManagers();
- KeyStore ts = KeyStore.getInstance("JKS");
- ts.load(new FileInputStream(tpath), password);
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
- tmf.init(ts);
- TrustManager[] tm = tmf.getTrustManagers();
- context = SSLContext.getInstance("SSL");
- context.init(km, tm, null);
- } catch (..... e) {
- e.printStackTrace(); //捕获异常
- }
- SSLServerSocketFactory ssf = (SSLServerSocketFactory) context.getServerSocketFactory();
- try {
- SSLServerSocket ss = (SSLServerSocket) ssf.createServerSocket(8000);
- /**
- * 此处不是太明白:三个方法具体什么作用
- */
- //ss.setNeedClientAuth(true);
- //ss.setUseClientMode(true);
- //ss.setWantClientAuth(true);
- System.out.println("等待客户点连接。。。");
- while (flag) {
- Socket s = ss.accept();
- System.out.println("接收到客户端连接");
- ObjectOutputStream os = new ObjectOutputStream(s.getOutputStream());
- os.writeObject("echo : Hello");
- os.flush();
- os.close();
- System.out.println();
- s.close();
- }
- ss.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
(e) 创建客户端Socket对象
- public class TestSSLSocketClient {
- private static String kpath = "e:\\keytool\\sslclienttwo.keystore";
- private static String tpath = "e:\\keytool\\sslclient.keystore";
- //private static String path = "e:\\keytool\\tclient.keystore";
- private static char[] password = "aaaaaaa".toCharArray();
- /**
- * @param args
- */
- public static void main(String[] args) {
- SSLContext context = null;
- try {
- KeyStore ks = KeyStore.getInstance("JKS");
- ks.load(new FileInputStream(kpath), password);
- KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
- kmf.init(ks, password);
- KeyManager [] km = kmf.getKeyManagers();
- KeyStore ts = KeyStore.getInstance("JKS");
- ts.load(new FileInputStream(tpath), password);
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
- tmf.init(ts);
- TrustManager [] tm = tmf.getTrustManagers();
- context = SSLContext.getInstance("SSL");
- context.init(km, tm, null);
- } catch (...... e) {
- e.printStackTrace(); //捕获异常
- }
- SSLSocketFactory ssf = context.getSocketFactory();
- try {
- SSLSocket ss = (SSLSocket) ssf.createSocket("localhost", 8000);
- /**
- * 此处不是太明白:三个方法具体什么作用
- */
- //ss.setNeedClientAuth(true);
- //ss.setUseClientMode(true);
- //ss.setWantClientAuth(true);
- System.out.println("客户端就绪。");
- ObjectInputStream br = new ObjectInputStream(ss.getInputStream());
- try {
- System.out.println(br.readObject());
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- br.close();
- ss.close();
- System.out.println("客户端测试ok");
- } catch (UnknownHostException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
//以下为上述三个方法,来自JDK1.6
- abstract void setNeedClientAuth(boolean need)
- 控制接受的服务器模式SSLSocket是否将在开始时配置为要求客户端验证。
- abstract void setUseClientMode(boolean mode)
- 控制接受的连接是以(默认的)SSL 服务器模式还是在 SSL 客户端模式工作。
- abstract void setWantClientAuth(boolean want)
- 控制 accept 服务器模式的 SSLSockets 是否将在开始时配置为请求 客户端验证。