We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在很久之前,我在 划一划 HTTPS 以及 SSL/TLS 的重要知识点 提到过客户端 HTTPS 证书,之后就没后续了,不过目前还是遇到了接口问题,不得不用上了。
接下来会给大家说清几个概念:HTTPS 单向认证、HTTPS 双向认证、中间人攻击。
在这两种认证场景中,主要的区别在于服务器是不是需要客户端提供证书验证,我们,在进行 TCP 三次握手成功后,就开始进行 SSL 阶段的握手了了。
SSL 握手阶段可以参考如下,此图盗自 Ssl handshake with two way authentication with certificates 。这张图里面的大致阶段是对的,只是并不是很详细,更详细的请参考 Transport Layer Security 。
正如如上图所示,这个过程中,主要区别就在于第三阶段1:
如果把第三阶段省略掉,那就变成单向认证了,所以担心加密过程开销的同学们可以稍稍省点心了,因为整个过程中,就是 SSL 握手阶段的开销会略微加大,而实际通信过程中的开销还是与单向认证一样。
所谓中间人攻击,就是在双方握手阶段,由于没有用 CA 对证书进行验证造成的问题:比如你如果不在意浏览器的警告,或者被骗安装了攻击者提供的 CA 证书。攻击者可以在你与服务器建立 SSL 通信的时候,先用假的证书与你建立 SSL 通信,然后再与服务器建立 SSL 通信,这个过程中,攻击者就能拿到这个过程中所有的明文消息。
用一个非常简单的例子来说明,就是 HTTPS 抓包,比如现在你需要抓手机上的 HTTPS 请求,那么你就需要在手机上安装抓包工具提供的 CA 证书,然后配置好代理后,就可以抓到 HTTPS 的明文请求了。
到目前,我们知道了 HTTPS 双向认证是怎么一回事,那么它能解决我们的什么问题呢?防止有人冒充我们的客户端来请求服务器的私有资源,那么我们可以直接去验证客户端的身份,就能在很大程度上解决问题。
下面自问自答,来帮大家理清楚思路:
相信这样的场景很常见,为了保护接口,直接跟客户端约定一个密钥,然后大家根据这个密钥来进行加密通信,这种方案在我看来很容易破解,而且一旦破解拿到密钥,你很难短时间里面通过更新客户端来更换密钥。
这种方式,做的好的能够使用公开经得起验证的加密算法,而做的差的会用私有加密算法(多个公开算法叠加套用不算私有)。这在一定程度上能够阻挡破解者,只是私有加密算法出问题的概率在遇到高价值资源的时候,就会变得非常大,简而言之,就如闭源与开源一般。
另外,还有个很尴尬的地方在于,在开发人员进行接口调试的时候,一大段密文信息将会让调试非常困难。于是,所谓的的防止破解,更多的时候都在恶心开发者自己了。
用 HTTPS 证书就不会有这个问题了,在自己电脑安装完成证书后,就能在浏览器中明文调试了。
理论上,跟 HTTPS 单向的认证区别就在于多了一步验证客户端证书的过程,因此会多出 SSL 握手时的开销,具体我还没有压力测试过,但是我相信玩过 kubernetes 的应该会对此有个概念。
如果你预计会有比较大的流量,不放心,那就建议先找几台机器进行测试再决定也不迟。
确实如此,如果客户端会流落在他人手里,那么在不加一层保护的前提下,确实有可能会泄露,只是你有两种方案可以一起使用来保护证书:
strings
我的理由主要有两点:
大多数没有选择客户端证书的,我认为是因为对它不够了解,或者说它的强大显得过于简单,导致很多人都没有什么安全感,尤其是领导没有什么安全感,但是请大家不妨想想现在有谁能够简单破解 HTTPS 请求内容呢?基于 RSA 加密的通信,是我们现代互联网的安全基础,没有谁能够轻易破解。
目前大部分方案都是拿 OpenSSL + Nginx 来举例的(随便搜索都是一大堆),那我就把 OpenSSL 换成更简单好用的 CFSSL 。
多种方式可选,比如最简单的就是直接去下载已经编译好的工具:cfssl/releases 。
我目前使用的是 v1.5.0 版本的,大版本前提下,命令应该不会相差太多。
生成 CA 默认配置:
cfssl print-defaults config > ca-config.json
这里面的内容需要略加修改,下面给出一个我修改的例子,因为今天只需要客户端证书,因此 profiles 里面只留下了 client,signing.default.expiry 表示签发证书的默认过期时间,时间比较短,而另一个 signing.profiles.client.expiry 则比较长,8760h 就表示一年了。
signing.default.expiry
signing.profiles.client.expiry
{ "signing": { "default": { "expiry": "168h" }, "profiles": { "client": { "expiry": "8760h", "usages": [ "signing", "key encipherment", "client auth" ] } } } }
然后是生成 CA 证书申请:
cfssl print-defaults csr > ca-csr.json
也需要根据自己的情况修改,这里需要注意下 key,v1.5.0 下面默认是 ecdsa-256 ,而我改成了 rsa-2048,只是为了说明方便,以及兼容性。
{ "CN": "Awesome Inc", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "Guangdong", "L": "Shenzhen", "O": "Awesome Inc", "OU": "Tech Dept" } ] }
然后就可以生成 CA 证书了:
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
在当前目录下,会生成三个文件:
接下来就开始生产客户端证书,与上面是类似的:
cfssl print-defaults csr > client.json
照例给出 client.json 的修改样例:
{ "CN": "xizhibei", "hosts": [""], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "Guangdong", "L": "Shenzhen", "O": "Awesome Inc", "OU": "Tech Dept" } ] }
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client
命令行中会出现如下的日志:
2021/02/03 15:21:09 [INFO] generate received request 2021/02/03 15:21:09 [INFO] received CSR 2021/02/03 15:21:09 [INFO] generating key: rsa-2048 2021/02/03 15:21:10 [INFO] encoded CSR 2021/02/03 15:21:10 [INFO] signed certificate with serial number 657128885698804019594922156238712961504332210277
然后生成了三个文件:
当证书发布出去后,在有效期之前,它是一直有效的,但是如果我们想让它提前失效(比如泄露的情况下),那么就需要用到撤销证书。
比如,我们要撤销上面刚刚生成的证书,需要先将序列号写入撤销证书序列号列表,然后来生成撤销证书:
echo 657128885698804019594922156238712961504332210277 >> crl-serials.txt cfssl gencrl crl-serials.txt ca.pem ca-key.pem| base64 -D | openssl crl -inform DER -out ca-crl.pem
然后替换已有的撤销证书即可。
好了,到目前为止,证书部分基本上就搞定了,相对 OpenSSL 来说是不是很简单?
将上面生成的 ca.pem 以及 ca-crl.pem 放在 /etc/nginx/certs/client_ca 中,然后在 server 中如下配置(这里假设你已经开启了 HTTPS,并且配置好了服务端的证书):
ca.pem
ca-crl.pem
/etc/nginx/certs/client_ca
ssl_client_certificate /etc/nginx/certs/client_ca/ca.pem; ssl_crl /etc/nginx/certs/client_ca/ca-crl.pem; ssl_verify_client on;
更新 Nginx:
nginx -s reload
这里留给你一个思考题,为什么这里只需要配置一个 CA 证书就可以了?
如果你在配置好 nginx 后,直接用浏览器请求网站,那么你就会看到一个 HTTP 400 错误,告诉你需要提供证书。
本地安装证书很简单,直接鼠标双击证书就能够安装,如果是 Mac,记得在 Keychain 中配置始终信任。
另外,为了方便发送证书给他人使用,建议打包成 pkcs12 格式的证书,还能设置密码。
pkcs12
openssl pkcs12 -export -clcerts \ -CApath . -inkey xizhibei-key.pem -in xizhibei.pem \ -certfile ca.pem -passout pass:strong-password -out xizhibei.p12
The text was updated successfully, but these errors were encountered:
@xizhibei
mac用户打包完p12证书后,安装一直提示密码输入错误,密码实际上并没有输入错误,请问这是怎么回事呢?通过openssl pkcs12 -info 命令查看证书信息也都正常
openssl pkcs12 -info
Sorry, something went wrong.
No branches or pull requests
在很久之前,我在 划一划 HTTPS 以及 SSL/TLS 的重要知识点 提到过客户端 HTTPS 证书,之后就没后续了,不过目前还是遇到了接口问题,不得不用上了。
简介
接下来会给大家说清几个概念:HTTPS 单向认证、HTTPS 双向认证、中间人攻击。
HTTPS 单向认证与双向认证
在这两种认证场景中,主要的区别在于服务器是不是需要客户端提供证书验证,我们,在进行 TCP 三次握手成功后,就开始进行 SSL 阶段的握手了了。
SSL 握手阶段可以参考如下,此图盗自 Ssl handshake with two way authentication with certificates 。这张图里面的大致阶段是对的,只是并不是很详细,更详细的请参考 Transport Layer Security 。
正如如上图所示,这个过程中,主要区别就在于第三阶段1:
如果把第三阶段省略掉,那就变成单向认证了,所以担心加密过程开销的同学们可以稍稍省点心了,因为整个过程中,就是 SSL 握手阶段的开销会略微加大,而实际通信过程中的开销还是与单向认证一样。
中间人攻击
所谓中间人攻击,就是在双方握手阶段,由于没有用 CA 对证书进行验证造成的问题:比如你如果不在意浏览器的警告,或者被骗安装了攻击者提供的 CA 证书。攻击者可以在你与服务器建立 SSL 通信的时候,先用假的证书与你建立 SSL 通信,然后再与服务器建立 SSL 通信,这个过程中,攻击者就能拿到这个过程中所有的明文消息。
用一个非常简单的例子来说明,就是 HTTPS 抓包,比如现在你需要抓手机上的 HTTPS 请求,那么你就需要在手机上安装抓包工具提供的 CA 证书,然后配置好代理后,就可以抓到 HTTPS 的明文请求了。
我们的问题
到目前,我们知道了 HTTPS 双向认证是怎么一回事,那么它能解决我们的什么问题呢?防止有人冒充我们的客户端来请求服务器的私有资源,那么我们可以直接去验证客户端的身份,就能在很大程度上解决问题。
下面自问自答,来帮大家理清楚思路:
为什么不用自己的方式来加密呢?
相信这样的场景很常见,为了保护接口,直接跟客户端约定一个密钥,然后大家根据这个密钥来进行加密通信,这种方案在我看来很容易破解,而且一旦破解拿到密钥,你很难短时间里面通过更新客户端来更换密钥。
这种方式,做的好的能够使用公开经得起验证的加密算法,而做的差的会用私有加密算法(多个公开算法叠加套用不算私有)。这在一定程度上能够阻挡破解者,只是私有加密算法出问题的概率在遇到高价值资源的时候,就会变得非常大,简而言之,就如闭源与开源一般。
另外,还有个很尴尬的地方在于,在开发人员进行接口调试的时候,一大段密文信息将会让调试非常困难。于是,所谓的的防止破解,更多的时候都在恶心开发者自己了。
用 HTTPS 证书就不会有这个问题了,在自己电脑安装完成证书后,就能在浏览器中明文调试了。
会给服务器造成很大压力吗?
理论上,跟 HTTPS 单向的认证区别就在于多了一步验证客户端证书的过程,因此会多出 SSL 握手时的开销,具体我还没有压力测试过,但是我相信玩过 kubernetes 的应该会对此有个概念。
如果你预计会有比较大的流量,不放心,那就建议先找几台机器进行测试再决定也不迟。
客户端证书不是也有泄露的风险吗?
确实如此,如果客户端会流落在他人手里,那么在不加一层保护的前提下,确实有可能会泄露,只是你有两种方案可以一起使用来保护证书:
strings
命令, 或者解压你的安装包就能获取到明文证书。另外,你也可以直接用 pkcs12 格式的证书,记得更换一个更强的密码;为何我对于客户端证书如此推崇?
我的理由主要有两点:
大多数没有选择客户端证书的,我认为是因为对它不够了解,或者说它的强大显得过于简单,导致很多人都没有什么安全感,尤其是领导没有什么安全感,但是请大家不妨想想现在有谁能够简单破解 HTTPS 请求内容呢?基于 RSA 加密的通信,是我们现代互联网的安全基础,没有谁能够轻易破解。
如何实现2、3
目前大部分方案都是拿 OpenSSL + Nginx 来举例的(随便搜索都是一大堆),那我就把 OpenSSL 换成更简单好用的 CFSSL 。
安装
多种方式可选,比如最简单的就是直接去下载已经编译好的工具:cfssl/releases 。
我目前使用的是 v1.5.0 版本的,大版本前提下,命令应该不会相差太多。
CA 证书
生成 CA 默认配置:
cfssl print-defaults config > ca-config.json
这里面的内容需要略加修改,下面给出一个我修改的例子,因为今天只需要客户端证书,因此 profiles 里面只留下了 client,
signing.default.expiry
表示签发证书的默认过期时间,时间比较短,而另一个signing.profiles.client.expiry
则比较长,8760h 就表示一年了。然后是生成 CA 证书申请:
cfssl print-defaults csr > ca-csr.json
也需要根据自己的情况修改,这里需要注意下 key,v1.5.0 下面默认是 ecdsa-256 ,而我改成了 rsa-2048,只是为了说明方便,以及兼容性。
然后就可以生成 CA 证书了:
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
在当前目录下,会生成三个文件:
客户端证书
接下来就开始生产客户端证书,与上面是类似的:
cfssl print-defaults csr > client.json
照例给出 client.json 的修改样例:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client
命令行中会出现如下的日志:
然后生成了三个文件:
撤销证书
当证书发布出去后,在有效期之前,它是一直有效的,但是如果我们想让它提前失效(比如泄露的情况下),那么就需要用到撤销证书。
比如,我们要撤销上面刚刚生成的证书,需要先将序列号写入撤销证书序列号列表,然后来生成撤销证书:
然后替换已有的撤销证书即可。
好了,到目前为止,证书部分基本上就搞定了,相对 OpenSSL 来说是不是很简单?
配置 Nginx
将上面生成的
ca.pem
以及ca-crl.pem
放在/etc/nginx/certs/client_ca
中,然后在 server 中如下配置(这里假设你已经开启了 HTTPS,并且配置好了服务端的证书):更新 Nginx:
这里留给你一个思考题,为什么这里只需要配置一个 CA 证书就可以了?
客户端证书的安装
如果你在配置好 nginx 后,直接用浏览器请求网站,那么你就会看到一个 HTTP 400 错误,告诉你需要提供证书。
本地安装证书很简单,直接鼠标双击证书就能够安装,如果是 Mac,记得在 Keychain 中配置始终信任。
另外,为了方便发送证书给他人使用,建议打包成
pkcs12
格式的证书,还能设置密码。openssl pkcs12 -export -clcerts \ -CApath . -inkey xizhibei-key.pem -in xizhibei.pem \ -certfile ca.pem -passout pass:strong-password -out xizhibei.p12
Ref
The text was updated successfully, but these errors were encountered: