如何使用Java访问ssl双向认证的Https资源——三种方式(好文章!)

194 篇文章 18 订阅
17 篇文章 4 订阅

本文的相关源码位于 https://github.com/dreamingodd/CA-generation-demo 

0.Nginx配置Https双向认证

首先配置Https双向认证的服务器资源。

可以参考:http://www.cnblogs.com/dreamingodd/p/7357029.html

完成之后如下效果:

1.方式一:导入cacerts进行访问

首先将服务器证书导入keystore cacerts,默认密码为changeit,如果需要修改密码就改一下。(如果是自签的证书,这里把证书换成ca.crt

keytool -import -alias ssl.demo.com -keystore cacerts -file C:\Development\deployment\ssl\ca-demo\server.crt

需要使用管理员权限到你使用的JDK security目录下执行(注意如果你有多个JDK的情况),效果如下:

然后使用Java访问:

package me.dreamingodd.ca;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;


/**
 * #1
 * HTTPS 双向认证 - direct into cacerts
 * @Author Ye_Wenda
 * @Date 7/11/2017
 */
public class HttpsKeyStoreDemo {
    // 客户端证书路径,用了本地绝对路径,需要修改
    private final static String PFX_PATH = "C:\\Development\\deployment\\ssl\\ca-demo\\client.p12";
    private final static String PFX_PWD = "demo"; //客户端证书密码及密钥库密码

    public static String sslRequestGet(String url) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        InputStream instream = new FileInputStream(new File(PFX_PATH));
        try {
            // 这里就指的是KeyStore库的密码
            keyStore.load(instream, PFX_PWD.toCharArray());
        } finally {
            instream.close();
        }

        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, PFX_PWD.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext
                , new String[] { "TLSv1" }  // supportedProtocols ,这里可以按需要设置
                , null  // supportedCipherSuites
                , SSLConnectionSocketFactory.getDefaultHostnameVerifier());

        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpGet httpget = new HttpGet(url);
//          httpost.addHeader("Connection", "keep-alive");// 设置一些heander等
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                // 返回结果
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println(sslRequestGet("https://ssl.demo.com/"));
    }

}

运行结果如下:

2.方式二:生成truststore库文件进行访问-原生方式

如果服务器的JDK/JRE不能随便改动,我们还可以使用生成truststore库的方式来实现。

首先通过ca.crt生成自己的truststore,把ca.crt复制一份,重命名为ca.cer,复制到security目录下,执行

keytool -keystore demo.truststore -keypass demodemo -storepass demodemo -alias DemoCA -import -trustcacerts -file ca.cer

效果如下:

使用生成的demo.truststore和client.p12进行java访问:

package me.dreamingodd.ca;

import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyStore;


/**
 * #2
 * HTTPS 双向认证 - use truststore
 * 原生方式
 * @Author Ye_Wenda
 * @Date 7/11/2017
 */
public class HttpsTruststoreNativeDemo {
    // 客户端证书路径,用了本地绝对路径,需要修改
    private final static String CLIENT_CERT_FILE = "C:/Development/deployment/ssl/ca-demo/client.p12";
    // 客户端证书密码
    private final static String CLIENT_PWD = "demo";
    // 信任库路径
    private final static String TRUST_STRORE_FILE = "C:\\Development\\deployment\\ssl\\ca-demo\\demo.truststore";
    // 信任库密码
    private final static String TRUST_STORE_PWD = "demodemo";


    private static String readResponseBody(InputStream inputStream) throws IOException {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            StringBuffer sb = new StringBuffer();
            String buff = null;
            while((buff = br.readLine()) != null){
                sb.append(buff+"\n");
            }
            return sb.toString();
        } finally {
            inputStream.close();
        }
    }

    public static void httpsCall() throws Exception {
        // 初始化密钥库
        KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance("SunX509");
        KeyStore keyStore = getKeyStore(CLIENT_CERT_FILE, CLIENT_PWD, "PKCS12");
        keyManagerFactory.init(keyStore, CLIENT_PWD.toCharArray());

        // 初始化信任库
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance("SunX509");
        KeyStore trustkeyStore = getKeyStore(TRUST_STRORE_FILE, TRUST_STORE_PWD,"JKS");
        trustManagerFactory.init(trustkeyStore);

        // 初始化SSL上下文
        SSLContext ctx = SSLContext.getInstance("SSL");
        ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory
                .getTrustManagers(), null);
        SSLSocketFactory sf = ctx.getSocketFactory();

        HttpsURLConnection.setDefaultSSLSocketFactory(sf);
        String url = "https://ssl.demo.com";
        URL urlObj = new URL(url);
        HttpsURLConnection con = (HttpsURLConnection) urlObj.openConnection();
        con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");
        con.setRequestProperty("Accept-Language", "zh-CN;en-US,en;q=0.5");
        con.setRequestMethod("GET");

        String response = readResponseBody(con.getInputStream());
        System.out.println(response);
    }

    /**
     * 获得KeyStore
     *
     * @param keyStorePath
     * @param password
     * @return

     * @throws Exception
     */
    private static KeyStore getKeyStore(String keyStorePath, String password,String type)
            throws Exception {
        FileInputStream is = new FileInputStream(keyStorePath);
        KeyStore ks = KeyStore.getInstance(type);
        ks.load(is, password.toCharArray());
        is.close();
        return ks;
    }


    public static void main(String[] args) throws Exception {
        httpsCall();
    }

}

