文章目录
- 1. .p12文件
- 1.1 主要特点
- 1.2 常见用途
- 1.3 常见操作
- 1.4 与其他格式的区别
- 1.5 与公钥的区别和联系
- 1.6 安全性注意事项
- 2. Nginx 配置
- 2.1 location指令
- 2.2 alias 与 root 指令的区别
- 3 双向认证配置
- 3.1 创建根证书
- 3.1.1 生成根CA的私钥
- 3.1.2 生成请求证书
- 3.1.3 生成自签署CA证书
- 3.2 用根CA为服务器签发证书
- 3.2.1 生成服务器的私钥
- 3.2.2 生成证书请求
- 3.2.3 使用CA证书签署服务器证书
- 3.3 用根CA为客户端签发证书
- 3.3.1 生成客户端私钥
- 3.3.2 生成证书请求
- 3.3.3 使用CA证书签署客户端证书
- 3.3.4 生成p12
- 3.4 Nginx 服务器块配置
- 3.4.1 增加 HTTPS 服务器配置
- 3.4.2 重启 Nginx
- 3.5 开启端口
- 3.6 客户端证书生成脚本
上文介绍了双向认证证书的文件类型、工作流程、生成和测试指令。本文以实例介绍 https + 域名 + 双向证书认证 访问模式的部署。
构建一个基于 HTTPS + 域名 + 双向证书认证(mTLS)
的模式是一个非常强大的安全实践。它通常用于保护高安全性的API、微服务之间的通信、金融系统或内部管理后台。要实现这个模式,你需要准备以下文件:
角色 | 所需文件 | 作用 |
---|---|---|
证书颁发机构 (CA) | ca.key ca.crt (或 .cer ) | 1. ca.key :CA的私钥,高度敏感,用于签署CSR。2. ca.cer :根证书,必须分发给所有服务器和客户端,作为信任的锚点。 |
服务器 (Server) | server.key server.csr server.crt (或 .cer ) | 1. server.key :服务器的私钥。2. server.csr :服务器的证书签名请求。3. server.crt :由CA签署的服务器证书。证书的SAN中必须包含服务的域名(如 DNS:api.example.com)。 |
客户端 (Client) | client.key client.csr client.crt (或 .cer ) | 1. client.key :客户端的私钥。 2. client.csr :客户端的证书签名请求。3. client.crt :由 同一个CA 签署的客户端证书。 |
1. .p12文件
.p12文件(也称为PFX
文件)是一种数字证书容器文件,采用PKCS#12
标准格式。它就像一个"数字身份证文件包",将多个安全相关的文件打包在一起并用密码保护。
一个典型的.p12
文件包含:
- 🔑 私钥:加密密钥
- 📄 用户证书:公钥和身份信息
- 🔗 CA证书链:颁发机构的证书(建立信任链)
- 📝 其他元数据:证书名称、有效期等信息
1.1 主要特点
- 密码保护:必须用密码才能打开和使用
- 平台通用:Windows、macOS、Linux都支持
- 完整包:包含身份验证所需的所有文件
- 二进制格式:不是文本文件,不能直接查看内容
1.2 常见用途
🔐 客户端身份认证
# 使用.p12文件访问需要认证的网站
curl --cert client.p12:password https://secure.example.com
💻 导入到操作系统
- Windows:双击导入到证书管理器
- macOS:钥匙串访问
- Linux:用于各种应用的客户端认证
🌐 浏览器客户端证书
- 用于访问银行、政府等需要强认证的网站
🚪 VPN接入认证
- 许多VPN系统使用.p12文件进行客户端认证
1.3 常见操作
- 查看.p12文件信息:
openssl pkcs12 -info -in zhoubowen.p12
# 会提示输入密码,然后显示文件内容
- 从.p12提取各个组件:
# 提取私钥
openssl pkcs12 -in zhoubowen.p12 -nocerts -out private.key# 提取证书(包含公钥)
openssl pkcs12 -in zhoubowen.p12 -clcerts -nokeys -out certificate.crt# 从证书提取公钥
openssl x509 -in certificate.crt -pubkey -noout > zhoubowen_public.pem# 提取CA证书
openssl pkcs12 -in zhoubowen.p12 -cacerts -nokeys -out ca.crt
- 生成.p12文件:
openssl pkcs12 -export \-inkey private.key \-in certificate.crt \-certfile ca.crt \-out zhoubowen.p12 \-name "pxshen" \-password pass:"@HzRaobR7SPX"
pkcs12
:处理PKCS#12格式文件的子命令-export
: 表示要创建一个新的PKCS#12文件-inkey
:指定私钥-in
:指定数字证书,由CA签发-certfile
:指定CA的根证书-name
:为证书设置一个友好名称,导入证书库时显示这个名称,方便用户识别和管理多个证书。-password
:设置.p12文件的加密密码,pass:"$password"
表示直接提供密码文本,也可以使用其他方式,如env:
、file:
等。
1.4 与其他格式的区别
格式 | 扩展名 | 内容 |
---|---|---|
.p12/.pfx | .p12, .pfx | 私钥+证书+CA链 |
.pem | .pem, .crt, .key | 私钥、公钥 |
.jks | .jks | Java密钥库格式 |
1.5 与公钥的区别和联系
- 包含关系:
.p12文件 = 私钥 + 公钥证书 + CA证书链
公钥证书 = 公钥 + 身份信息 + CA签名
- 从.p12文件中提取公钥:
# 从.p12文件中提取公钥
openssl pkcs12 -in user.p12 -clcerts -nokeys -out public_key.pem# 或者从证书中提取公钥
openssl x509 -in user.crt -pubkey -noout > public_key.pem
.p12文件用于客户端SSL认证
,导入到浏览器,用于网站客户端证书认证。而公钥用于加密数据、验证签名。
1.6 安全性注意事项
- 密码强度:使用强密码保护.p12文件
- 安全存储:像保护银行卡密码一样保护.p12文件
- 定期更换:证书过期后应及时更新
- 谨慎分发:只在必要时分发给授权用户
2. Nginx 配置
Nginx
是配置 mTLS 非常常见的 Web 服务器。
此处引申一下 nginx.conf 中 HTTP
的配置:
# 全局配置
user root; # 以root用户身份运行工作进程
worker_processes auto; # 自动设置工作进程数量(通常等于CPU核心数)
error_log /var/log/nginx/error.log; # 错误日志位置
pid /run/nginx.pid; # 存储主进程ID的文件位置# 加载动态模块
include /usr/share/nginx/modules/*.conf;# 事件模块
events {worker_connections 1024; # 每个工作进程最大并发连接数
}# HTTP模块
http {# 定义日志格式log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';# 访问日志位置和格式access_log /var/log/nginx/access.log main;# 性能优化相关设置sendfile on; # 使用高效文件传输tcp_nopush on; # 优化数据包发送tcp_nodelay on; # 禁用Nagle算法keepalive_timeout 65; # 保持连接超时时间types_hash_max_size 4096; # MIME类型哈希表大小# include /etc/nginx/mime.types; # MIME类型定义default_type application/octet-stream; # 默认MIME类型# 包含其他配置文件include /etc/nginx/conf.d/*.conf;# HTTP服务器配置server {listen 80; # 监听80端口(HTTP)server_name 182.61.130.103; # 服务器域名或IP地址# 前端静态资源location /h5/ {alias /mnt/h5/; # 将/h5/映射到本地目录/mnt/h5/try_files $uri $uri/ =404; # 尝试提供请求的文件,否则返回404}# 后台静态资源服务location / {root /mnt/vue-ui/dist;index index.html index.htm;}# API 转发location ~ /manager/ {# rewrite ^/manager/(.*) /$1 break; # 重写URL,移除/manager/前缀proxy_pass http://127.0.0.1:8081; # 转发到本地8081端口# 设置代理头信息proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;# 支持WebSocketproxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";}# 自定义404错误页面error_page 404 /404.html;location = /404.html {root /usr/share/nginx/html/; # 404页面所在目录internal; # 防止直接通过URL访问}# 阻止访问非指定路径的请求,匹配所有不以 /h5/ /manager/ 开头的URL路径# location ~ ^(?!/(h5|manager)/) {# return 404;# }}
}
配置一个 HTTP
服务器配置,静态资源服务通过 /h5/
路径提供前端静态资源,通过 /
路径提供后台管理静态资源,将所有 /manager/
开头的请求转发到本地的 8081
端口应用服务器。
2.1 location指令
Nginx 的 location
指令有多种匹配方式,每种都有不同的优先级和用途。
- 精确匹配 - 最高优先级
location = /exact-path {# 只完全匹配 /exact-path# 不匹配 /exact-path/ 或 /exact-path/other
}示例:
location = /login {# 只匹配 http://example.com/loginproxy_pass http://backend/login;
}
- 优先前缀匹配 - 第二优先级
location ^~ /static/ {# 匹配以 /static/ 开头的所有路径# 且跳过正则表达式检查
}示例:
location ^~ /static/ {# 匹配 /static/, /static/js/, /static/css/app.css 等alias /var/www/static/;
}
- 正则表达式匹配 - 第三优先级
location ~ /user/[0-9]+ {# 匹配 /user/123, /user/456 等
}location ~ /manager/ {# 这个配置会匹配任何包含 "/manager/" 字符串的URL# 匹配 /manager/ /manager/123 /manager/123/456 /user/manager/settings (中间包含 /manager/)# 不会匹配 /manager (缺少结尾的斜杠)
}location ~* \.(jpg|jpeg|png|gif)$ {# 匹配 .jpg, .JPG, .PNG 等图片文件
}
- 普通前缀匹配 - 最低优先级
location /api/ {# 匹配以 /api/ 开头的所有路径
}
- 命名location - 用于内部重定向
location @fallback {# 不会直接匹配客户端请求# 只能通过 error_page, try_files 等内部指令使用proxy_pass http://backup-server;
}
匹配优先级从高到低:
location = /path
(精确匹配)location ^~ /prefix/
(优先前缀匹配)location ~ /regex/
或location ~* /regex/
(按配置顺序)location /prefix/
(普通前缀匹配)
2.2 alias 与 root 指令的区别
特性 | alias | root |
---|---|---|
路径映射 | 完全替换匹配的路径部分 | 将匹配的路径追加到指定目录后 |
语法 | location /path/ { alias /dir/; } | location /path/ { root /dir/; } |
路径处理 | /path/file → /dir/file | /path/file → /dir/path/file |
结尾斜杠 | 通常需要以斜杠结尾 | 不需要以斜杠结尾 |
适用场景 | 虚拟目录映射 | 常规目录服务 |
alias示例:
location /mgr/ {alias /mnt/view/mgr/;# 请求: /mgr/index.html# 实际文件: /mnt/view/mgr/index.html
}
root示例:
location /mgr/ {root /mnt/view/;# 请求: /mgr/index.html# 实际文件: /mnt/view/mgr/index.html
}
3 双向认证配置
目录层级:
/mnt/certs/
├── ca/
│ ├── ca.key # ca密钥
│ ├── ca.csr # ca证书请求文件
│ ├── ca.crt # ca根证书
│ └── ca.srl # ca序列号文件
├── server/
│ ├── server.key # 服务器密钥
│ ├── server.csr # 服务器请求文件
│ └── server.crt # 服务器证书
├── client/
│ ├── client.key # 客户端密钥
│ ├── csr/
│ │ └── *.csr # 客户端请求文件
│ ├── crt/
│ │ └── *.crt # 客户端证书
│ ├── p12/
│ │ └── *.p12 # 客户端数字证书容器文件
│ └── passwords.txt # 数字证书密码
└── script└── batchCreateCilent.sh # 客户端证书生成脚本
注1:密钥、证书指令含义参见上篇《HTTPS + 域名 + 双向证书认证(上)》
3.1 创建根证书
3.1.1 生成根CA的私钥
openssl genrsa -aes256 -out /mnt/certs/ca/ca.key 2048caKey
3.1.2 生成请求证书
openssl req -new -sha256 \-key /mnt/certs/ca/ca.key \-out /mnt/certs/ca/ca.csr \-subj "/C=CN/ST=AnHui/L=HeFei/O=Ums/OU=IT/CN=CA/emailAddress=admin@ums.com"
3.1.3 生成自签署CA证书
openssl x509 -req -days 36500 -sha256 -extensions v3_ca \-signkey /mnt/certs/ca/ca.key \-in /mnt/certs/ca/ca.csr \-out /mnt/certs/ca/ca.crt
3.2 用根CA为服务器签发证书
3.2.1 生成服务器的私钥
openssl genrsa -aes256 -out /mnt/certs/server/server.key 2048serverKey
3.2.2 生成证书请求
openssl req -new -sha256 \-key /mnt/certs/server/server.key \-out /mnt/certs/server/server.csr \-subj "/C=CN/ST=AnHui/L=HeFei/O=Ums/OU=IT/CN=ahgasazj/emailAddress=admin@ums.com"
3.2.3 使用CA证书签署服务器证书
openssl x509 -req -days 3650 -sha256 -extensions v3_req \-CA /mnt/certs/ca/ca.crt \-CAkey /mnt/certs/ca/ca.key \-CAserial /mnt/certs/ca/ca.srl \-CAcreateserial \-in /mnt/certs/server/server.csr \-out /mnt/certs/server/server.crt
3.3 用根CA为客户端签发证书
3.3.1 生成客户端私钥
openssl genrsa -aes256 -out /mnt/certs/client/client.key 2048clientKey
3.3.2 生成证书请求
openssl req -new -sha256 \-key /mnt/certs/client/client.key \-out /mnt/certs/client/csr/pxshen.csr \-subj "/C=CN/ST=AnHui/L=HeFei/O=Ums/OU=IT/CN=pxshen/emailAddress=admin@ums.com"
3.3.3 使用CA证书签署客户端证书
openssl x509 -req -days 3650 -sha256 -extensions v3_req \-CA /mnt/certs/ca/ca.crt \-CAkey /mnt/certs/ca/ca.key \-CAserial /mnt/certs/ca/ca.srl \-CAcreateserial \-in /mnt/certs/client/csr/pxshen.csr \-out /mnt/certs/client/crt/pxshen.crt
3.3.4 生成p12
openssl pkcs12 -export \-inkey /mnt/certs/client/client.key \-in /mnt/certs/client/crt/pxshen.crt \-certfile /mnt/certs/ca/ca.crt \-out /mnt/certs/client/p12/pxshen.p12 \-name "pxshen" \-password pass:"@HzRaobR7SPX"
3.4 Nginx 服务器块配置
3.4.1 增加 HTTPS 服务器配置
http {...# HTTP服务器配置server {...}# HTTPS 服务器配置server {listen 2443 ssl;server_name ahyjhxsh.com;# 服务器证书和私钥ssl_certificate /mnt/certs/server/server.crt;ssl_certificate_key /mnt/certs/server/server.key;# 启用双向认证ssl_client_certificate /mnt/certs/ca/ca.crt; # 告诉Nginx:用这个CA证书去验证客户端提供的证书ssl_verify_client on; # 开启客户端证书验证# 后台静态资源服务location / {if ($ssl_client_verify != SUCCESS) {return 403;}root /mnt/vue-ui/dist;index index.html index.htm;}# API 转发location ~ /manager/ {# rewrite ^/manager/(.*) /$1 break; # 重写URL,移除/manager/前缀proxy_pass http://127.0.0.1:8081; # 转发到本地8081端口# 设置代理头信息proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;# 支持WebSocketproxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";# 如果验证成功,Nginx会将客户端证书的信息存储在变量中# 你可以将这些信息传递给后端应用(如用户名、证书序列号等)proxy_set_header X-SSL-Client-Verify $ssl_client_verify;proxy_set_header X-SSL-Client-S-DN $ssl_client_s_dn;proxy_set_header X-SSL-Client-I-DN $ssl_client_i_dn;proxy_set_header X-SSL-Client-Serial $ssl_client_serial;}}
}
关键指令解释:
ssl_client_certificate
:指定信任的CA证书文件,用于验证客户端证书。ssl_verify_client on
:开启强制双向认证。没有有效客户端证书的连接将被拒绝。
3.4.2 重启 Nginx
报错:
[root@instance-9iidvp74-1 conf]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
nginx: [emerg] the "ssl" parameter requires ngx_http_ssl_module in /usr/local/nginx/conf/nginx.conf:85
需要 nginx 开启 ssl 模块
# 进入 Nginx 源码目录(如果不知道位置,可以重新下载)
/mnt/nginx/nginx-1.20.1/# 使用 nginx -V 输出的配置参数,并添加 --with-http_ssl_module
./configure --with-http_ssl_module# 编译
make# 不要 make install,会覆盖配置!而是替换二进制文件
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
cp objs/nginx /usr/local/nginx/sbin/nginx
[root@instance-9iidvp74-1 nginx-1.20.1]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
Enter PEM pass phrase:
启动需要输入 server.key 密码
3.5 开启端口
# 防火墙开启2443端口
firewall-cmd --zone=public --add-port=2443/tcp --permanent# 刷新
firewall-cmd --reload
3.6 客户端证书生成脚本
batchCreateCilent.sh:
[root@instance-1ays3hq4 certs]# cat script/batchCreateCilent.sh
#!/bin/bash
usernames=("zhoubowen")# 定义随机密码生成函数
generate_password() {local length=${1:-12} # 默认长度为 12tr -dc 'A-Za-z0-9!@#$^*()_+' < /dev/urandom | head -c "$length"
}# 创建必要的目录
mkdir -p /mnt/certs/client/{csr,crt,p12}# 循环遍历用户名列表
for username in "${usernames[@]}"; doecho "正在为用户 $username 生成证书..."# 生成 CSR 文件openssl req -new \-key /mnt/certs/client/client.key \-out /mnt/certs/client/csr/${username}.csr \-subj "/C=CN/ST=AnHui/L=Hefei/O=ahyjhxsh/CN=$username"# 检查 CSR 文件是否生成成功if [ $? -ne 0 ]; thenecho "生成 CSR 文件失败:$username"continuefi# 使用 CA 签发证书并生成 CRT 文件openssl x509 -req \-in /mnt/certs/client/csr/${username}.csr \-CA ca/ca.crt \-CAkey ca/ca.key \-CAcreateserial \-out /mnt/certs/client/crt/${username}.crt \-days 365# 检查 CRT 文件是否生成成功if [ $? -ne 0 ]; thenecho "生成证书失败:$username"continuefi# 生成随机密码password=$(generate_password 12)echo "用户 $username 的 .p12 文件密码: $password"# 生成 P12 文件openssl pkcs12 -export \-inkey /mnt/certs/client/client.key \-in /mnt/certs/client/crt/${username}.crt \-certfile ca/ca.crt \-out /mnt/certs/client/p12/${username}.p12 \-name "$username" \-password pass:"$password"# 检查 P12 文件是否生成成功if [ $? -eq 0 ]; thenecho "用户 $username 的 .p12 文件已成功生成!"elseecho "生成 .p12 文件失败:$username"fiecho "$username:$password" >> /mnt/certs/client/passwords.txt
doneecho "所有用户的证书和 .p12 文件生成完成。"