关于https的双向认证详解tomcat+java实现交叉验证

关于https的双向认证详解

原doc文档导出pdf

https://github.com/enderwsp/share4u/raw/master/%E5%85%B3%E4%BA%8Ehttps%E7%9A%84%E5%8F%8C%E5%90%91%E8%AE%A4%E8%AF%81%E8%AF%A6%E8%A7%A3tomcat%2Bjava%E5%AE%9E%E7%8E%B0%E4%BA%A4%E5%8F%89%E9%AA%8C%E8%AF%81.pdf

基本描述

Https是在基本的http通讯协议上增加的tls安全协议,tls安全协议需要ssl安全证书作为加解密要素,双向认证是指在客户端确认服务的是否可信任的单向认证基础上增加服务端的对客户端的认证。

主要内容

https通信协议

是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL

参见 百度https百科

tls安全协议

SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密

参见 百度SSL

 

ssl安全证书

SSL 证书就是遵守 SSL协议,由受信任的数字证书颁发机构CA,在验证服务器身份后颁发,具有服务器身份验证和数据传输加密功能

 

参见 百度ssl证书

证书链

证书链的描述,证书链的可信传递机制,以及根证书的来源和查看方式

 

证书格式

常见证书格式和转换

https://blog.csdn.net/justinjing0612/article/details/7770301

tls认证过程

SSL认证是指客户端到服务器端的认证。主要用来提供对用户和服务器的认证;对传送的数据进行加密和隐藏;确保数据在传送中不被改变,即数据的完整性,现已成为该领域中全球化的标准,即所谓的单向认证

参见 百度ssl认证

其他内容

HTTPS实战之单向验证和双向验证

https://www.jianshu.com/p/119c4dbb7225

 

浅谈HTTPS(SSL/TLS)原理

https://www.jianshu.com/p/41f7ae43e37b

      

HTTPS通信中的身份认证机制

 

https://blog.csdn.net/bravegogo/article/details/60766773

 

SSL双向认证以及证书的制作和使用-https+客户端身份验证

https://blog.csdn.net/soarheaven/article/details/78784848

 

X - Certificate and Key management证书管理工具

https://hohnstaedt.de/xca/index.php

 

ssl证书相当于安全的钥匙,如果私钥外泄被恶意盗用,会存在安全问题

双向认证的常用场景

银行网银U盾使用

专业版登录支持U盾认证多重认证

资金动账类交易U盾再次认证确认客户端身份

金融支付工具安全数字证书

支付宝数字证书app下载安装可以提高app支付安全,支付额度

常用pc安装数字证书免除重复的其他安全校验

其他对通信安全有较高要求的都可以应用此https双向认证

主要成本在于向具有CA资质的机构申请有效的证书,非CA机构的自制根证书及证书链管理需要自行负责

https应用的使用示例

工具版本说明

本地操作系统版本:win10

本地tomcat版本9

本地java版本1.8

证书管理工具xca 2.1.2

Keytool界面工具1.6

http://enkj.jb51.net:81/201703/tools/keytool_jb51.rar

 

 

前提概要

客户端和服务端角色根据通信请求来确定,客户端和服务端需要保持支持一致的TLS安全协议版本

单向认证说明

 

单向认证首先需要申请制作证书链

 

 

 

对于客户端请求服务端,比较常见的客户端是指浏览网页使用的浏览器(chrome,IE,firefox等)以及移动客户端(ios或android app),服务端一般指客户端请求目标服务的服务提供者

 

双向认证Java的https客户端请求

Keystool工具使用

请参考网络使用说明

依照java的keytool命令界面化工具,生成java专有的jks格式容器文件

依赖包



            org.apache.commons

            commons-io

            1.3.2

        

        

        

            org.apache.httpcomponents

            httpclient

            4.5.9

        

 

Java client demo

package com.web.ssl.twoway.ssltwoway;



import com.web.ssl.twoway.ssltwoway.Constants;

import org.apache.commons.io.IOUtils;

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.conn.ssl.TrustSelfSignedStrategy;

import org.apache.http.conn.ssl.TrustStrategy;

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 com.web.ssl.twoway.ssltwoway.Constants;



import javax.net.ssl.SSLContext;

import java.io.File;

import java.io.FileInputStream;

import java.security.KeyStore;

