HTTPS双向认证(Mutual TLS authentication)

双向认证,顾名思义,客户端和服务器端都需要验证对方的身份,在建立Https连接的过程中,握手的流程比单向认证多了几步。单向认证的过程,客户端从服务器端下载服务器端公钥证书进行验证,然后建立安全通信通道。双向通信流程,客户端除了需要从服务器端下载服务器的公钥证书进行验证外,还需要把客户端的公钥证书上传到服务器端给服务器端进行验证,等双方都认证通过了,才开始建立安全通信通道进行数据传输。

原理

单向认证流程中,服务器端保存着公钥证书和私钥两个文件,整个握手过程如下:

  1. 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务器端;
  2. 服务器端将本机的公钥证书(server.crt)发送给客户端;
  3. 客户端读取公钥证书(server.crt),取出了服务端公钥;
  4. 客户端生成一个随机数(密钥R),用刚才得到的服务器公钥去加密这个随机数形成密文,发送给服务端;
  5. 服务端用自己的私钥(server.key)去解密这个密文,得到了密钥R
  6. 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。

双向认证流程

  1. 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务端;
  2. 服务器端将本机的公钥证书(server.crt)发送给客户端;
  3. 客户端读取公钥证书(server.crt),取出了服务端公钥;
  4. 客户端将客户端公钥证书(client.crt)发送给服务器端;
  5. 服务器端使用根证书(root.crt)解密客户端公钥证书,拿到客户端公钥;
  6. 客户端发送自己支持的加密方案给服务器端;
  7. 服务器端根据自己和客户端的能力,选择一个双方都能接受的加密方案,使用客户端的公钥加密8. 后发送给客户端;
  8. 客户端使用自己的私钥解密加密方案,生成一个随机数R,使用服务器公钥加密后传给服务器端;
  9. 服务端用自己的私钥去解密这个密文,得到了密钥R
  10. 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。

证书准备

如果要把整个双向认证的流程跑通,最终需要六个证书文件:

  • 服务器端公钥证书:server.crt
  • 服务器端私钥文件:server.key
  • 根证书:root.crt
  • 客户端公钥证书:client.crt
  • 客户端私钥文件:client.key
  • 客户端集成证书(包括公钥和私钥,用于浏览器访问场景):client.p12

所有的这些证书,我们都可以向证书机构去申请签发,一般需要收取一定的证书签发费用,此时我们需要选择大型的证书机构去购买。如果只是企业内部使用,不是给公众使用,也可以自行颁发自签名证书,具体的颁发办法请参见本文第四章。

自签名证书

生成这一些列证书之前,我们需要先生成一个CA根证书,然后由这个CA根证书颁发服务器公钥证书和客户端公钥证书。为了验证根证书颁发与验证客户端证书这个逻辑,我们使用根证书生成两套不同的客户端证书,然后同时用两个客户端证书来发送请求,看服务器端是否都能识别。

生成自签名根证书

(1)创建根证书私钥:
openssl genrsa -out root.key 1024(2)创建根证书请求文件:
openssl req -new -out root.csr -key root.key
后续参数请自行填写,下面是一个例子:
Country Name (2 letter code) [XX]:cn
State or Province Name (full name) []:bj
Locality Name (eg, city) [Default City]:bj
Organization Name (eg, company) [Default Company Ltd]:alibaba
Organizational Unit Name (eg, section) []:test
Common Name (eg, your name or your servers hostname) []:root
Email Address []:a.alibaba.com
A challenge password []:
An optional company name []:(3)创建根证书:
openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 3650

在创建证书请求文件的时候需要注意三点,下面生成服务器请求文件和客户端请求文件均要注意这三点: 根证书的Common Name填写root就可以,所有客户端和服务器端的证书这个字段需要填写域名,一定要注意的是,根证书的这个字段和客户端证书、服务器端证书不能一样; 其他所有字段的填写,根证书、服务器端证书、客户端证书需保持一致 最后的密码可以直接回车跳过。

经过上面三个命令行,我们最终可以得到一个签名有效期为10年的根证书root.crt,后面我们可以用这个根证书去颁发服务器证书和客户端证书。

生成自签名服务器端证书

(1)生成服务器端证书私钥:
openssl genrsa -out server.key 1024(2) 生成服务器证书请求文件,过程和注意事项参考根证书,本节不详述:
openssl req -new -out server.csr -key server.key(3) 生成服务器端公钥证书
openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650

经过上面的三个命令,我们得到:

server.key:服务器端的秘钥文件 server.crt:有效期十年的服务器端公钥证书,使用根证书和服务器端私钥文件一起生成

生成自签名客户端证书

(1)生成客户端证书秘钥:
openssl genrsa -out client.key 1024
openssl genrsa -out client2.key 1024(2) 生成客户端证书请求文件,过程和注意事项参考根证书,本节不详述:
openssl req -new -out client.csr -key client.key
openssl req -new -out client2.csr -key client2.key(3) 生客户端证书
openssl x509 -req -in client.csr -out client.crt -signkey client.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650
openssl x509 -req -in client2.csr -out client2.crt -signkey client2.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650(4) 生客户端p12格式证书,需要输入一个密码,选一个好记的,比如123456
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
openssl pkcs12 -export -clcerts -in client2.crt -inkey client2.key -out client2.p12

重复使用上面的三个命令,我们得到两套客户端证书: client.key / client2.key: 客户端的私钥文件 client.crt / client2.key: 有效期十年的客户端证书,使用根证书和客户端私钥一起生成 client.p12/client2.p12:客户端p12格式,这个证书文件包含客户端的公钥和私钥,主要用来给浏览器访问使用

验证

使用curl加上证书路径,可以直接测试Nginx的HTTPS双向认证是否配置成功。下面我们测试三个用例:

使用client.crt / client.key这一套客户端证书来调用服务器端
使用client.crt2 / client2.key这一套客户端证书来调用服务器端
不使用证书来调用服务器端
下面是三个用例的测试结果:

带证书的成功调用

#--cert指定客户端公钥证书的路径
#--key指定客户端私钥文件的路径
#-k不校验证书的合法性,因为我们用的是自签名证书,所以需要加这个参数
#可以使用-v来观察具体的SSL握手过程curl --cert ./client.crt --key ./client.key https://integration-fred2.fredhuang.com -k -v
* Rebuilt URL to: https://47.93.245.203/
*   Trying 47.93.245.203...
* TCP_NODELAY set
* Connected to 47.93.245.203 (47.93.245.203) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pemCApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=CN; ST=BJ; L=BJ; O=Alibaba; OU=Test; CN=integration-fred2.fredhuang.com; emailAddress=a@alibaba.com
*  start date: Nov  2 01:01:34 2019 GMT
*  expire date: Oct 30 01:01:34 2029 GMT
*  issuer: C=CN; ST=BJ; L=BJ; O=Alibaba; OU=Test; CN=root; emailAddress=a@alibaba.com
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET / HTTP/1.1
> host:integration-fred2.fredhuang.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.17.5
< Date: Sat, 02 Nov 2019 02:39:43 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Wed, 30 Oct 2019 11:29:45 GMT
< Connection: keep-alive
< ETag: "5db97429-264"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 47.93.245.203 left intact

使用client2.crt / client2.key这一套客户端证书来调用服务器端

curl --cert ./client2.crt --key ./client2.key https://integration-fred2.fredhuang.com -k
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>

不带证书的调用

curl https://integration-fred2.fredhuang.com -k
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.17.5</center>
</body>
</html>

三个用例都符合预期,从第一个测试日志中,我们可以看到,整个通信过程较长,客户端验证服务器端的证书,客户端也将自己的证书上传到服务器端进行验证。使用根证书颁发的两个客户端证书都可以正常发起双向HTTPS认证的调用。没有带客户端证书的调用会被服务器端拒绝服务。

使用Java调用

由于使用的是自签名证书,使用ApacheHttpClient去调用的话,需要将服务器证书加入可信任证书库中,才能成功调用,也可以在代码中简单忽略证书。

cd $JAVA_HOME
sudo ./bin/keytool -import -alias ttt -keystore cacerts -file /Users/fred/temp/cert5/server.crt

