目录

一、 研究目的和简要
二、 Openssl生成级联证书
三、 Nginx配置双向认证
四、 Java实现双向认证
五、 证书关系图

一、 研究目的和简要

看着简单的SSL证书,可以说绝对被SSL证书的外表欺骗了。为什么写这么多?我也很讨厌要看一大堆东西才实现的功能!但SSL双向认证真需要这么多字来说明(虽然废话也挺多,例如这句)。笔者研究了1个星期,最终是实现了双向认证,但对ssl认证过程和加密过程还是没有完全明白。网上是有很多关于生成证书和java代码实例,但大多都不能在实际工程场景中发挥作用。内容是挺多的,请耐心看,虽然理解方面存在我的脑洞,但可以保证这个文章绝对不是从网上简单的复制粘贴,是真材实料,杠杠的研究。
目的:
熟悉https请求双向认证的原理和运用方法,并在实际环境中得到正确使用
简要:
由于程序研究过程以及网络培训视频、网络文章等多以自己生成非正规CA发布的证书,绝大部分生成的证书与实际服务器使用的证书差别较大,在笔者生成大量证书(其实也就10套左右)后,感谢地址
https://blog.csdn.net/langeldep/article/details/54675898?utm_source=blogxgwz8
生成的证书经笔者测试可实现双向验证。
测试包括(细节将在“Nginx配置双向认证”和“Java实现双向认证”中体现):同一套证书验证可通过验证,客户端不信任或者信任另一套证书不可通过验证等。
证书生成后,nginx需要正确配置和Java代码正确配置才可达到双向认证成功的效果。Nginx配置错误或者无效,多出现无论以什么证书,只要有证书就可通过验证的情况。在nginx配置正确后,这时Java代码只要没有正确配置就会出现验证失败。
以上是关于“Openssl生成级联证书”、“Nginx配置双向认证”、“Java实现双向认证”等小节之必要的说明。

二、 Openssl生成级联证书

参考来源: https://blog.csdn.net/langeldep/article/details/54675898?utm_source=blogxgwz8
以下所有步骤都是在root用户下执行。

第一步:生成相关目录和文件

笔者使用的是linux虚拟机研究,openssl安装后与参考网站的一样,配置文件在/etc/pki/tls,所以跟着作了:

mkdir /etc/pki/ca_linvo
cd /etc/pki/ca_linvo
mkdir root server client newcerts
echo 01 > serial
echo 01 > crlnumber
touch index.txt

经过思考,生成这些文件可能是为了区分原文件夹,避免证书管理混乱。而笔者在生成第二套证书时,发现用原来的文件夹保存第二套也是有问题的,这涉及到第二步为什么要修改openssl配置文件。所以还是乖乖生成这些文件夹和文件,每新建一套都创建一个。

第二步:修改openssl配置

修改配置之前,笔者担忧出错,所以拷贝了一份配置文件的备份,参考操作为:

cp /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl.cnf_bak
vi /etc/pki/tls/openssl.cnf

说明一下,上面的openssl.cnf就是openssl的配置文件
[ ca ]下的default_ca改为自己的ca名字,例如参考网站中的[ CA_linvo ]
操作方式:注释掉原来的,添加自己的
关于JAVA与nginx双向认证的总结
复制ca设置并改成自己的。原设置是[ CA_default ],赋值一遍粘贴一下就可以了
关于JAVA与nginx双向认证的总结
将自己复制的那一份设置中的下面三个参数改为对应参数,具体的参数根据创建的文件夹而定:

dir = /etc/pki/ca_linvo
certificate = $dir/root/ca.crt
private_key = $dir/root/ca.key

如果只是创建一套,可以忽略这一段话→→→这里certificatec和private_key的参数说明了根证书只能是ca.crt,根私钥只能是ca.key,所以笔者在创建第二套级联证书的根证书时,想到要去修改配置文件怕会造成证书管理混乱,所以在生成第二套时,又创建了一个证书文件夹ca_lan并在配置文件中加入了另一个CA_lan的设置,这样两套证书就分开管理了。
修改完后,shift+ZZ或者:wq保存修改
(tips: vi或者vim在按入i键后进入编辑模式,编辑模式中按Esc退出编辑模式,在这种情况下使用shift+ZZ或者:wq才是退出并保存修改)

第三步:创建根证书

创建根证书私钥:

openssl genrsa -des3 -out /etc/pki/ca_linvo/root/ca.key 2048

创建根证书请求文件:

openssl req -new -key /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/root/ca.csr

创建根证书:

