2019年的人们如何生成HTTPS证书

由于配置相关的教程总是有年限限制,过期就不能用了,本教程至少保证2019.4.14还可用。
环境: centos,nginx,chrome
备注: 可以避免chrome的NET::ERR_CERT_COMMON_NAME_INVALID错误。

摘要

从HTTP升级到HTTPS: 用openssl命令创建本地的CA,然后自签证书,然后配置到nginx中,最后信任一下本地CA即可。

需求

从HTTP协议升级到HTTPS。
用HTTPS可以防止会话内容被拦截解析,同时防止中间人攻击,防止他人伪装称你的网站,欺骗你的客户。
SSL协议的详细含义可以参见:
https://xiaoyue26.github.io/2018/09/26/2018-09/%E9%80%9A%E4%BF%97%E7%90%86%E8%A7%A3SSL-TLS%E5%8D%8F%E8%AE%AE%E5%8C%BA%E5%88%AB%E4%B8%8E%E5%8E%9F%E7%90%86/

现有架构

请求=>nginx服务器=>后端网站服务

改造思路/原理

原理上只需要让nginx负责SSL协议的部分即可,不需要动后端网站服务。
客户端发送HTTPS请求到nginx服务器,nginx服务器转发HTTP请求到后端网站服务。
(封装的思想,上层变动对底层HTTP服务透明)
HTTPS中断、TLS中断:
nginx直接负责搞定ssl部分,netty等后端服务只需要负责http部分就好了。
如果依赖nginx的话,netty的SslHandler什么的都可以废掉了XD

所以我们只需要关心架构中的前半部分:
请求=>nginx服务器

再分解一下这部分的话:
用户=>浏览器(chrome)==https请求=>nginx服务器

整个架构中我们需要修改的部分:

  1. nginx配置.

是的,就这么一项。所以改造成本很低。
当然了,如果不想花钱买官方CA证书的话,也就是自己弄一个CA, 然后给自己的网站颁发证书的话,还需要改动用户浏览器的信任CA,那么需要修改的部分就增加一项了:

  1. nginx配置;
  2. 用户浏览器信任CA。

这里的证书、CA是个啥概念呢?
证书: 就好比我们网站的身份证;
CA : 就好比派发身份证的派出所。
本质上是一个信任传递、担保的过程,用户浏览器会默认信任几个官方的CA,只要官方CA承认的网站,信任传递一下,用户就可以也信任了。
参见下图可以通过chrome右键”检查”的security面板查看证书的详细信息。

所以如果花钱让官方CA帮我们签发证书的话,用户可以直接默认信任我们的证书;
而如果我们自己弄的CA的话,好比自己开的黑作坊,用户不可能直接信任黑作坊签发的身份证的,就需要修改用户浏览器配置了,加入我们的私人CA证书。

公网HTTPS

生成数字证书

可以参考:
http://www.ruanyifeng.com/blog/2016/08/migrate-from-http-to-https.html

https://www.gogetssl.com/
https://www.ssls.com/
https://sslmate.com/
购买SSL证书。

免费的:
https://certbot.eff.org/
可以用这个工具,选择转发服务器和操作系统,生成证书:
https://certbot.eff.org/lets-encrypt

配置nginx

把原来nginx配置中的:

1
listen       80;

改成:

1
2
3
listen 443 ssl;
ssl_certificate /usr/local/nginx/ssl/private/server.crt;
ssl_certificate_key /usr/local/nginx/ssl/private/device.key;

这里的service.crt就是数字证书了。
如果要支持http和https同时可以访问,就把listen 80再加上。

如果要强制https,即使访问了http也强制跳转https(一般都需要这样搞),可以增加rewrite配置:

1
2
3
4
5
server {
listen 80;
server_name localhost;
rewrite ^(.*) https://$server_name$1 permanent;
}

局域网HTTPS

公网https起码要买个域名,买个服务器(阿里云),如果只是局域网玩玩、或者自签证书,可以如下操作:

  1. 本地生成一个CA;
  2. 用这个CA给自己网站的数字证书签名,生成网站数字证书;
  3. 修改nginx配置;
  4. 配置用户chrome信任第一步中的CA。

