https双向认证

在单向认证中,客户端对服务端身份进行验证;双向认证在此基础上增加了服务端验证客户端的环节。即收发消息双向都需要验证对方的身份。

创建keystore与truststore

与单向认证相比,双向认证需要两套keystore,并分别将自己的证书导出发送给对方。

  1. 服务端keystore与truststore

    • 创建服务端证书
      keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 36000 -alias test.com -keystore test.com.jks
    • 导出服务端证书
      keytool -exportcert -alias test.com -keystore test.com.jks -file test.com.cer -rfc
    • 生成truststore
      keytool -importcert -alias *.test.com -file test.com.cer -keystore test.com.trust.jks
  2. 创建客户端keystore与truststore

    • 创建客户端证书
      keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 36000 -alias client -keystore client.jks
    • 导出客户端证书
      keytool -exportcert -alias client -keystore client.jks -file client.cer -rfc
    • 生成truststore
      keytool -importcert -alias client -file client.cer -keystore client.trust.jks

这样我们得到两套keystore/truststore,分别是

  • 服务端 test.com.jks, test.com.trust.jks
  • 客户端 client.jks, client.trust.jks

服务端

将test.com.jks, client.trust.jks放在服务端

tomcat的配置

将 test.com.jks, client.trust.jks放到tomcat下的conf目录。设置conf/server.xml

<Connector port="443" protocol="org.apache.coyote.http11.Http11Protocol"
           maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
           clientAuth="true" sslProtocol="TLS" 
           keystoreFile="conf/test.com.jks"
           keystorePass="123456"
           truststoreFile="conf/client.trust.jks"
           truststorePass="fedcba"
           />

SSLServerSocket

先定义个加载keystore的工具类

/**
 * Created by <a href="mailto:manwu91@gmail.com">Monk</a> at 17:32, 15/03/2017
 */
public class KeystoreTool {
    /**
     * 加载密钥库
     */
    public static KeyManager[] loadKeyManagers(InputStream keystore, String pass) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(keystore, pass.toCharArray());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(keyStore, pass.toCharArray());
        return kmf.getKeyManagers();
    }

    /**
     * 加载信任库
     */
    public static TrustManager[] loadTrustManagers(InputStream truststore, String pass) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(truststore, pass.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(keyStore);
        return tmf.getTrustManagers();
    }
}

服务端

/**
 * TLS协议的双向认证服务端
 * Created by <a href="mailto:manwu91@gmail.com">Monk</a> at 15:51, 15/03/2017
 */
public class Server {
    public static void main(String[] args) throws Exception {
        new Server().startUp();
    }

    public void startUp() throws Exception {
        SSLContext cxt = SSLContext.getInstance("TLSv1.2");
        //加载keystore与truststore
        cxt.init(KeystoreTool.loadKeyManagers(Server.class.getResourceAsStream("/test.com.jks"), "123456"),
                KeystoreTool.loadTrustManagers(Server.class.getResourceAsStream("/client.trust.jks"), "fedcba"),
                null);
        //开启client auth
        SSLParameters parameters = cxt.getDefaultSSLParameters();
        parameters.setNeedClientAuth(true);
        SSLServerSocket serverSocket = (SSLServerSocket) cxt.getServerSocketFactory().createServerSocket(8443);
        serverSocket.setSSLParameters(parameters);
        while (true) {
            System.out.println("waiting for client");
            Socket conn = serverSocket.accept();
            System.out.println("conntion accepted from " + conn.getRemoteSocketAddress().toString());
            new Thread(new Worker(conn)).start();
        }
    }

    /**
     * 工作线程
     */
    static class Worker implements Runnable {
        private Socket socket;

        public Worker(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                socket.getOutputStream().write("helloworld\n".getBytes());
                socket.getOutputStream().flush();
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

客户端

将 client.jks, test.com.trust.jks放在客户端。请求时如果keystore或truststore不匹配,会得到异常

javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate

代码如下

/**
 * TLS协议的客户端
 * Created by <a href="mailto:manwu91@gmail.com">周晓鹏</a> at 16:25, 15/03/2017
 */
public class Client {
    public static void main(String[] args)  throws Exception{
        requestHttps("https://www.test.com:443/");
        requestTLS("127.0.0.1", 8443);
    }

    /**
     * https协议请求
     * @param url
     * @throws Exception
     */
    public static void requestHttps(String url) throws Exception {
        HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
        conn.setSSLSocketFactory(sslSocketFactory());
        conn.connect();
        System.out.println(conn.getResponseCode());

    }

    /**
     * tls协议请求
     * @param host
     * @param port
     * @throws Exception
     */
    public static void requestTLS(String host, int port) throws Exception {
        Socket socket = sslSocketFactory().createSocket(host, port);
        InputStream in = socket.getInputStream();
        int i = 0;
        while ((i = in.read()) > 0) {
            System.out.print((char) i);
        }

    }

    static SSLSocketFactory sslSocketFactory() throws Exception {
        SSLContext sslCxt = SSLContext.getInstance("TLSv1.2");
        sslCxt.init(
                KeystoreTool.loadKeyManagers(Client.class.getResourceAsStream("/client.jks"), "abcdef"),
                KeystoreTool.loadTrustManagers(Client.class.getResourceAsStream("/test.com.trust.jks"), "098765"),
                null);
        return sslCxt.getSocketFactory();
    }
}
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章