openssl x509 -req -days 3650 -in /etc/pki/ca_linvo/root/ca.csr -signkey /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/root/ca.crt

创建证书吊销列表:

openssl ca -gencrl -out /etc/pki/ca_linvo/root/ca.crl -crldays 7

需要注意这里的路径需要改为自己创建的文件夹路径,例如笔者创建第二套证书时,创建秘钥是使用:openssl genrsa -des3 -out /etc/pki/ca_lan/root/ca.key 2048,不同点是-out后面的路径不同。
ca.key为私钥,csr是请求文件,crt是证书,crl证书吊销列表。
笔者理解如下:
关于JAVA与nginx双向认证的总结
最终目的是得到crt证书。形象地讲,有了私钥是一种身份证明,就像我是有钥匙的人了,我用这个钥匙可以去锁东西了,想打开我锁的东西,那就请得到我允许打开的证明(crt证书),证书怎么来?把我承认的请求(csr)发给我,我就给你证书。这个想象式理解只是笔者自己的脑洞,可能跟实际作用有出入。
可以说笔者并没有深入研究,这里大部分是笔者脑洞,包括所有小节。

第四步:生成服务器证书

生成服务器证书私钥:

openssl genrsa -des3 -out /etc/pki/ca_linvo/server/server.key 2048

生成服务器证书请求文件:

openssl req -new -key /etc/pki/ca_linvo/server/server.key -out /etc/pki/ca_linvo/server/server.csr

生成服务器证书:

openssl ca -in /etc/pki/ca_linvo/server/server.csr -cert /etc/pki/ca_linvo/root/ca.crt -keyfile /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/server/server.crt -days 3650

同样要注意修改一下路径未自己创建的文件夹路径。
关于JAVA与nginx双向认证的总结
同样脑洞开始:证书比根证书多了一个根证书的签名。就像这个证书虽然是一个私钥生成的,但这个有证书还被那个人的老板签了个字,这样它老板可以解释这个证书是不是被认可的。客户端拿着这个证书用根证书验证就可以通过。

第五步:生成客户端证书

生成客户端证书私钥:

openssl genrsa -des3 -out /etc/pki/ca_linvo/client/client.key 2048

生成客户端证书请求文件:

openssl req -new -key /etc/pki/ca_linvo/client/client.key -out /etc/pki/ca_linvo/client/client.csr

生成客户端证书:

openssl ca -in /etc/pki/ca_linvo/client/client.csr -cert /etc/pki/ca_linvo/root/ca.crt -keyfile /etc/pki/ca_linvo/root/ca.key -out /etc/pki/ca_linvo/client/client.crt -days 3650

同样要注意修改一下路径未自己创建的文件夹路径。
方式跟第四步服务器证书生成一样。参考网站中提到服务器似乎不用根证书参与,所以有以下脑洞:
关于JAVA与nginx双向认证的总结
浏览器访问会从服务器中获取服务器证书,所以浏览器只添加客户端证书就可以。
Tips:其它有帮助的命令,以下命令自己注意一下路径和文件名
生成客户端证书安装pfx文件,双击就可安装到电脑中,浏览器就可识别:

openssl pkcs12 -export -inkey /etc/pki/ca_linvo/client/client.key -in /etc/pki/ca_linvo/client/client.crt -out /etc/pki/ca_linvo/client/client.pfx

生成java可用的p12文件:

openssl pkcs12 -export -clcerts -in client-cert.cer -inkey client-key.key -out client.p12

秘钥转非秘秘钥:可以用来转服务器证书,转了非秘秘钥后,nginx重启就不用输入秘钥密码

openssl rsa -in danfeng.key -out danfeng_nopass.key

三、 Nginx配置双向认证

以下内容在nginx配置文件中设置,在主体中设置。

server{
}

其它设置自行去学习,这里只列出配置部分的设置

server{


ssl on;
ssl_certificate /xxxxxxxxxxxxxx/ssl/myssl/server.crt;
ssl_certificate_key /xxxxxxxxxxxxxx/ssl/myssl/server_nopass.key;
ssl_client_certificate /xxxxxxxxxxxxxx/ssl/myssl/ca.crt;
ssl_verify_depth 2;
ssl_verify_client on;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;

}

主要由以下几个参数需要理解:
ssl on : 设置启动https请求
ssl_certificate :设置服务器证书
ssl_certificate_key :设置服务器证书秘钥
ssl_client_certificate :设置根证书,用于验证客户端
ssl_verify_depth :设置验证程度
ssl_verify_client :设置是否开启客户端验证
其它的为什么不写出来?因为我也没有去详细研究

