一、背景
继上一篇企业级nginx实战,里面有一位访客留言说要我写一篇关于验证客服端证书的留言。我们通常配置的https一般使用的证书是单向验证,也就是说客户端单向验证服务器的安全性,但是服务器端是没有对客户端的身份进行验证的。Nginx的单向和双向验证都要http_ssl_module的支持,查看方法如下:
- nginx安装包
>./nginx -v nginx version: nginx/1.19.4 built by gcc 8.3.0 (Debian 8.3.0-6) built with OpenSSL 1.1.1d 10 Sep 2019 TLS SNI support enabled configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.19.4/debian/debuild-base/nginx-1.19.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'
- docker镜像版
[root@k8snode1 html]# docker exec -it nginx /bin/sh # nginx -V nginx version: nginx/1.19.4 built by gcc 8.3.0 (Debian 8.3.0-6) built with OpenSSL 1.1.1d 10 Sep 2019 TLS SNI support enabled configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.19.4/debian/debuild-base/nginx-1.19.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' #
- 如不支持的解决思路:Nginx为了支持Https需要安装http_ssl_module模块。在编译时需要带上--with-http_ssl_module参数。
./configure --prefix=/usr/local/nginx --with-http_ssl_module make && make install
二、单向验证
1. 原理解析
单向认证流程中,服务器端保存着公钥证书和私钥两个文件,整个握手过程如下:
- 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务器端;
- 服务器端将本机的公钥证书(server.crt)发送给客户端;
- 客户端读取公钥证书(server.crt),取出了服务端公钥;客户端生成一个随机数(密钥R),用刚才得到的服务器公钥去加密这个随机数形成密文,发送给服务端;
- 服务端用自己的私钥(server.key)去解密这个密文,得到了密钥R
- 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。
2. 证书文件
- 服务器端私钥文件:server.key
- 服务器端公钥证书:server.pem
3. 配置方法
- 证书申请和下载
华为云和阿里云有个人的免费的证书,可以申请,但是尴尬的是,需要域名。如果没有域名的小伙伴可以自己通过jdk或者openssl生成证书,如果不会的话,可以参考文章末尾关于生成证书的章节。2. 配置文件、证书存放目录和静态资源目录
root@iZbp1g1qpfdlqea4avz2sfZ:~/nginx-conf# ll total 24 drwxr-xr-x 4 root root 4096 Jan 3 00:49 ./ drwx------ 24 root root 4096 Jan 8 23:26 ../ -rw-r--r-- 1 root root 7205 Dec 29 13:44 nginx.conf drwxr-xr-x 2 root root 4096 Jul 6 2021 ssl/ drwxr-xr-x 3 root root 4096 Dec 27 10:43 static/ root@iZbp1g1qpfdlqea4avz2sfZ:~/nginx-conf# ll ssl total 20 drwxr-xr-x 2 root root 4096 Jul 6 2021 ./ drwxr-xr-x 4 root root 4096 Jan 3 00:49 ../ -rw-r--r-- 1 root root 1679 Jul 6 2021 server.key -rw-r--r-- 1 root root 4224 Jul 6 2021 server.pem
证书下载好后会有一个server.key和server.pem文件,将文件放置在ssl目录下
- 修改nginx.conf
events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #上传大小限制 client_max_body_size 5000m; #下载大小限制 proxy_max_temp_file_size 5000m; gzip on; server { listen 443 ssl; server_name cloud-dev.imagecore.com.cn; # 指定证书文件存放路径 ssl_certificate /etc/nginx/ssl/server.pem; # 指定key文件存放路径 ssl_certificate_key /etc/nginx/ssl/server.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; add_header Content-Security-Policy "upgrade-insecure-requests"; location / { root /etc/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
需要注意的是指定的密钥与证书和挂载的容器数据卷路径是一致的,否则会报找不到证书和密钥的错误。
- 创建nginx容器
docker run --name nginx -p 443:443 -v /root/nginx/nginx.conf:/etc/nginx/nginx.conf -v /root/nginx/ssl:/etc/nginx/ssl -v /root/nginx/html:/etc/nginx/html -d nginx
- 通过https加域名访问主页
三、双向验证
1. 原理解析
- 客户端发起HTTPS连接请求,同时将SSL协议版本的信息发送给服务端;
- 服务器端将本机的公钥证书(server.crt)发送给客户端;
- 客户端读取公钥证书(server.crt),取出了服务端公钥;
- 客户端将客户端公钥证书(client.crt)发送给服务器端;
- 服务器端使用根证书(root.crt)解密客户端公钥证书,拿到客户端公钥;
- 客户端发送自己支持的加密方案给服务器端;
- 服务器端根据自己和客户端的能力,选择一个双方都能接受的加密方案,使用客户端的公钥加密8后发送给客户端;
- 客户端使用自己的私钥解密加密方案,生成一个随机数R,使用服务器公钥加密后传给服务器端;
- 服务端用自己的私钥去解密这个密文,得到了密钥R
- 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了
2. 证书文件
2.1 接入单个客户端
「使用客户端的公钥证书作为服务器端的验证证书来进行客户端的身份验证」
- 服务器端公钥证书:server.pem
- 服务器端私钥文件:server.key
- 服务器端根证书:client.pem
- 客服端公钥证书:client.pem
- 客服端私钥文件:client.key
- 客服端集成证书(含公私钥):client.p12
2.2 接入多个客户端
「使用服务器端跟证书签发客户端证书文件,然后利用根证书来进行客服端身份验证」
- 服务器端公钥证书:server.pem
- 服务器端私钥文件:server.key
- 服务器端根证书:root.crt
- 客服端公钥证书:client.pem
- 客服端私钥文件:client.key
- 客服端集成证书(含公私钥):client.p12
3. 配置方法
3.1 单个客户端
这个直接申请两套华为云或者阿里云的证书就可以了,不做过多的解释,直接展示Nginx的配置就可以了,具体的启动和展示就不在赘述。
events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #上传大小限制 client_max_body_size 5000m; #下载大小限制 proxy_max_temp_file_size 5000m; gzip on; server { listen 443 ssl; server_name cloud-dev.imagecore.com.cn; # 指定证书文件存放路径 ssl_certificate /etc/nginx/ssl/server.pem; # 指定key文件存放路径 ssl_certificate_key /etc/nginx/ssl/server.key; # 指定客户端校验证书 ssl_client_certificate /etc/nginx/ssl/client.pem # 开启客户端证书验证 ssl_verify_client on; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; add_header Content-Security-Policy "upgrade-insecure-requests"; location / { root /etc/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
3.2 多个客户端
接入多个客户端,可以采用多个域名的方式接入,但是不太实用;一般都是使用同一个域名和接口的,剩下的方式就是根证签发客户证书的方式了。
- 「安装openssl」
[root@k8snode1 ~]# yum install Loaded plugins: fastestmirror, product-id, search-disabled-repos, subscription- : manager This system is not registered with an entitlement server. You can use subscripti on-manager to register. Error: Need to pass a list of pkgs to install Mini usage: install PACKAGE... Install a package or packages on your system aliases: install-n, install-na, install-nevra [root@k8snode1 ~]# yum install openssl Loaded plugins: fastestmirror, product-id, search-disabled-repos, subscription- : manager This system is not registered with an entitlement server. You can use subscripti on-manager to register. Determining fastest mirrors * base: mirrors.aliyun.com * extras: mirrors.aliyun.com * updates: mirrors.aliyun.com base | 3.6 kB 00:00 extras | 2.9 kB 00:00 updates | 2.9 kB 00:00 (1/2): extras/7/x86_64/primary_db | 243 kB 00:01 (2/2): updates/7/x86_64/primary_db | 13 MB 00:18 Resolving Dependencies --> Running transaction check ---> Package openssl.x86_64 1:1.0.2k-19.el7 will be updated ---> Package openssl.x86_64 1:1.0.2k-22.el7_9 will be an update --> Processing Dependency: openssl-libs(x86-64) = 1:1.0.2k-22.el7_9 for package: 1:openssl-1.0.2k-22.el7_9.x86_64 --> Running transaction check ---> Package openssl-libs.x86_64 1:1.0.2k-19.el7 will be updated ---> Package openssl-libs.x86_64 1:1.0.2k-22.el7_9 will be an update --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Updating: openssl x86_64 1:1.0.2k-22.el7_9 updates 494 k Updating for dependencies: openssl-libs x86_64 1:1.0.2k-22.el7_9 updates 1.2 M Transaction Summary ================================================================================ Upgrade 1 Package (+1 Dependent package) Total download size: 1.7 M Is this ok [y/d/N]: y Downloading packages: Delta RPMs disabled because /usr/bin/applydeltarpm not installed. (1/2): openssl-1.0.2k-22.el7_9.x86_64.rpm | 494 kB 00:01 (2/2): openssl-libs-1.0.2k-22.el7_9.x86_64.rpm | 1.2 MB 00:02 -------------------------------------------------------------------------------- Total 626 kB/s | 1.7 MB 00:02 Running transaction check Running transaction test Transaction test succeeded Running transaction Updating : 1:openssl-libs-1.0.2k-22.el7_9.x86_64 1/4 Updating : 1:openssl-1.0.2k-22.el7_9.x86_64 2/4 Cleanup : 1:openssl-1.0.2k-19.el7.x86_64 3/4 Cleanup : 1:openssl-libs-1.0.2k-19.el7.x86_64 4/4 Verifying : 1:openssl-1.0.2k-22.el7_9.x86_64 1/4 Verifying : 1:openssl-libs-1.0.2k-22.el7_9.x86_64 2/4 Verifying : 1:openssl-libs-1.0.2k-19.el7.x86_64 3/4 Verifying : 1:openssl-1.0.2k-19.el7.x86_64 4/4 Updated: openssl.x86_64 1:1.0.2k-22.el7_9 Dependency Updated: openssl-libs.x86_64 1:1.0.2k-22.el7_9 Complete!
- 「生成自签名根证书」
(1)创建根证书私钥: openssl genrsa -out root.key 1024 (2)创建根证书请求文件: openssl req -new -out root.csr -key root.key 后续参数请自行填写,下面是一个例子: Country Name (2 letter code) [XX]:CH State or Province Name (full name) []:HB Locality Name (eg, city) [Default City]:WH Organization Name (eg, company) [Default Company Ltd]:HH Organizational Unit Name (eg, section) []:HH Common Name (eg, your name or your server's hostname) []:root Email Address []:aaa Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: (3)创建根证书: openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 365 (4)执行结果查看 [root@k8snode1 ssl]# ll total 12 -rw-r--r-- 1 root root 839 Jan 9 11:08 root.crt -rw-r--r-- 1 root root 647 Jan 9 11:06 root.csr -rw-r--r-- 1 root root 887 Jan 9 10:57 root.key
- 「生成自签名服务器端证书」
(1)生成服务器端证书私钥: openssl genrsa -out server.key 1024 (2) 生成服务器证书请求文件,过程和注意事项参考根证书,本节不详述: openssl req -new -out server.csr -key server.key (3) 生成服务器端公钥证书 openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650 (4) 执行结果查看 [root@k8snode1 ssl]# ll total 28 -rw-r--r-- 1 root root 839 Jan 9 11:08 root.crt -rw-r--r-- 1 root root 647 Jan 9 11:06 root.csr -rw-r--r-- 1 root root 887 Jan 9 10:57 root.key -rw-r--r-- 1 root root 17 Jan 9 11:37 root.srl -rw-r--r-- 1 root root 834 Jan 9 11:37 server.crt -rw-r--r-- 1 root root 643 Jan 9 11:37 server.csr -rw-r--r-- 1 root root 887 Jan 9 11:36 server.key
- 「生成自签名客户端证书」
(1)生成客户端证书秘钥: openssl genrsa -out client.key 1024 openssl genrsa -out client2.key 1024 (2) 生成客户端证书请求文件,过程和注意事项参考根证书,本节不详述: openssl req -new -out client.csr -key client.key openssl req -new -out client2.csr -key client2.key (3) 生客户端证书 openssl x509 -req -in client.csr -out client.crt -signkey client.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650 openssl x509 -req -in client2.csr -out client2.crt -signkey client2.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650 (4) 生客户端p12格式证书,需要输入一个密码,选一个好记的,比如123456 openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12 openssl pkcs12 -export -clcerts -in client2.crt -inkey client2.key -out client2.p12 (5) 执行结果查看 [root@k8snode1 ssl]# ll [root@k8snode1 ssl]# ll total 60 -rw-r--r-- 1 root root 810 Jan 9 11:45 client2.crt -rw-r--r-- 1 root root 615 Jan 9 11:45 client2.csr -rw-r--r-- 1 root root 887 Jan 9 11:43 client2.key -rw-r--r-- 1 root root 1573 Jan 9 11:46 client2.p12 -rw-r--r-- 1 root root 794 Jan 9 11:45 client.crt -rw-r--r-- 1 root root 599 Jan 9 11:43 client.csr -rw-r--r-- 1 root root 891 Jan 9 11:43 client.key -rw-r--r-- 1 root root 1565 Jan 9 11:45 client.p12 -rw-r--r-- 1 root root 839 Jan 9 11:08 root.crt -rw-r--r-- 1 root root 647 Jan 9 11:06 root.csr -rw-r--r-- 1 root root 887 Jan 9 10:57 root.key -rw-r--r-- 1 root root 17 Jan 9 11:45 root.srl -rw-r--r-- 1 root root 834 Jan 9 11:37 server.crt -rw-r--r-- 1 root root 643 Jan 9 11:37 server.csr -rw-r--r-- 1 root root 887 Jan 9 11:36 server.key
- nginx配置文件
events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #上传大小限制 client_max_body_size 5000m; #下载大小限制 proxy_max_temp_file_size 5000m; gzip on; server { listen 443 ssl; server_name cloud-dev.imagecore.com.cn; # 指定证书文件存放路径 ssl_certificate /etc/nginx/ssl/server.crt; # 指定key文件存放路径 ssl_certificate_key /etc/nginx/ssl/server.key; # 指定客户端校验证书 ssl_client_certificate /etc/nginx/ssl/root.crt # 开启客户端证书验证 ssl_verify_client on; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; add_header Content-Security-Policy "upgrade-insecure-requests"; location / { root /etc/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
四、总结
以上就是Nginx单向校验和双向校验在内容,其中双向部分包含了对client的校验部分,应该是回答了上一篇知友的提问。这里下一篇将讲述双向校验的测试方式,这里暂时先保留一下。其实这一部分的配置及其简单,基本上就只有四个属性的配置:ssl_certificate
和ssl_certificate_key
,这一组是服务端检验的参数;ssl_client_certificate
和ssl_verify_client
,这一组是客户端检验的参数。