正如你所访问的这个网站,你会发现 Chrome(或者你用的其他浏览器),一般情况下(只要不是版本太老)都会在网站的 URL 前面加上绿色标志,表示你访问的网站是真的我部署的,而不是受到恶意拦截或者 DNS 污染等而伪造的假网站。一般来说,普通的网友想要拥有一个被信任的 HTTPS 网站,一般都是需要向 SSL 证书提供商处认证然后获得被信任的证书,这里之所以能被信任,是因为你电脑中的浏览器内置了很多根证书,通过这个根证书首先能证明这些提供商是正版的,当你的浏览器确认这些提供商是正版的了之后,再帮我的网站证明我的网站是正版的,从而告诉你这个正在使用浏览器的人,你看的网站是真的。
说起来有点绕,但是,这里涉及到几个在 TLS 中很重要的几个概念,理解了这几个概念对你阅读这篇文章有很大的帮助:
- TLS:其实和 SSL 是同一个东西,是建立在传输层之上的保证应用层数据安全性的协议,SSL 属于旧版本,到 3.0 版本之后就被 TLS 1.0 替代了;
- 非对称加密:加密和解密用的不是同一个密码的加密方式。平时你解锁手机输入的密码和你设置的时候输入的密码是一样的,这叫做对称加密;但是,在计算机中,有一类算法可以让你设置的密码和解密的密码不一样,也可以正确得加解密,这就是所谓的非对称加密;
- TLS 就是典型的非对称加密的应用,服务器通过服务器的密码加密,然后你通过广而告之的服务器的解密密码解析服务器加密后的数据,然后实现秘密得通信;
- 因为服务器的解密密码是广而告之的,所以可能会有人告诉你个假的密码,然后再通过假的服务器发送给你假的数据,因为这假的是成对的,你能正确解密,会让你误以为你拿的的真的;
- 要想拿到真的服务器的解密密码,那么就需要内置在你浏览器上的根证书帮你确认你拿到的服务器的解密密码是不是真的,如果不是真的,你的浏览器会警告你。
听上去有点绕,但是简单来讲就差不多是这么回事,因为今天不想讲太多理论上的东西,等周末闲下来再写一篇画画图,和有兴趣的同学聊一聊,今天要将的是实践,如何自己构造一对安全可靠的 SSL 证书,并且在自己的测试环境跑起来。先预先说一下,这里是以 CentOS 下的 Nginx 为例进行的:
第一步:安装Nginx和OpenSSL
$ yum install nginx openssl -y
因为后面会产生比较多的文件,所以为了能更好得管理这些文件,有必要新建一个目录,所以:
$ mkdir /home/liqiang.io
$ cd /home/liqiang.io
$ mkdir certs private config server
这里解释一下每个目录的作用:
- certs:这个是用于生成认证文件的目录,这里认证文件可以选择分发给最终用户,导入浏览器;也可以选择发送证书授权商,帮你认证证书;
- private:这里存放的是 CA 的私钥【不可泄露】
- config:我用来放配置的目录,其实就一个
- server:用来放服务端的证书文件的目录,给 Nginx 用的【不可泄露】
第二步:创建 openssl 配置文件
- conf 目录新建 openssl.conf 文件
里面的内容就写这些:
```
[ ca ]
default_ca = foo # The default ca section
[ foo ]
dir = ./ # top dir
database = ./index.txt # index file.
new_certs_dir = ./newcerts # new certs dir
certificate = ./private/ca.crt # The CA cert
serial = ./serial # serial no file
private_key = ./private/ca.key # CA private key
RANDFILE = ./private/.rand # random number file
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = sha256 # message digest method to use
unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
policy = policy_any # default policy
[ policy_any ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
localityName = optional
commonName = supplied
emailAddress = optional
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = CN
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = GD
localityName = Locality Name (eg, city)
localityName_default = Shenzhen
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Domain Control Validated
commonName = 192.168.57.16
commonName_max = 64
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:TRUE # 如果生成 CA 要改成 TRUE,server 证书要改成 FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = 192.168.57.16
IP.1 = 192.168.1.1
IP.2 = 192.168.57.16
```
第三步:创建根证书
前面说过了,因为是非对称加密,所以会有公钥私钥一说,公钥是会给别人的,而私钥是证明你自己的存在,必须收藏好。其实根证书也是一种服务证书,所以你会发现步骤和后面的服务器证书步骤差不多:
3.1 生成私钥 key 文件
$ openssl genrsa -out private/ca.key 2048
3.2 生成证书请求 csr 文件
$ openssl req -new -key private/ca.key -out private/ca.csr -config "./config/openssl.conf"
3.3 生成凭证 crt 文件
$ openssl x509 -req -days 365 -in private/ca.csr -signkey private/ca.key -out private/ca.crt -extensions v3_req -extfile "./config/openssl.conf"
3.4 为 key 设置起始序列号和创建 CA 键库
$ echo RAND > serial
$ touch index.txt
$ touch index.txt.attr
3.5 创建证书撤销列表
$ openssl ca -gencrl -out ./private/ca.crl -crldays 7 -config "./config/openssl.conf"
整套连起来就是这样的:
第四步:服务器证书的生成
当有了根证书之后,我们就可以来设置一个服务器的证书了,其实,如果要从简的话,直接将根证书作为服务器证书自验也行,不过,出于一个好习惯,我还是推荐根证书和服务器证书分开好一些,这也是工业的一个好实践。其实刚刚说了,这个步骤和创建根证书的步骤差不多,所以也就不多赘述了,直接走起:
$ openssl genrsa -out server/server.key 2048
$ openssl req -new -key server/server.key -out server/server.csr -config "./config/openssl.conf"
# 不同的地方:使用私有的 CA key 为 key 签名
$ openssl x509 -req -sha256 -extensions v3_req -extfile "./config/openssl.conf" -CA private/ca.crt -CAkey private/ca.key -CAcreateserial -in server/server.csr -out server/server.crt
执行的效果图差不多是这样的:
第五步:Nginx 配置 SSL 证书
到这一步的时候其实你在目录中已经生成好了需要的经根证书认证过的 SSL 证书了,下一步是时候上环境了,其实 Nginx 的配置也是极其简单的,我这里上一个配置看看:
你可以将这个配置添加到你的 Nginx 中,然后记得 reload 一下 Nginx:
$ nginx -t reload
这个时候打开浏览器查看一下,我这里的情况是这样的:
这是很明显的呀,因为你的证书使用自己的根证书签名认证的,而浏览器默认是不认你的根证书的,所以肯定是不安全的。那么可以怎么让他信任呢?最简单的方式就是将文件 private/ca.crt
导入到浏览器中,这样你就让你的浏览器信任你自己的根证书了,这其实也是很多企业自己签证书的常用操作,很多内网服务都是通过这种方式进行的,不用买外部的服务,省钱。。。
我这个版本的 Chrome 是这么设置的:
添加根证书之后,世界一切都是那么美好,只是绿的不够。
第六步:客户端证书的生成
如果你觉得只验证服务器不够,同时对访客也要进行一波验证,那么没问题,你也可以要求客户端提供自己的证书,然后服务端进行验证;当然,首先还是要有客户端的证书对吧,还是通过自己的根证书签发:
6.1 创建存放 key 的目录 users
$ mkdir client
6.2 创建一个客户端 key
$ openssl genrsa -des3 -out ./client/client.key 2048
- 要密码?随便输,不过得记住
6.3 为客户端 key 创建一个证书签名请求文件(csr)
$ openssl req -new -key ./client/client.key -out ./client/client.csr
6.4 使用私有的 CA key 为客户端 key 签名
$ openssl ca -in ./client/client.csr -cert ./private/ca.crt -keyfile ./private/ca.key -out ./client/client.crt -config "./config/openssl.conf"
- 将证书转换为大多数浏览器都能识别的 PKCS12 文件
$ openssl pkcs12 -export -clcerts -in ./client/client.crt -inkey ./client/client.key -out ./client/client.p12
这前 4 部分的执行情况大概是这样的:
6.5 修改 Nginx 配置如下:
然后你重启一下 nginx 再访问看看:
嘿嘿预想不到吧。这是因为 https 双向验证需要客户端安装证书,而你此时默认是没有客户端的证书的,这里也提示得很明显了,所以下面需要将客户端证书添加进去。
6.6 添加客户端证书
拿到生成的客户端证书 client.p12,直接双击它,进入 “证书导入向导”:导入证书,直接双击下一步,下一步,输入密码,添加成功。同时 MAC 下需要手动添加为信任钥匙串,并使用safari打开测试
然后再次打开浏览器,你就会看到这个了:
然后确定共用这个之后,Mac 处于安全会要求你输入密码:
当输入密码之后,又是刚才的页面了,万事大吉:
总结
本文对创建自签名的 SSL 证书进行一个可实践的指导,并且给出了服务器和客户端双向认证的方案,并且是可以保证你安全认证的,希望对有需要的同学有所帮助。同时,最近我看到有几个网站在做一些抄袭的事情,甚至于连本文的原地址都去掉了,不知道这些网站意欲何为?本博客的文章,除了标注了转载和翻译之外,其他所有文章均为本人在工作业余时间完成,大部分都是熬夜写的,希望能够尊重个人劳动成果。同时,我也对那些于我学习这些知识有帮助的文章表示感谢,并在下方贴回引用。