测试过程:
客户端带证书访问可正常访问
客户端不带证书不可访问
注释掉ssl_verify_client那一行,不管有没有客户端证书都可以访问,并且同时注释掉ca证书那一行也一样,而待客户端证书访问的响应数据也是正常。这里怀疑客户端发送内容是不是没有加密?

四、 Java实现双向认证

java需要一个信任服务器证书和客户端证书,下面用两种方式处理证书和信任库(应该针对不同的证书有不同的方式)。

第一种方式:使用httpclient处理:

java代码:参考来源:https://www.cnblogs.com/dreamingodd/p/7491098.html

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
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 HttpsDemo {
    // 客户端证书路径,用了本地绝对路径,需要修改
    private final static String TRUST_PATH = "F:\\自己生成的证书\\openssl证书\\级联证书\\ca_linvo\\server\\tserver.keystore";
    private final static String PFX_PATH = "F:\\自己生成的证书\\openssl证书\\级联证书\\ca_linvo\\client\\client.p12";
    private final static String PFX_PWD = "333333"; //客户端证书密码及**库密码

    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);
            HttpPost httpget = new HttpPost(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 {
        char SEP = File.separatorChar;
        System.setProperty("javax.net.ssl.trustStore", TRUST_PATH);
//        System.out.println(System.getProperty("java.home") + SEP + "lib" + SEP + "security" + SEP + "cacerts");
//        System.setProperty("javax.net.ssl.trustStore", System.getProperty("java.home") + SEP + "lib" + SEP + "security" + SEP + "cacerts");
        System.out.println(sslRequestGet("https://ip:6644/posttest?inputname=23442432424"));
    }

}

其中需要一个插件,若需要jar包类型,自己去网上找:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.2</version>
</dependency>

类中属性:三个常量字符串,一个为服务器证书信任库文件,一个是客户端p12证书文件,一个是客户端证书密码

// 客户端证书路径,用了本地绝对路径,需要修改
private final static String CER_PATH = "F:\\自己生成的证书\\openssl证书\\级联证书\\ca_linvo\\server\\tserver.keystore";
private final static String PFX_PATH = "F:\\自己生成的证书\\openssl证书\\级联证书\\ca_linvo\\client\\client.p12";
private final static String PFX_PWD = "333333"; //客户端证书密码及**库密码

sslRequestGet方法里的代码:
用于将客户端p12证书添加到请求中

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

利用生成的keystore生成SSLContext再生成Httpsclinet

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

在main函数中,需要指定信任库的位置javax.net.ssl.trustStore。

System.setProperty("javax.net.ssl.trustStore", TRUST_PATH);

如果不是main函数执行,则可以通过手动添加到JDK或JRE里面的信任库。
下面是关于手动导入、查看、删除JDK或JRE信任库的命令:
1、添加证书到信任库:
这其中遇到一个问题,电脑有好几JDK版本,甚至一个版本甚至装了一个JRE和JDK里面JRE,要添加的是哪个JRE里面。答案是根据运行的情况或者开发使用的情况,到相应的地方,例如我使用JDK里面的JRE作为开发,那么使用的lib是xxxxx/JDK1.8.0/jre/lib。
记得修改下面命令中,lib/security/cacerts的路径。默认密码是changeit。

keytool -import -alias FIDDLER -keystore ../lib/security/cacerts -file 代理工具证书路径\证书文件名

2、查看信任库信息:
同样记得注意下面的路径。

keytool -list -keystore "xxxxx/jre/lib/security/cacerts"  -storepass changeit

如果要根据名称部分匹配,可以在后面加上,看清楚前面有个|按键哦

|findstr xxx

可以根据查出来的别名alias删除

keytool -delete -alias taobao -keystore "xxxx/lib/security/cacerts"  -storepass changeit

第二种方式使用JDK的类处理:

java代码:
https部分参考来源 https://blog.csdn.net/hellozpc/article/details/82077763
http很久之前在网上找的,很悲伤的事情是没有记录下来从哪里抄来的了,往原作者有怪莫怪。

import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyStore;
import java.util.List;
import java.util.Map;

public class URLConnClientUtil {
    private final static String charset = "UTF-8";
    protected static String clientKeyType = "JKS";
    protected static String truestKeyType = "JKS";

    public String getTruestKeyType() {
        return truestKeyType;
    }

    public void setTruestKeyType(String truestKeyType) {
        this.truestKeyType = truestKeyType;
    }

    public String getClientKeyType() {
        return clientKeyType;
    }