import java.security.cert.CertificateException;

import java.security.cert.X509Certificate;



public class SSLhttps {





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

        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        keyStore.load(new FileInputStream(new File(Constants.clientPrivateFile)), Constants.clientPrivatePassword.toCharArray());

        SSLContext sslcontext = SSLContexts.custom()

                //忽略掉对服务器端证书的校验

                .loadTrustMaterial(new TrustStrategy() {

                    public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {

                        return true;

                    }

                })



                //加载服务端提供的truststore(如果服务器提供truststore的话就不用忽略对服务器端证书的校验了)

                .loadTrustMaterial(new File(Constants.trustAcFile), Constants.trustAcFilePassword.toCharArray(),

                        new TrustSelfSignedStrategy())

                .loadKeyMaterial(keyStore, Constants.clientPrivatePassword.toCharArray())

                .build();

        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(

                sslcontext,

                new String[]{"TLSv1"},

                null,

                SSLConnectionSocketFactory.getDefaultHostnameVerifier());

        CloseableHttpClient httpclient = HttpClients.custom()

                .setSSLSocketFactory(sslConnectionSocketFactory)//取消改行注释不导入运行环境的客户端证书 AAA

                .build();



        try {



            HttpGet httpget = new HttpGet("https://localhost:8443/");

//            若注释 AAA 行的内容,请求需要双向认证的服务会出现以下异常

//unable to find valid certification path to requested target

            System.out.println("Executing request " + httpget.getRequestLine());



            CloseableHttpResponse response = httpclient.execute(httpget);

            try {

                HttpEntity entity = response.getEntity();

                System.out.println(response.getStatusLine());

                System.out.println(IOUtils.toString(entity.getContent()));

                EntityUtils.consume(entity);

            } finally {

                response.close();

            }

        } finally {

            httpclient.close();

        }

    }

}

Java httpsServer demo

 


 

package com.web.ssl.twoway.ssltwoway;



import java.io.BufferedReader;

import java.io.FileInputStream;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.security.KeyStore;

import java.security.cert.Certificate;



import javax.net.ssl.KeyManager;

import javax.net.ssl.KeyManagerFactory;

import javax.net.ssl.SSLContext;

import javax.net.ssl.SSLServerSocket;

import javax.net.ssl.SSLServerSocketFactory;

import javax.net.ssl.SSLSession;

import javax.net.ssl.SSLSocket;

import javax.net.ssl.TrustManager;

import javax.net.ssl.TrustManagerFactory;

import javax.security.cert.X509Certificate;



public class HTTPSServer {

    private boolean isServerDone = false;



    public static void main(String[] args) {

        HTTPSServer server = new HTTPSServer();

        server.run();

    }



    // Create the and initialize the SSLContext

    private SSLContext createSSLContext() {

        try {

            KeyStore keyStore = KeyStore.getInstance("PKCS12");

            keyStore.load(new FileInputStream(Constants.serverPrivateFile), Constants.serverPrivatePassword.toCharArray());



            // Create key manager

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");

            keyManagerFactory.init(keyStore, Constants.serverPrivatePassword.toCharArray());

            KeyManager[] km = keyManagerFactory.getKeyManagers();

            KeyStore keyStoreTrust = KeyStore.getInstance("JKS");

            keyStoreTrust.load(new FileInputStream(Constants.trustAcFile), Constants.trustAcFilePassword.toCharArray());

            // Create trust manager

            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");

            trustManagerFactory.init(keyStoreTrust);

            TrustManager[] tm = trustManagerFactory.getTrustManagers();



            // Initialize SSLContext

            SSLContext sslContext = SSLContext.getInstance("TLS");

            sslContext.init(km, tm, null);



            return sslContext;

        } catch (Exception ex) {

            ex.printStackTrace();

        }



        return null;

    }



    // Start to run the server

    public void run() {

        SSLContext sslContext = this.createSSLContext();



        try {

            // Create server socket factory

            SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();



            // Create server socket

            SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(Constants.DEFAULT_PORT);



            System.out.println("SSL server started");

            while (!isServerDone) {

                SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();



                // Start the server thread

                new ServerThread(sslSocket).start();

            }

        } catch (Exception ex) {

            ex.printStackTrace();

        }

    }



    // Thread handling the socket from client

    static class ServerThread extends Thread {

