HTTPS:HTTPS双向SSL认证配置总结_20191017_七侠镇莫尛貝

N年前搞过https双向ssl认证,记得很简单(还是winxp + IE得年代)。

最近又遇到https双向认证的需求,测试了一下,竟然问题一大堆。

听说是这几年相关业内发生了一些事,https双向认证策略变得比以前严格了。

严格可以理解,不过各个浏览器厂商使用不同的规范就显得有点乱了。比如谷歌浏览器和火狐对https相关的要求和界面表现就不太一样。乱猜可能是大厂为争夺规范制定的话语权导致。

 

过程记录和注意事项整理如下:

1测试环境:tomcat8, jkd8,win7x64, 主要测试了谷歌浏览器74版,(火狐浏览器48版版本比较老了,捎带测了一下,最新量子版有时间再测)。

服务器证书和客户端证书,使用公司的CA产品签发。服务器证书SHA256 RSA2048。客户端证书SHA256   RSA2048。

推荐一个工具:KeyStore Explorer,可方便地把pfx和jks互相转换,制作keystore.比keytool命令行方便多了。

2.tomcat配置:server.xml

这里9443是https单向认证的端口;9444是https双向认证的端口。区别是clientAuth的值不同。

3.服务器证书的格式要求:

一个是增强秘钥用法。一个是使用者备用名称。

使用者备用名称里可以包含多个域名。比如,这里我还写入了一个www.baidu.com的域名,修改hosts文件后测试完全没问题。

如果是内网IP设备证书,使用者别用名称要用IP Address而不是DNS Name。多个IP Address应该也可以,没时间测试。

服务器证书如果不包含使用者备用名称,谷歌浏览器会有警告:

正常应该是酱婶儿滴:

不过火狐浏览器我不管咋搞都有警告:

不知道咋回事儿,初步怀疑和服务器的根证书使用SHA1算法有关。后面有时间再换个SHA256的根试试。

有了解原因的大肘子请不吝赐教。

20191028补充:

火狐的警告问题,解决了。方法是手动设置服务器证书的根信任问题,如下图:

 

 

4.客户端证书的格式要求:

一个是Netscape Cert Type=client,必须的,否则无法用于https双向认证登陆:

一个是增强密钥用法。这里有个比较奇怪的现象:如果客户端证书单包含安全电子邮件属性,  则谷歌浏览器在https登陆时,证书选择框里不显示这个客户端证书!? 如果增强密钥用法包含客户端身份验证属性,或者增强密钥用法干脆为空,则谷歌浏览器在https登陆时,证书选择框里正常显示这个客户端证书。原因未知。

5.浏览器测试:

火狐浏览器要先把客户端证书文件导入浏览器才能用作https双向ssl认证,麻烦!

 

谷歌浏览器就可以直接用系统里的证书,好评。

 

6.Java 连接https服务器双向ssl认证代码示例:


//https://blog.csdn.net/u010096403/article/details/81027835
import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.security.KeyStore;
import java.util.logging.Logger;

public class Main {

    public static String doPostsWithOwnCerSSL2(String url,String params,String type,String charset) {
        PrintWriter out = null;
        InputStream in = null;
        String result = null;
        Logger logger = Logger.getLogger("test logger");
        try
        {
            // tomcat 的服务器证书的根证书,不必包含服务器证书
            //String trustStorePath = "server.jks";
            String trustStorePath = "server_rootca_only.jks";
            String trustStorePassword = "password";

            //注意:客户端证书必须要包含nsCertType=client属性,否则无法创建ssl连接。
            //注意:客户端证书不必包含证书链
            //String privatecert = "ssl_client_auth_1234.pfx";
            String privatecert = "ssl_client_auth_1234_nochain.pfx";
            //String privatecert = "ssl_client_auth_1234_bad.pfx";
            String privatecertPsd = "1234";

            //创建SSL对象
            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
            SSLContext sslContext =SSLContext.getInstance("SSL", "SunJSSE");

            //重写509TrustManager
            //添加信任库,也就是添加服务器的公钥证书进去
            TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance("SunX509");
            KeyStore trustKeyStore=KeyStore.getInstance("JKS");
            trustKeyStore.load(new FileInputStream(trustStorePath),trustStorePassword.toCharArray());
            trustManagerFactory.init(trustKeyStore);
            TrustManager[]  trustManagers=trustManagerFactory.getTrustManagers();

            //添加私钥证书,也就是客户端创建的p12证书,公钥已经给到服务器,在tomcat中已经配置
            KeyManagerFactory kmf =KeyManagerFactory.getInstance("SunX509","SunJSSE");
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(new FileInputStream(privatecert),privatecertPsd.toCharArray());
            kmf.init(keyStore,privatecertPsd.toCharArray());
            KeyManager[] keyManagers = kmf.getKeyManagers();

            sslContext.init(keyManagers,trustManagers, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf =sslContext.getSocketFactory();
            URL uri = new URL(url);
            HttpsURLConnection urlcon =(HttpsURLConnection) uri.openConnection();
            urlcon.setDoInput(true);
            urlcon.setDoOutput(true);
            urlcon.setSSLSocketFactory(ssf);

            urlcon.setRequestMethod(type.toUpperCase());

            urlcon.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            });

            urlcon.connect();
            out = new PrintWriter(urlcon.getOutputStream());
//          out.print(params);
            out.write(params);//相比print,write更直观一点
            out.flush();//数据送出
            in = urlcon.getInputStream();
            BufferedReader buffer = new BufferedReader(new InputStreamReader( in, charset));
            StringBuffer bs = new StringBuffer();
            String line = null;
            while ((line =buffer.readLine()) != null) {
                bs.append(line);
            }
            result = bs.toString();
        }
        catch (Exception e)
        {
            logger.info(e.getMessage());
            result = "ERROR:"+e.getMessage();
        }
        logger.info(result);
        return result;
    }

    public static void main(String[] args) {
        String url="https://www.httstest.cn:9024/app/password.logon";
        String params="username=test&password=mypass";
        String type="post";
        String charset="utf-8";

        String html=doPostsWithOwnCerSSL2(url,params,type,charset);
        System.out.println("html=" + html);

    }
}

7. https原理:https://www.cnblogs.com/dhcn/p/10824323.html

Https单向认证和双向认证

https://blog.csdn.net/duanbokan/article/details/50847612 

一、Http
HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准。HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全。

使用TCP端口为:80

二、Https
Hyper Text Transfer Protocol over Secure Socket Layer,安全的超文本传输协议,网景公式设计了SSL(Secure Sockets Layer)协议用于对Http协议传输的数据进行加密,保证会话过程中的安全性。

使用TCP端口默认为443

三、SSL协议加密方式
SSL协议即用到了对称加密也用到了非对称加密(公钥加密),在建立传输链路时,SSL首先对对称加密的密钥使用公钥进行非对称加密,链路建立好之后,SSL对传输内容使用对称加密。

对称加密
速度高,可加密内容较大,用来加密会话过程中的消息

公钥加密
加密速度较慢,但能提供更好的身份认证技术,用来加密对称加密的密钥

四、单向认证
Https在建立Socket连接之前,需要进行握手,具体过程如下:

 

客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
客户端使用服务端返回的信息验证服务器的合法性,包括:

证书是否过期
发型服务器证书的CA是否可靠
返回的公钥是否能正确解开返回证书中的数字签名
服务器证书上的域名是否和服务器的实际域名相匹配
验证通过后,将继续进行通信,否则,终止通信

客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
服务器端在客户端提供的加密方案中选择加密程度最高的加密方式。
服务器将选择好的加密方案通过明文方式返回给客户端
客户端接收到服务端返回的加密方式后,使用该加密方式生成产生随机码,用作通信过程中对称加密的密钥,使用服务端返回的公钥进行加密,将加密后的随机码发送至服务器
服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获取对称加密密钥。
在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。
五、双向认证
双向认证和单向认证原理基本差不多,只是除了客户端需要认证服务端以外,增加了服务端对客户端的认证,具体过程如下:

 

客户端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。
服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书
客户端使用服务端返回的信息验证服务器的合法性,包括:

证书是否过期
发型服务器证书的CA是否可靠
返回的公钥是否能正确解开返回证书中的数字签名
服务器证书上的域名是否和服务器的实际域名相匹配
验证通过后,将继续进行通信,否则,终止通信

服务端要求客户端发送客户端的证书,客户端会将自己的证书发送至服务端
验证客户端的证书,通过验证后,会获得客户端的公钥
客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择
服务器端在客户端提供的加密方案中选择加密程度最高的加密方式
将加密方案通过使用之前获取到的公钥进行加密,返回给客户端
客户端收到服务端返回的加密方案密文后,使用自己的私钥进行解密,获取具体加密方式,而后,产生该加密方式的随机码,用作加密过程中的密钥,使用之前从服务端证书中获取到的公钥进行加密后,发送给服务端
服务端收到客户端发送的消息后,使用自己的私钥进行解密,获取对称加密的密钥,在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。

 

8.FAQ:

    1.测试发现,选择证书后,浏览器提示证书无效(ERR_BAD_SSL_CLIENT_AUTH_CERT)。把jks在其他tomcat环境上测试,没问题。这种情况可能是jdk有关,换一个jdk版本试试。(linux + tomcat8 + jdk1.7.0_75 就出现这种情况,换成jdk8就好了)

    20191021: 这可能和jdk是否支持TLS v1.2有关。网上有方法修改java.security强制开启tlsv1.2方法,还有修改tomcat的catalina.sh开启tlsv1.2的方法。甚至还和应用程序有关?待确认。

 

9. stunnel 配置双向ssl认证注意事项: 20191028补充:

http://ju.outofmemory.cn/entry/284567

;fips = yes
client = no
sslVersion = all

chroot = /opt/stunnel/
setuid = root
setgid = root

pid = /temp/stunnel.pid
output = /log/stunnel.log

cert = /opt/stunnel/conf/103.140.pem
key = /opt/stunnel/conf/103.140.pem


[open]
accept = 8443
connect = 8080

verify = 3
CAfile = /opt/stunnel/conf/ca_root.pem
;CApath = /opt/stunnel/conf/ca/

这里注意,如果verify=2,则客户端使用ca_root.pem里的根证书签发的任意一个证书即可登陆。(这里有一个问题,ca_root.pem里配置的中间CA证书无效!原因未知。)

如果verify=3,则客户端只认ca_root.pem里配置的客户端证书(需要把客户端证书的cert base64编码放置到ca_root.pem里)。

10.其他,待补充。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值