    public void setClientKeyType(String clientKeyType) {
        this.clientKeyType = clientKeyType;
    }
    /**
     * @param urlCon
     */
    private static void closeUrlCon(HttpsURLConnection urlCon) {
        if (urlCon != null) {
            urlCon.disconnect();
        }
    }

    /**
     * @param is
     * @param br
     * @throws IOException
     */
    private static void closeInputStream(InputStream is, BufferedReader br)
            throws IOException {
        if (br != null) {
            br.close();
        }
        if (is != null) {
            is.close();
        }
    }

    /**
     * @param os
     * @param fis
     * @param bis
     * @throws IOException
     */
    private static void closeOutputStream(OutputStream os, InputStream fis,
                                          BufferedInputStream bis) throws IOException {
        if (os != null) {
            os.flush();
        }
        if (bis != null) {
            bis.close();
        }
        if (fis != null) {
            fis.close();
        }
        if (os != null) {
            os.close();
        }
    }

    public static String sendHttpsGet(String param, String requestAddr,String clientCertPath,String clientCertPwd,String trustCertPath,String truestCertPwd) {

        SSLContext sslContext = null;

        String requestData = param;
        requestAddr = requestAddr + "?" + requestData;
        StringBuffer sb = null;
        HttpsURLConnection urlCon = null;

        OutputStream os = null;
        InputStream fis = null;
        BufferedInputStream bis = null;

        InputStream is = null;
        BufferedReader br = null;
        try {
            if (sslContext == null) {
                sslContext = SSLContext.getInstance("SSL");
                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
                KeyManager[] keyManagers = null;
                TrustManager[] trustManagers = null;
                if(clientCertPath != null){
                    KeyStore keyStore = KeyStore.getInstance(clientKeyType);
                    keyStore.load(new FileInputStream(clientCertPath),clientCertPwd == null?null:clientCertPwd.toCharArray());
                    kmf.init(keyStore, clientCertPwd.toCharArray());
                    keyManagers = kmf.getKeyManagers();
                }

                if(trustCertPath != null){
                    KeyStore trustKeyStore = KeyStore.getInstance(truestKeyType);
                    trustKeyStore.load(new FileInputStream(trustCertPath),truestCertPwd == null?null:truestCertPwd.toCharArray());
                    tmf.init(trustKeyStore);
                    trustManagers = tmf.getTrustManagers();
                }

                sslContext.init(keyManagers, trustManagers, null);
                HostnameVerifier hostnameVerifier = new HostnameVerifier() {
                    @Override
                    public boolean verify(String arg0, SSLSession arg1) {
                        return true;
                    }
                };
                HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
            }

            URL url = new URL(requestAddr);
            urlCon = (HttpsURLConnection) url.openConnection();
            urlCon.setRequestMethod("GET");
            //urlCon.setRequestProperty("Content-type", "text/xml;charset=" + charset);
            urlCon.setSSLSocketFactory(sslContext.getSocketFactory());
            urlCon.connect();

            closeOutputStream(os, fis, bis);

            is = urlCon.getInputStream();
            br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                closeOutputStream(os, fis, bis);
                closeInputStream(is, br);
                closeUrlCon(urlCon);
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return null;
    }

    public static String sendHttpsPost(String param, String requestAddr,String clientCertPath,String clientCertPwd,String trustCertPath,String truestCertPwd) {

        SSLContext sslContext = null;

        String requestData = param;
        StringBuffer sb = null;
        HttpsURLConnection urlCon = null;

        OutputStream os = null;
        InputStream fis = null;
        BufferedInputStream bis = null;

        InputStream is = null;
        BufferedReader br = null;
        try {
            if (sslContext == null) {
                sslContext = SSLContext.getInstance("SSL");
                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

                KeyManager[] keyManagers = null;
                TrustManager[] trustManagers = null;
                if(clientCertPath != null){
                    KeyStore keyStore = KeyStore.getInstance(clientKeyType);
                    keyStore.load(new FileInputStream(clientCertPath),clientCertPwd == null?null:clientCertPwd.toCharArray());
                    kmf.init(keyStore, clientCertPwd.toCharArray());
                    keyManagers = kmf.getKeyManagers();
                }

                if(trustCertPath != null){
                    KeyStore trustKeyStore = KeyStore.getInstance(truestKeyType);
                    trustKeyStore.load(new FileInputStream(trustCertPath),truestCertPwd == null?null:truestCertPwd.toCharArray());
                    tmf.init(trustKeyStore);
                    trustManagers = tmf.getTrustManagers();
                }

                sslContext.init(keyManagers, trustManagers, null);
                HostnameVerifier hostnameVerifier = new HostnameVerifier() {
                    @Override
                    public boolean verify(String arg0, SSLSession arg1) {
                        return true;
                    }
                };
                HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
            }

            URL url = new URL(requestAddr);
            urlCon = (HttpsURLConnection) url.openConnection();

            urlCon.setDoOutput(true);
            urlCon.setDoInput(true);
            urlCon.setRequestMethod("POST");
            //urlCon.setRequestProperty("Content-type", "text/xml;charset=" + charset);
            urlCon.setSSLSocketFactory(sslContext.getSocketFactory());

            os = urlCon.getOutputStream();
            fis = new ByteArrayInputStream(requestData.getBytes(charset));
            bis = new BufferedInputStream(fis);
            byte[] bytes = new byte[1024];
            int len = -1;
            while ((len = bis.read(bytes)) != -1) {
                os.write(bytes, 0, len);
            }
            closeOutputStream(os, fis, bis);

            is = urlCon.getInputStream();
            br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                closeOutputStream(os, fis, bis);
                closeInputStream(is, br);
                closeUrlCon(urlCon);
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return null;
    }

    public static String sendGet(String url, String param) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();
            Map<String, List<String>> map = connection.getHeaderFields();
            for (String key : map.keySet()) {
                System.out.println(key + "--->" + map.get(key));
            }
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("Sending GET exception!" + e);
            e.printStackTrace();
        }
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }
    