        private SSLSocket sslSocket = null;



        ServerThread(SSLSocket sslSocketInit) {

            sslSocket = sslSocketInit;

            sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());

            sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols());

            sslSocket.setNeedClientAuth(true);

            sslSocket.setWantClientAuth(true);

            sslSocket.setEnableSessionCreation(true);

        }



        public void run() {



            try {

                // Start handshake

                sslSocket.startHandshake();



                // Get session after the connection is established

                SSLSession sslSession = sslSocket.getSession();



                System.out.println("SSLSession :");

                System.out.println("\tProtocol : " + sslSession.getProtocol());

                System.out.println("\tCipher suite : " + sslSession.getCipherSuite());



                // Start handling application content

                InputStream inputStream = sslSocket.getInputStream();

                OutputStream outputStream = sslSocket.getOutputStream();



                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

                PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));



                String line = null;

                while ((line = bufferedReader.readLine()) != null) {

                    System.out.println("Inut : " + line);



                    if (line.trim().isEmpty()) {

                        break;

                    }

                }

                boolean clientCertValid = false;

                try {

                    Certificate[] c1 = sslSession.getLocalCertificates();

                    for (Certificate c1One : c1) {

                        System.out.println("getLocalCertificates : " + c1One.toString());

                    }

                    X509Certificate[] c2 = sslSession.getPeerCertificateChain();

                    for (X509Certificate c1One : c2) {

                        System.out.println("getPeerCertificateChain : " + c1One.toString());

                    }

                    Certificate[] c3 = sslSession.getPeerCertificates();

                    for (Certificate c1One : c3) {

                        System.out.println("getPeerCertificates : " + c1One.toString());

                    }

                    clientCertValid = true;

                } catch (Exception cex) {



                }

                if (clientCertValid) {

                    // Write data

                    printWriter.print("HTTP/1.1 200 OK\r\nServer: johnserver/1.0.8.18\r\nContent-Length: 3\r\n\r\nok\n");



                    printWriter.flush();

                } else {

                    // Write data

                    printWriter.print("HTTP/1.1 200 FAILED\r\nServer: johnserver/1.0.8.18\r\nContent-Length: 6\r\n\r\nFAILED\n");



                    printWriter.flush();

                }

                sslSocket.close();

            } catch (Exception ex) {

                ex.printStackTrace();

            }

        }

    }

}

 

证书链制作

--也可以使用其他证书管理工具(keytool、openssl等)

先下载安装xca工具,地址是http://xca.hohnstaedt.de/

先用xca创建一本ca证书,

本次说明使用的版本为2.1.2

运行C:\Aprograms\Agreen\xca-portable-2.1.2\xca-portable-2.1.2\xca.exe

后打开的界面

关于https的双向认证详解tomcat+java实现交叉验证_第1张图片

依次File-- New DataBase,

关于https的双向认证详解tomcat+java实现交叉验证_第2张图片

选择xdb文件保存路径,输入文件名,点击保存,

关于https的双向认证详解tomcat+java实现交叉验证_第3张图片

再输入密码(演示密码123456)

关于https的双向认证详解tomcat+java实现交叉验证_第4张图片

点击OK确定后,回到主界面

关于https的双向认证详解tomcat+java实现交叉验证_第5张图片

切换到Certificates页面,点击New Certificate

关于https的双向认证详解tomcat+java实现交叉验证_第6张图片

创建根证书,

在source栏,签名算法选择SHA 512,证书模版选择默认CA,再点击Apply all

修改任何内容记得保存

关于https的双向认证详解tomcat+java实现交叉验证_第7张图片

在extensions栏,type选择Certificate Authority

可选—time range 有效期设置10年后点击apply

关于https的双向认证详解tomcat+java实现交叉验证_第8张图片

Subject栏,填好各个字段,都可以随便填

关于https的双向认证详解tomcat+java实现交叉验证_第9张图片

点击Generate a new key生产私钥

关于https的双向认证详解tomcat+java实现交叉验证_第10张图片

不修改内容,点击Create

关于https的双向认证详解tomcat+java实现交叉验证_第11张图片

然后点击新建证书的OK’

关于https的双向认证详解tomcat+java实现交叉验证_第12张图片

CA证书做好了,有效期默认10年