结果同1。

3.方式三:生成truststore库文件进行访问-Apache HTTP 组件方式

package me.dreamingodd.ca;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.net.URI;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.SecureRandom;

/**
 * #3
 * HTTPS 双向认证 - use truststore
 * Apache插件
 * @Author Ye_Wenda
 * @Date 7/11/2017
 */
public class HttpsTruststoreApacheContextDemo {
    // 客户端证书路径,用了本地绝对路径,需要修改
    private final static String CLIENT_CERT_FILE = "C:/Development/deployment/ssl/ca-demo/client.p12";
    // 客户端证书密码
    private final static String CLIENT_PWD = "demo";
    // 信任库路径
    private final static String TRUST_STRORE_FILE = "C:\\Development\\deployment\\ssl\\ca-demo\\demo.truststore";
    // 信任库密码
    private final static String TRUST_STORE_PWD = "demodemo";


    private static String readResponseBody(InputStream inputStream) throws IOException {
        try{
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            StringBuffer sb = new StringBuffer();
            String buff = null;
            while((buff = br.readLine()) != null){
                sb.append(buff+"\n");
            }
            return sb.toString();
        }finally{
            inputStream.close();
        }
    }

    public static void httpsCall() throws Exception {
        // 初始化密钥库
        KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance("SunX509");
        KeyStore keyStore = getKeyStore(CLIENT_CERT_FILE, CLIENT_PWD, "PKCS12");
        keyManagerFactory.init(keyStore, CLIENT_PWD.toCharArray());

        // 初始化信任库
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance("SunX509");
        KeyStore trustkeyStore = getKeyStore(TRUST_STRORE_FILE, TRUST_STORE_PWD,"JKS");
        trustManagerFactory.init(trustkeyStore);

//        SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, "123456".toCharArray())
//            .loadTrustMaterial(new File(TRUST_STRORE_FILE),"012345".toCharArray()).setSecureRandom(new SecureRandom()).useProtocol("SSL").build();
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,new String[]{"TLSv1", "TLSv2", "TLSv3"},null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());

        CloseableHttpClient closeableHttpClient = HttpClients.custom().setSSLContext(sslContext).build();
        HttpGet getCall = new HttpGet();
        getCall.setURI(new URI("https://ssl.demo.com"));
        CloseableHttpResponse response = closeableHttpClient.execute(getCall);
        System.out.println(convertStreamToString(response.getEntity().getContent()));

    }

    public static String convertStreamToString(InputStream is) {
        /*
          * To convert the InputStream to String we use the BufferedReader.readLine()
          * method. We iterate until the BufferedReader return null which means
          * there's no more data to read. Each line will appended to a StringBuilder
          * and returned as String.
          */
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return sb.toString();
    }

    /**
     * 获得KeyStore
     *
     * @param keyStorePath
     * @param password
     * @return
     * @throws Exception
     */
    private static KeyStore getKeyStore(String keyStorePath, String password,String type)
            throws Exception {
        FileInputStream is = new FileInputStream(keyStorePath);
        KeyStore ks = KeyStore.getInstance(type);
        ks.load(is, password.toCharArray());
        is.close();
        return ks;
    }



    public static void main(String[] args) throws Exception {
        httpsCall();
    }
}

结果同2。

本文的相关源码位于 https://github.com/dreamingodd/CA-generation-demo 


转载自,对原文有补充:https://www.cnblogs.com/dreamingodd/p/7491098.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SSL双向认证是指在SSL连接中,不仅客户端要验证服务器的身份,服务器也要验证客户端的身份。Java实现SSL双向认证一般需要以下步骤: 1. 创建KeyStore和TrustStore,分别存储客户端和服务器的证书和私钥。可以使用keytool工具来创建这些文件。 2. 在服务器端,创建SSLContext对象,并为其指定KeyManagerFactory和TrustManagerFactory。这些工厂类可以使用KeyStore和TrustStore来初始化。 3. 在客户端,创建SSLContext对象,并为其指定TrustManagerFactory。这个工厂类可以使用TrustStore来初始化。 4. 在服务器端,创建ServerSocket并开始监听客户端连接。当客户端连接到服务器时,服务器会为客户端创建一个SSLSocket并开始SSL握手。 5. 在客户端,创建SSLSocket并连接到服务器。客户端和服务器会进行SSL握手,包括交换证书和验证身份等步骤。 6. 客户端和服务器建立SSL连接后,可以开始进行安全通信。 下面是一个简单的Java SSL双向认证的示例代码: 服务器端: ```java KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream("server.jks"), "password".toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(keyStore, "password".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(8443); while (true) { SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); // 处理客户端连接 } ``` 客户端: ```java KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream("client.jks"), "password".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket("localhost", 8443); // 发送和接收数据 ``` 需要注意的是,这只是一个简单的示例代码,实际中还需要考虑证书的管理、密码的安全等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值