Java SSL Socket:keytool工具与安全Socket通信

  Java安全Socket扩展(JSSE)可以使用安全Socket层(SSL)版本3和传输层安全(TLS)协议及相关算法来保护网络通信的安全。SSL可以提供各级机密和认证功能,前者依靠密钥,后者依靠证书。同普通Socket类似,SSLSocket对象构建客户端安全套接字流程,而SSLServerSocket对象构建服务端安全套接字流程,用户调用两者的通信接口同普通Socket大体一致;与普通Socket不同的是,SSL**需要控制身份认证和加密行为,但是做法很简单,配置支持的密码组(CipherSuites)即可。
  下面首先介绍使用Java工具keytool生成公钥和证书的方法:
1. 生成服务端私钥server.keys
keytool -genkey -alias serverkey -keystore server.keys
Java SSL Socket:keytool工具与安全Socket通信_第1张图片
2. 从服务端私钥导出服务端证书server.crt
keytool -export -alias serverkey -keystore server.keys -file server.crt
这里写图片描述
3. 将服务端证书导入到客户端KeyStore,即client.keys
keytool -import -alias serverkey -file server.crt -keystore client.keys
Java SSL Socket:keytool工具与安全Socket通信_第2张图片


  以上利用keytool只生成了服务端的证书和密钥,这和大多数web服务一致,仅需要单方面认证身份即可,往往服务端需要向客户端证明自身身份,客户端并不需要。现在客户端利用client.keys来对服务端进行身份验证和数据加/解密,服务端利用server.keys来对数据加/解密。下面提供SSL通信服务端和客户端的示例代码:

  • SSLServer.java,SSL服务端:
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import java.io.*;
import java.net.Socket;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Created by Cheney Hwang on 2017/5/2.
 * SSLServerSocketFactory.getDefault()返回的工厂只支持服务器验证而不支持加密
 * 使用SSLContext来对其弥补
 */
public class SSLServer {
    public final static int PORT=5555;
    public final static String PROTOCOL="ssl";

    public static void main(String[] args){
        try {
            //用指定的协议创建SSL上下文(有SSL和TLS协议)
            SSLContext context = SSLContext.getInstance(PROTOCOL);
            //管理x.509密钥
            KeyManagerFactory kmf=KeyManagerFactory.getInstance("Sunx509");
            //JKS密钥库
            KeyStore ks=KeyStore.getInstance("JKS");
            //密钥文件加密口令,server.keys的口令是Hc19920210
            char[] passwd="Hc19920210".toCharArray();
            ks.load(new FileInputStream("server.keys"),passwd);
            //授权管理密钥
            kmf.init(ks,passwd);
            //初始化上下文
            context.init(kmf.getKeyManagers(),null,null);
            //下面创建的工厂支持服务器验证和加密
            SSLServerSocketFactory factory=context.getServerSocketFactory();
            SSLServerSocket server=(SSLServerSocket)factory.createServerSocket(PORT);

            String[] supported=server.getSupportedCipherSuites();
            List anonSupported=new ArrayList<>();
            //获取所有匿名(非验证)密码组
            for(String elem:supported){
                if(elem.indexOf("_anon_")>0){
                    anonSupported.add(elem);
                }
            }

            String[] oldEnabled=server.getEnabledCipherSuites();
            String[] newEnabled=new String[oldEnabled.length+anonSupported.size()];
            System.arraycopy(oldEnabled,0,newEnabled,0,oldEnabled.length);
            int index=oldEnabled.length;
            for(String elem:anonSupported){
                newEnabled[index++]=elem;
            }
            //允许匿名和加密身份认证,JDK1.7默认只支持加密认证密码组,此处打开包括匿名认证密码组
            server.setEnabledCipherSuites(newEnabled);

            while(true){
                try(Socket client=server.accept()){
                    Writer out=new OutputStreamWriter(client.getOutputStream());
                    Date now=new Date();
                    out.write(now.toString()+"\r\n");
                    out.flush();
                    out.close();

                }catch (IOException e){
                    e.printStackTrace();
                }
            }

        }catch (IOException|KeyManagementException|KeyStoreException
                |NoSuchAlgorithmException|CertificateException
                |UnrecoverableEntryException ex){
            ex.printStackTrace();
        }
    }
}
  • SSLClient.java,SSL客户端:
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 * Created by Cheney Hwang on 2017/5/2.
 */
public class SSLClient {
    public final static int PORT = 5555;
    public final static String HOST = "localhost";

    public static void main(String[] args) {
        System.setProperty("javax.net.ssl.trustStore", "client.keys");
        System.setProperty("javax.net.ssl.trustStorePassword", "Hc19920210");

        SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        try (SSLSocket socket = (SSLSocket) factory.createSocket(HOST, PORT)) {

            //启用所有密码组
            String[] supported = socket.getSupportedCipherSuites();
            socket.setEnabledCipherSuites(supported);

            Reader in = new InputStreamReader(socket.getInputStream());
            int c;
            while ((c = in.read()) != -1) {
                System.out.write((char) c);
            }
            in.close();

        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

  JDK1.7默认只启用所有加密认证密码组。以上SSL服务端启用了匿名(无需身份验证)密码组,SSL客户端启用了所有密码组。

你可能感兴趣的:(Java)