关于https的双向认证详解tomcat+java实现交叉验证_第13张图片

根证书导出成只包含公钥的证书格式,这本根证书就是放在网站上供用户下载安装,或主动安装到客户机器中的

选择创建好的根证书,点击导出export

关于https的双向认证详解tomcat+java实现交叉验证_第14张图片

Filename可以选择导出文件路径

关于https的双向认证详解tomcat+java实现交叉验证_第15张图片

点击OK

关于https的双向认证详解tomcat+java实现交叉验证_第16张图片

制作服务器证书、客户端证书和制作CA证书差不多,只有两个地方不一样:

1选择已经制作好的根CA,然后点击New Certificate1

关于https的双向认证详解tomcat+java实现交叉验证_第17张图片

2

签名时,选择使用根证书,这里是seeme根证书进行签名颁发,然后证书模版选择服务器HTTPS SERver(制作客户端证书就选择HTTPS_client),其他都和制作根证书一样,然后点击Apply all(这个一定不能忘),然后再切到Subject、Extension页面填写相应的东西就OK了

Server证书

关于https的双向认证详解tomcat+java实现交叉验证_第18张图片

Extensions不修改

关于https的双向认证详解tomcat+java实现交叉验证_第19张图片

Subject填入信息后点击generate生成证书

关于https的双向认证详解tomcat+java实现交叉验证_第20张图片

关于https的双向认证详解tomcat+java实现交叉验证_第21张图片

点击create

关于https的双向认证详解tomcat+java实现交叉验证_第22张图片

点击新建证书的OK

Server证书创建成功后的展示

关于https的双向认证详解tomcat+java实现交叉验证_第23张图片

关于https的双向认证详解tomcat+java实现交叉验证_第24张图片

 

再将服务器证书导出来,选择p12格式

关于https的双向认证详解tomcat+java实现交叉验证_第25张图片

 

输入server p12导出的密码

(演示的密码112233)

 

新增https client证书

关于https的双向认证详解tomcat+java实现交叉验证_第26张图片

关于https的双向认证详解tomcat+java实现交叉验证_第27张图片

 

 

关于https的双向认证详解tomcat+java实现交叉验证_第28张图片

关于https的双向认证详解tomcat+java实现交叉验证_第29张图片

 

点击右下角的generate

关于https的双向认证详解tomcat+java实现交叉验证_第30张图片

点击create

关于https的双向认证详解tomcat+java实现交叉验证_第31张图片

点击OK

关于https的双向认证详解tomcat+java实现交叉验证_第32张图片

 

关于https的双向认证详解tomcat+java实现交叉验证_第33张图片

演示的密码111222

所有的证书文件

关于https的双向认证详解tomcat+java实现交叉验证_第34张图片

关于https的双向认证详解tomcat+java实现交叉验证_第35张图片

关于https的双向认证详解tomcat+java实现交叉验证_第36张图片

关于https的双向认证详解tomcat+java实现交叉验证_第37张图片

关于https的双向认证详解tomcat+java实现交叉验证_第38张图片

演示的证书passwords密码

Xdb证书容器123456

服务器私钥123123

客户端私钥111222

个人客户端证书导入注意

修改本地客户端证书(删除或者重新导入)

关于https的双向认证详解tomcat+java实现交叉验证_第39张图片

千万记得点击清除SSL状态

关于https的双向认证详解tomcat+java实现交叉验证_第40张图片

tomcat配置准备

作为双向认证支持的web容器,用作java实现的客户端和服务端交叉验证双向认证有效性

Tomcat9下载

https://tomcat.apache.org/download-90.cgi

导出的服务器p12证书放在tomcat的conf/sslcerts目录下,(当然也可以使用绝对路径)

修改默认配置,打开https,设置单向认证

Conf/server.xml

Service节点新增

               maxThreads="150" SSLEnabled="true" secure="true" slProtocol="TLS">

       

           

       

   

 

Conf/web.xml

