原理

稍微懂点HTTPS原理的都知道,TLS提供机密性、完整性、真实性、身份验证…

不管是双sp里还是平时的实践中,耳熟能详的就是机密性,用非对称协商会话秘钥,在用会话秘钥加密会话

那么真实性和身份验证体现在哪?

身份验证,也就是认证,也就是验证你就是你,这里就要看是单向还是双向了

如果是单向,那默认指客户端验证服务器,也就是我们安全设计里面的最低要求,有公网的就需要使用HTTPS,证书绑定了域名的话,用IP访问会弹框【不安全】,必须信任才能继续,或者使用非授权/信任CA签发的服务器证书,也是会弹框【不安全】,这个弹框【不安全】,是专门指客户端验证服务器的过程

如果是双向,则是在单向也就是客户端验证服务器的基础上,加上服务器验证客户端。为什么要这么做?可以试想一下,如果我们的系统只想让指定客户访问,那么我们又没有Ukey,就很适合这种方式,另一种白名单嘛

附图:单向SSL中,非授权/信任CA签发的证书,或者授权/信任CA签发的证书,但是证书绑定域名,非要用IP访问,必须点高级,信任站点,才能访问,这个就相当于手动信任/认证

charles 双向证书认证 双向ssl证书 原理_charles 双向证书认证

顺便说一下SSL和TLS的关系吧

先有的SSL,然后慢慢迭代TLS取代了SSL,在SSL 3.0之后,出现了TLS 1.0,TLS 1.0所以可以理解为SSL 3.1,不过目前安全的TLS版本是1.2和1.3,或许只是习惯,我们才会经常说双向SSL、SSL VPN…

nginx

单向SSL没什么好说的,我们主要就是要说双向SSL

单向SSL所需要的就只是服务器的证书,双向SSL涉及到的就是CA证书和客户端证书,并校验客户端证书,一定要把这两者分开,明白原理,我们就来实现

参考

https://www.jianshu.com/p/59ecf0120048

在安装openssl的linux下执行CA证书、服务器证书、客户端证书的生成,如果比较熟练了,可以直接把命令汇总章节中,生成CA、客户端、服务器证书的命令直接合起来写成个shell,一键生成

生成自签CA证书

生成CA的私钥文件

openssl genrsa -out ca.key 2048

生成CA证书,-subj参数可以免去交互式

openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=CN/O=People's Republic of China/CN=China CA"

通过openssl工具查看CA证书的内容

openssl x509 -text -noout -in ca.crt

生成自签服务器证书

-subj 参数很重要,其中/CN要填写具体的域名,要与nginx的server_name匹配上。

chrome浏览器对证书很严格,证书必须要有SAN(Subject Alternative Name)。
v3_req表示生成是X.509 v3和SAN的证书,-extfile和-extensions这两个参数是可选的,如果没有这两个参数,那证书就是没有SAN的,推荐加上这两个参数。

生成服务器的私钥文件

openssl genrsa -out server.key 2048

生成服务器的证书签发文件,即csr后缀的文件

openssl req -new -key server.key -out server.csr -subj "/C=CN/O=People's Republic of China/CN=example.com"

使用CA签发服务器证书,这里要注意一下,看看openssl的安装路径,我的配置就在这里/etc/pki/tls/openssl.cnf,不知道在哪的也可以find搜一下

openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extfile <(sed "/\[ v3_req \]/ a\subjectAltName = @alt_names" /etc/pki/tls/openssl.cnf <(printf "\n[alt_names]\nDNS.1=example.com\nDNS.2=www.example.com")) -extensions v3_req

通过openssl工具查看服务器证书的内容

openssl x509 -text -noout -in server.crt

生成客户端证书

客户端证书步骤跟服务器证书一样,只是参数不一样。

生成客户端的私钥文件

openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/C=CN/O=People's Republic of China/CN=Private certificate assigned to Tom"

使用CA签发客户端证书

openssl x509 -req -in client.csr -out client.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650

导出p12格式的

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

温馨提示:不需要密码就按回车键。

通过openssl工具查看客户端证书的内容

openssl x509 -text -noout -in client.crt

至此CA、服务器、客户端三部分的证书就已经生成了

charles 双向证书认证 双向ssl证书 原理_客户端_02

配置nginx,开启ssl双向认证

nginx用linux的也行,windows的也行,只要linux里面有关键模块【–with-http_ssl_module】和就好了,由于windows下nginx/openresty是自带所有模块的,我就在windows演示了

charles 双向证书认证 双向ssl证书 原理_网络安全_03

7443单向、8443双向