	public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("Sending POST exception!"+e);
            e.printStackTrace();
        }
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }
}

这个java文本包括了使用https和http分别用post和get方法请求。其中有涉及到需要设置请求头等需要修改的可以自行在工具类中改,或者自己再封装一层。
https处使用的是两个证书库,一个是添加了服务器证书的信任库,一个是使用p12做成的证书库(应该直接使用p12也可以)
其中代码:
生成客户端证书库对象,修改了参考网站中的代码为了适配单向认证。

if(clientCertPath != null){
    KeyStore keyStore = KeyStore.getInstance(clientKeyType);
    keyStore.load(new FileInputStream(clientCertPath),clientCertPwd == null?null:clientCertPwd.toCharArray());
    kmf.init(keyStore, clientCertPwd.toCharArray());
    keyManagers = kmf.getKeyManagers();
}

生成信任库对象:

if(trustCertPath != null){
    KeyStore trustKeyStore = KeyStore.getInstance(truestKeyType);
    trustKeyStore.load(new FileInputStream(trustCertPath),truestCertPwd == null?null:truestCertPwd.toCharArray());
    tmf.init(trustKeyStore);
    trustManagers = tmf.getTrustManagers();
}

使用证书库和信任库生成SSLContext:

sslContext.init(keyManagers, trustManagers, null);

下面的代码应该是为了匹配证书中域名和服务器域名不匹配问题:

HostnameVerifier hostnameVerifier = new HostnameVerifier() {
    @Override
    public boolean verify(String arg0, SSLSession arg1) {
        return true;
    }
};
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

其中get和post连接和发送参数的不同点就请自行看java文件。
笔者使用的是第二种,因为工程运行过程不是main执行,而且第二种方式可以省去手动添加信任库的过程。
下面是如何利用p12文件生成客户端证书库和生成服务器证书信任库的命令,使用的是keytool。
生成客户端证书库,其中changeit(123456)换成你自己的密码,payment.keystore是生成后的文件名,可以改成自己想要的文件名。

keytool -importkeystore -v  -srckeystore client.p12 -srcstoretype pkcs12 -srcstorepass changeit(123456) -destkeystore payment.keystore -deststoretype jks -deststorepass changeit(123456)

生成服务器证书信任库

keytool -import -alias clientkey -file client.crt -keystore tserver.keystore

有了这些基本就完成了。

五、 证书关系图

关于JAVA与nginx双向认证的总结
简单点理解:
ca根秘钥生成证书后就隐藏身份了,只提供证书给外界。
下级证书都有ca证书参与。
下级有了证书后,根据自己所需和自己的秘钥生成对应证书文件。
结合之前的图,客户端有证书可以跟服务器的CA证书进行验证,服务器有证书跟客户端的信任库进行验证。

相关文章:

  • 2022-01-08
  • 2021-06-18
  • 2021-11-11
  • 2022-01-12
  • 2021-05-27
  • 2021-03-27
  • 2021-07-10
  • 2022-02-04
猜你喜欢
  • 2021-11-20
  • 2021-05-27
  • 2022-02-06
  • 2021-11-17
  • 2021-08-02
  • 2022-12-23
相关资源
相似解决方案