可以看出多了1,2两步来生成证书,代替购买证书;
多了第4步来强制用户信任非官方CA.

1. 本地生成CA

找个干净的目录开始操作:

1
2
openssl genrsa -des3 -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem

里面可以填下密码,email地址和ca的名字。其他的可以留空。

第一条命令: 生成本地CA的密钥rootCA.key(要记住你设置的密码,比如我的是staythenight);
第二条命令: 用这个密钥进行签名,生成一张CA的证书rootCA.pem.
(这里设置的过期时间为1024天).

2. 生成网站数字证书(用这个CA给自己网站的数字证书签名)

为了避免chrome的NET::ERR_CERT_COMMON_NAME_INVALID错误,需要在网站证书里填一些额外的信息。
首先创建文件server.csr.cnf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=US
ST=RandomState
L=RandomCity
O=RandomOrganization
OU=RandomOrganizationUnit
emailAddress=296671657@qq.com # 修改成自己的email
CN = kandiandata.oa.com # 修改成自己的域名

然后创建文件v3.ext:

1
2
3
4
5
6
7
8
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = kandiandata.oa.com # 修改成自己的域名
DNS.2 = localhost

创建证书:

1
2
3
4
5
6
openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout device.key -config server.csr.cnf

openssl x509 -req -in server.csr \
-CA rootCA.pem \
-CAkey rootCA.key \
-CAcreateserial -out server.crt -days 1800 -sha256 -extfile v3.ext

第一条命令: 用server.csr.cnf配置生成网站证书server.csr,同时生成网站私钥device.key(给nginx用的)。
第二条命令: 用CA私钥rootCA.key以CA的名义(rootCA.pem)️给网站证书签名,生成CA签名后的证书server.crt,同时加上v3.ext中的配置(防止chrome报错)。

到这里我们就准备好了下一步nginx要用到的两个文件:

1
2
server.crt
device.key

server.crt: 网站的数字证书;
device.key: 网站的私钥,用来解开用户发过来的通信密码。详细原理参见:
http://xiaoyue26.github.io/2018/09/26/2018-09/%E9%80%9A%E4%BF%97%E7%90%86%E8%A7%A3SSL-TLS%E5%8D%8F%E8%AE%AE%E5%8C%BA%E5%88%AB%E4%B8%8E%E5%8E%9F%E7%90%86/

3. 配置nginx

这里和之前的一样,打开ssl支持,监听443:

1
2
3
listen 443 ssl;
ssl_certificate /usr/local/nginx/ssl/private/server.crt;
ssl_certificate_key /usr/local/nginx/ssl/private/device.key;

加上监听80+重定向:

1
2
3
4
5
server {
listen 80;
server_name localhost;
rewrite ^(.*) https://$server_name$1 permanent;
}

4. 配置用户chrome信任第一步中的CA

将第一步中的rootCA.pem发送给用户,让它安装即可。
(千万不要发错了。)
如果是mac系统,可以直接双击安装到钥匙串中:

在钥匙串中选择系统=>证书,然后完全信任ca的证书即可:

最后得到chrome的承认:

其他

还可以查看openssl支持的ssl/tls版本:

1
openssl ciphers -v | awk '{print $2}' | sort | uniq

查看本地的443端口是否支持tls1.2协商:

1
openssl s_client -connect localhost:443 -tls1_2

成功的话会返回一大段内容,包括:

1
2
3
4
5
6
7
8
# 前面一大堆
---
Certificate chain
0 s:/C=US/ST=RandomState/L=RandomCity/O=RandomOrganization/OU=RandomOrganizationUnit/emailAddress=296671657@qq.com/CN=kandiandata.oa.com
i:/C=XX/L=Default City/O=tencent/OU=kandian/CN=pipe_ca/emailAddress=296671657@qq.com
---
Server certificate
# 后面一大堆

失败的话:

1
2
3
4
5
---
no peer certificate available
---
No client certificate CA names sent
---

推荐文章