将服务器端公钥证书设置为可信证书后,使用以下代码可以直接发起带客户端证书的HTTPS请求:

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;public class HttpClientWithClientCert {private final static String PFX_PATH = "/Users/fred/temp/cert5/client.p12";    //客户端证书路径private final static String PFX_PWD = "123456";    //客户端证书密码public static String sslRequestGet(String url) throws Exception {KeyStore keyStore = KeyStore.getInstance("PKCS12");InputStream instream = new FileInputStream(new File(PFX_PATH));try {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);//httpget.addHeader("host", "integration-fred2.fredhuang.com");// 设置一些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(System.getProperty("java.home"));System.out.println(sslRequestGet("https://integration-fred2.fredhuang.com"));}}

HTTPS双向认证(Mutual TLS authentication)相关推荐

  1. Per-service mutual TLS authentication enablement

    在这个 Installation guide,我们将展示如何在sidecars间启动 mutual TLS authentication .这个设置将会被网格中的所有sidecars应用. 在这个指导 ...

  2. android webview单向认证,android 让webview支持自签名证书https 双向认证(SSL)

    最近完成一个项目,安全级别比较高.所以涉及到https双向认证,在网上找了很多资料都没有完美的解决方案.最后参考了org.sandrob.sslexample的实现方式,结合实际情况才完成该技术难题, ...

  3. Apache httpd设置HTTPS双向认证

    一.环境 httpd: 2.4.4  openssl:1.0.1  os:ubuntu 12.04 LTS 二.场景 我准备在httpd上配置一个HTTPS双向认证,既向客户端表明自己的身份,也只允许 ...

  4. httpd设置HTTPS双向认证

    去年用tomcat.jboss配置过HTTPS双向认证,那时候主要用的是JDK自带的keytool工具.这次是用httpd + openssl,区别比较大 在网上搜索了很多文章,发现全面介绍的不多,或 ...

  5. 巧用 Nginx 快速实现 HTTPS 双向认证

    1.原理 双向认证,顾名思义,客户端和服务器端都需要验证对方的身份,在建立 HTTPS 连接的过程中,握手的流程比单向认证多了几步.单向认证的过程,客户端从服务器端下载服务器端公钥证书进行验证,然后建 ...

  6. tomcat实现https双向认证(在win10系统使用jdk1.8自带工具keytool)

    tomcat实现https双向认证(在win10系统使用jdk1.8自带工具keytool) 以下操作我在D:\shiro-cas-ssl进行操作 使用win+R运行certmgr.msc可以查看证书 ...

  7. Tomcat服务器配置https双向认证,使用JDK的keytool生成证书(适用于web、安卓、IOS)

    Tomcat服务器配置https双向认证,使用JDK的keytool生成证书(适用于web.安卓.IOS) 一.原理 1.HTTP:平时浏览网页时候使用的一种协议.HTTP协议传输的数据都是未加密的( ...

  8. (转载)Android 让WebView完美支持https双向认证(SSL)

    (转载)https://blog.csdn.net/kpioneer123/article/details/51491739 这是@happyzhang0502   关于webview https的建 ...

  9. https 双向认证开发实践

    https双向认证 证书如何使用 一.概念介绍 1.https协议介绍 与http协议的区别 https协议简单来说就是http协议的基础上增加了SSL协议 ,从而来保证数据传输的安全性. SSL协议 ...

最新文章

  1. lvs keepalived的tcp 长连接的问题解决办法
  2. Git 、CVS、SVN比较
  3. java mysql aio_Java中的NIO,BIO,AIO分别是什么
  4. aud$定位错误用户密码登陆数据库的具体信息
  5. infinity mysql_MySql锁机制
  6. MFC-使用自定义控件的方法
  7. 求字符串里面数字之和
  8. apache spark_Apache Spark软件包,从XML到JSON
  9. Delphi多媒体设计之TMediaPlayer组件(二)
  10. Python中super的用法
  11. Mongo 常用的server命令
  12. android 时钟动态图标,Android 8.1 Launcher3实现动态指针时钟功能
  13. 【图像配准】基于matlab OpenSUFT图像配准【含Matlab源码 1232期】
  14. centos tomcat部署
  15. 改图片分辨率怎么改?教你一招一分钟修改图片dpi
  16. 阿里云有奖调查结果公布,赠送10个阿里巴巴logo胸针...
  17. 达人评测 r33200g和i510400f选哪个好
  18. SqlServer2005中数据库角色成员身份
  19. 机械设计自动化行业现在吃香吗?有前途吗?
  20. AppKey和AppSecret的关系

热门文章

  1. Android 10.0 Launcher3 抽屉式(双层)app列表排序
  2. 关于下载淘宝教育的课程
  3. win11如何右键如果没有.txt文件,如何出现.txt文件
  4. 从人工智能角度考虑生命的本质和意义
  5. TokenInsight 对话首席——加密通证期货世界,究竟是投机还是金融工具?
  6. HTML5中常见的新语义化标签
  7. SPSSPRO模型归纳整理
  8. 拉杰尔安卓服务器注册上限,航海王启航409区圣地玛丽杰尔开服时间表_航海王启航新区开服预告_第一手游网手游开服表...
  9. Docker部署微服务应用笔记(三)
  10. 手风琴 html 左边,Layui手风琴左边的符号如何修改