Nginx双向证书认证

源起

服务器已经跑起来很久了,但是有需求要限制客户端使用我们服务,一同事说到了SSL双向认证的问题,之前没有玩过,所以查了一些资料。后来想了下,以前其实也碰到过,就是银行和支付宝的证书认证嘛,通过证书对帐号进行认证。还有一些登录的时候不填帐号,只填密码,其实就是在客户证书来当用户名嘛。这个在Nginx双向认证的时候就可以解决,Nginx收到证书信息,然后反向代理到后面服务的时候,可以在头信息里面加一点东西,就可以解决客户登录的时候的帐号填写问题了。当然还可以又填用户名,又使用证书,然后核对证书的用户名和用户填的用户名是否一致,不致的情况下,可以直接吊销证书等等,可以做的事情就很多了。简单记下之前配置的过程吧。

准备工作

可以编辑openssl.cnf,设置一部分默认值,比如国别、组织、机构啊什么的。CentOS在/etc/pki/tls目录下面,Gentoo在/etc/ssl目录下面。因为服务是CentOS,下面默认是在CentOS下操作。如果非root用户操作,做部分修改就行。

1
2
3
4
5
cd /etc/pki/CA
# 好像是证书db
touch index.txt
# 证书序号,每生成一个证书自增1
echo '00' > serial

生成CA密钥和证书

自已充当CA认证机构(一般为第三方权威机构),需要生成一个CA的密钥和证书,用于对其它用户的证书请求进行认证。

1
2
openssl genrsa -des3 -out ca-key.pem 4096
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem

生成自签名服务器证书(非必需)

如果可以,最好去第三方权威机构申请证书(非WoSign),或用letsencrypt的证书。实在不行,就像我们这样,自已生成证书算球的,就可以走下面的流程了。

1
2
3
4
5
6
7
openssl genrsa -des3 -out server-key.pem 1024
#生成无密码的私钥,这样nginx加载的时候不会提示输入密码的地方卡住。或者直接上面那句不加-des3选项
openssl rsa -in server-key.pem -out server-key.key
#生成csr签名申请,用于CA对证书进行签名。重要:其中提示输入的CN common name必须填服务器的域名、泛域名或IP地址。
openssl req -new -key server-key.pem -out server-req.csr
#用CA对服务器证书进行签名
openssl ca -policy policy_anything -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -in server-req.csr -out server-cert.pem

生成客户证书

与生成服务器证书基本一致,对于CA而言,是一样的。申请过来,进行认证。

1
2
3
4
5
6
7
8
openssl genrsa -des3 -out client-key.pem 1024
#生成csr签名申请,用于CA对证书进行签名。
openssl req -new -key client-key.pem -out client-req.csr
#用CA对证书进行签名
openssl ca -policy policy_anything -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -in client-req.csr -out client-cert.pem
#客户端证书转换为PKCS #12格式,客户端浏览器导入即可
#openssl pkcs12 -export -out client.pfx -inkey client-key.pem -in client-cert.pem -certfile ca-cert.pem
openssl pkcs12 -export -clcerts -in client-cert.pem -inkey client-key.pem -out client.p12

Nginx配置

/etc/nginx/conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name test.test;

ssl on;
# 可使用其它机构颁发的证书
ssl_certificate /etc/nginx/ssl/server-cert.pem;
# 服务器私钥
ssl_certificate_key /etc/nginx/ssl/server-key.key;

# 客户端证书认证
ssl_client_certificate /etc/nginx/ssl/ca-cert.pem;
# optional表示无证书可访问,用于无证书情况下的提示信息。
ssl_verify_client optional;

ssl_session_cache shared:SSL:5m;
ssl_session_timeout 1h;

ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;

client_max_body_size 10M;

# 客户端证书认证失败显示400信息
if ($ssl_client_verify != "SUCCESS") {
return 400;
}
......

参考