双向里面一定注意这个字段ssl_trusted_certificate,没有这个的话也还是会报错,我是一直都加着

server {
		listen 7443 ssl;
		listen [::]:7443 ssl;
        server_name  example.com www.example.com;

        ssl_certificate E:\\tools\pki\server.crt;
        ssl_certificate_key E:\\tools\pki\server.key;
		
		location / {
			root html;
			index index.html index.htm;
		}
	}
	
	server {
		listen 8443 ssl;
		listen [::]:8443 ssl;
        server_name  example.com www.example.com;

        ssl_certificate E:\\tools\pki\server.crt;
        ssl_certificate_key E:\\tools\pki\server.key;

        ssl_verify_client       on;
        ssl_trusted_certificate E:\\tools\pki\ca.crt;
        ssl_client_certificate E:\\tools\pki\ca.crt;

		location / {
			root html;
			index index.html index.htm;
		}
	}

使用curl测试ssl双向认证

curl没有使用客户端证书去请求,会报400错误。如果使用的客户端证书不是由特定CA签发的,也同样会报400错误。

用不带证书的,会报400

curl -k -vo /dev/null https://www.example.com/ --resolve www.example.com:443:127.0.0.1

用带证书的

curl -k -vo /dev/null https://www.example.com/ --resolve www.example.com:443:127.0.0.1 --cert ./client.crt --key ./client.key

使用浏览器测试ssl双向认证

启动nginx之后,只要日志里面没有报错,就可以开始愉快的验证了

单向

先看7443的单向,上面也说了单向只是客户端验证服务器,而服务器的证书是我自己颁发的,那显然会弹框,所以这需要我们手动去信任,

charles 双向证书认证 双向ssl证书 原理_ssl_04

会显示是不安全的HTTPS,当然算法、TLS版本我们都可以在配置里指定

charles 双向证书认证 双向ssl证书 原理_HTTPS_05

双向

双向主要的功能是服务器验证客户端,这类场景适合:

1、有明确的白名单要求

2、IP白名单无法满足,比如IP是变的,或者是一个很大的范围,这样防火墙无法配置,通过服务器验证客户端这个功能可以完美替代IP白名单

采用双向认证后,会生成配对的根证书和客户端证书,根证书刚刚配置在nginx服务器内,客户端证书就发给需要访问这个web的客户了,所以客户端证书如何发,带外等等是需要根据具体业务情况而定的

如果没有客户端证书,是这样的

charles 双向证书认证 双向ssl证书 原理_charles 双向证书认证_06

双击安装我们导出的p12文件

charles 双向证书认证 双向ssl证书 原理_客户端_07

如果刚刚在生成证书的时候没有设置密码,就一路下一步

charles 双向证书认证 双向ssl证书 原理_网络安全_08

charles 双向证书认证 双向ssl证书 原理_ssl_09

如果设置密码了,这里必须得输入,不然过不去

charles 双向证书认证 双向ssl证书 原理_HTTPS_10

charles 双向证书认证 双向ssl证书 原理_网络安全_11

charles 双向证书认证 双向ssl证书 原理_charles 双向证书认证_12

最后出现了导入成功

charles 双向证书认证 双向ssl证书 原理_ssl_13

关闭刚刚的浏览器(为了清理缓存,我都启动的是无痕)

查看浏览器里面的客户端证书,可以看到刚刚安装的,注意这种双击安装是全局的,每个浏览器都能看到

charles 双向证书认证 双向ssl证书 原理_网络安全_14

再次访问就会提示选择证书,选择后,就能正常访问了

charles 双向证书认证 双向ssl证书 原理_ssl_15

命令汇总

生成自签CA证书

openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=CN/O=People's Republic of China/CN=China CA"

生成服务器证书

openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/C=CN/O=People's Republic of China/CN=example.com"
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extfile <(sed "/\[ v3_req \]/ a\subjectAltName = @alt_names" /etc/ssl/openssl.cnf <(printf "\n[alt_names]\nDNS.1=example.com\nDNS.2=www.example.com")) -extensions v3_req

生成客户端证书

openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/C=CN/O=People's Republic of China/CN=Private certificate assigned to Tom"
openssl x509 -req -in client.csr -out client.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650

curl命令

curl -k -vo /dev/null https://www.example.com/ --resolve www.example.com:443:127.0.0.1
curl -k https://www.example.com/ --resolve www.example.com:443:127.0.0.1
curl -k -vo /dev/null https://www.example.com/ --resolve www.example.com:443:127.0.0.1 --cert ./client.crt --key ./client.key

tomcat

要使用keytool进行证书的签发

如果只是测试的话,不用加servelet,配好server.xml访问就行