JSSE(Java Secure Socket Extension)简单使用(一)

参考: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 规范密码匙和安全管理接口


SSL客户端

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();
    }
}

SSL服务器端

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的配置工作。

配置客户端和服务器端相互信任

1.创建客户端keystore文件

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>

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

E:\javassl>keytool -export -alias sslclient -keystore sslclientkeys -file sslclient.cer
输入密钥库口令:
存储在文件 <sslclient.cer> 中的证书

3.创建一个服务器端 keystore 文件

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> 的密钥口令
        (如果和密钥库口令相同, 按回车):

4.将服务器端 keystore 文件导出成证书格式

E:\javassl>keytool -export -alias sslserver -keystore sslserverkeys -file sslserver.cer
输入密钥库口令:
存储在文件 <sslserver.cer> 中的证书

5.将客户端证书导入到服务器端受信任的 keystore 中

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
证书已添加到密钥库中

6.将服务器端证书导入到客户端受信任的 keystore 中

注:由于服务端的证书是我们自己生成的,没有任何受信任机构的签名,所以客户端是无法验证服务端证书的有效性的,通信必然会失败。所以我们需要为客户端创建一个保存所有信任证书的仓库,然后把服务端证书导进这个仓库。这样,当客户端连接服务端时,会发现服务端的证书在自己的信任列表中,就可以正常通信了。

这就是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>

7.查看密钥库

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的客户端和服务器端

运行SSL客户端和服务器端

idea的集成开发环境,使用以下参数

client端,上图:(相对路径报错找不到文件,直接就用绝对路径了)

JSSE(Java Secure Socket Extension)简单使用(一)_第1张图片

-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端,上图

JSSE(Java Secure Socket Extension)简单使用(一)_第2张图片

-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...


你可能感兴趣的:(安全套接字编程)