welcome-file-list节点后新增

 

     

    CLIENT-CERT 

    Client Cert Users-only Area 

 

 

     

     

        SSL 

        /* 

     

     

        CONFIDENTIAL  

     

安装CA根证书

双击root.crt

点击安装证书

关于https的双向认证详解tomcat+java实现交叉验证_第41张图片

关于https的双向认证详解tomcat+java实现交叉验证_第42张图片

选择本地计算机

关于https的双向认证详解tomcat+java实现交叉验证_第43张图片

点下一步

关于https的双向认证详解tomcat+java实现交叉验证_第44张图片

选择 将所有的证书都放入下列存储

关于https的双向认证详解tomcat+java实现交叉验证_第45张图片

点击浏览选择受信任的根证书机构

点击确定

关于https的双向认证详解tomcat+java实现交叉验证_第46张图片

点击下一步直到完成

验证单向认证tomcat

启动tomcat

打开地址https://localhost:8443/

点击证书查看按钮(IE 为一个锁的形状)

关于https的双向认证详解tomcat+java实现交叉验证_第47张图片

关于https的双向认证详解tomcat+java实现交叉验证_第48张图片

点击查看证书

关于https的双向认证详解tomcat+java实现交叉验证_第49张图片

点击证书路径

关于https的双向认证详解tomcat+java实现交叉验证_第50张图片

 

显示该证书没有问题就是正常的

 

配置双向认证tomcat

配置需求说明

本次使用的tomcat9

服务端

在原有服务的私钥证书配置好的基础上,新增证书容器(包含客户端证书的根证书,能认证客户端公钥有效性的)

客户端

在原有的(安装服务端证书的根证书后)认证服务端证书的同时,提供客户端公钥证书给服务端ssl请求

Tomcat配置修改

在新增的connector中

    sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"

    port="8443" maxThreads="200"

    scheme="https" secure="true" SSLEnabled="true"

    keystoreFile="conf/sslcerts/localhost.p12" keystorePass="123123"

    truststoreFile="conf/sslcerts/trustme.jks" truststorePass="1q2w3e"

    clientAuth="true" sslProtocol="TLS" />

重启tomcat 后

双向验证服务端检查

打开IE(显示不明确),chrome(本地暂无客户端私钥,客户端证书暂时不提供)

关于https的双向认证详解tomcat+java实现交叉验证_第51张图片

提示ERR_BAD_SSL_CLIENT_AUTH_CERT表示当前浏览器访问ssl核验客户端证书失败

双向认证客户端检查

导入客户端证书到本机证书内容

IE –工具—

关于https的双向认证详解tomcat+java实现交叉验证_第52张图片

打开internet选项

关于https的双向认证详解tomcat+java实现交叉验证_第53张图片

点击内容栏

关于https的双向认证详解tomcat+java实现交叉验证_第54张图片

点击证书

关于https的双向认证详解tomcat+java实现交叉验证_第55张图片

点击导入证书

关于https的双向认证详解tomcat+java实现交叉验证_第56张图片

点击下一步

关于https的双向认证详解tomcat+java实现交叉验证_第57张图片

点击浏览选择客户端私钥文件

关于https的双向认证详解tomcat+java实现交叉验证_第58张图片

点击下一步

关于https的双向认证详解tomcat+java实现交叉验证_第59张图片

输入客户端私钥密码

关于https的双向认证详解tomcat+java实现交叉验证_第60张图片

勾选启用强私钥保护

关于https的双向认证详解tomcat+java实现交叉验证_第61张图片

点击下一步直到导入成功,

关于https的双向认证详解tomcat+java实现交叉验证_第62张图片

个人类的证书列表里面会多出刚导入的客户端证书

关于https的双向认证详解tomcat+java实现交叉验证_第63张图片

关于https的双向认证详解tomcat+java实现交叉验证_第64张图片

刷新浏览器

关于https的双向认证详解tomcat+java实现交叉验证_第65张图片

选择证书

关于https的双向认证详解tomcat+java实现交叉验证_第66张图片

选择后续所有的允许

最好能成功打开页面

注意双向认证权限读取申请

浏览器针对同一地址的双向认证才会弹出证书选择框

后续出现的安全要点允许

有需要重复操作的请按照个人客户端证书导入注意

关于https的双向认证详解tomcat+java实现交叉验证_第67张图片

本文的java demo 工程地址

https://github.com/enderwsp/sslDemo

工程内包括已经生成的服务器证书 客户端证书 根证书

本来是有一个ngnix的web服务版本做交叉验证的,但没有能和xca证书管理工具很好的搭配使用(不能导出key文件证书,需要openssl命令)

 

你可能感兴趣的:(dev,web)