文章目录
- [1. .p12文件](#1. .p12文件)
-
- [1.1 主要特点](#1.1 主要特点)
- [1.2 常见用途](#1.2 常见用途)
- [1.3 常见操作](#1.3 常见操作)
- [1.4 与其他格式的区别](#1.4 与其他格式的区别)
- [1.5 与公钥的区别和联系](#1.5 与公钥的区别和联系)
- [1.6 安全性注意事项](#1.6 安全性注意事项)
- [2. Nginx 配置](#2. Nginx 配置)
-
- [2.1 location指令](#2.1 location指令)
- [2.2 alias 与 root 指令的区别](#2.2 alias 与 root 指令的区别)
- [3 双向认证配置](#3 双向认证配置)
-
- [3.1 创建根证书](#3.1 创建根证书)
-
- [3.1.1 生成根CA的私钥](#3.1.1 生成根CA的私钥)
- [3.1.2 生成请求证书](#3.1.2 生成请求证书)
- [3.1.3 生成自签署CA证书](#3.1.3 生成自签署CA证书)
- [3.2 用根CA为服务器签发证书](#3.2 用根CA为服务器签发证书)
-
- [3.2.1 生成服务器的私钥](#3.2.1 生成服务器的私钥)
- [3.2.2 生成证书请求](#3.2.2 生成证书请求)
- [3.2.3 使用CA证书签署服务器证书](#3.2.3 使用CA证书签署服务器证书)
- [3.3 用根CA为客户端签发证书](#3.3 用根CA为客户端签发证书)
-
- [3.3.1 生成客户端私钥](#3.3.1 生成客户端私钥)
- [3.3.2 生成证书请求](#3.3.2 生成证书请求)
- [3.3.3 使用CA证书签署客户端证书](#3.3.3 使用CA证书签署客户端证书)
- [3.3.4 生成p12](#3.3.4 生成p12)
- [3.4 Nginx 服务器块配置](#3.4 Nginx 服务器块配置)
-
- [3.4.1 增加 HTTPS 服务器配置](#3.4.1 增加 HTTPS 服务器配置)
- [3.4.2 重启 Nginx](#3.4.2 重启 Nginx)
- [3.5 开启端口](#3.5 开启端口)
- [3.6 客户端证书生成脚本](#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 常见用途
🔐 客户端身份认证
bash
# 使用.p12文件访问需要认证的网站
curl --cert client.p12:password https://secure.example.com
💻 导入到操作系统
- Windows:双击导入到证书管理器
- macOS:钥匙串访问
- Linux:用于各种应用的客户端认证
🌐 浏览器客户端证书
- 用于访问银行、政府等需要强认证的网站
🚪 VPN接入认证
- 许多VPN系统使用.p12文件进行客户端认证
1.3 常见操作
- 查看.p12文件信息:
bash
openssl pkcs12 -info -in zhoubowen.p12
# 会提示输入密码,然后显示文件内容
- 从.p12提取各个组件:
bash
# 提取私钥
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文件:
bash
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 与公钥的区别和联系
- 包含关系:
bash
.p12文件 = 私钥 + 公钥证书 + CA证书链
公钥证书 = 公钥 + 身份信息 + CA签名
- 从.p12文件中提取公钥:
bash
# 从.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
的配置:
shell
# 全局配置
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;
# 支持WebSocket
proxy_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
指令有多种匹配方式,每种都有不同的优先级和用途。
- 精确匹配 - 最高优先级
bash
location = /exact-path {
# 只完全匹配 /exact-path
# 不匹配 /exact-path/ 或 /exact-path/other
}
示例:
location = /login {
# 只匹配 http://example.com/login
proxy_pass http://backend/login;
}
- 优先前缀匹配 - 第二优先级
bash
location ^~ /static/ {
# 匹配以 /static/ 开头的所有路径
# 且跳过正则表达式检查
}
示例:
location ^~ /static/ {
# 匹配 /static/, /static/js/, /static/css/app.css 等
alias /var/www/static/;
}
- 正则表达式匹配 - 第三优先级
bash
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 等图片文件
}
- 普通前缀匹配 - 最低优先级
bash
location /api/ {
# 匹配以 /api/ 开头的所有路径
}
- 命名location - 用于内部重定向
bash
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示例:
bash
location /mgr/ {
alias /mnt/view/mgr/;
# 请求: /mgr/index.html
# 实际文件: /mnt/view/mgr/index.html
}
root示例:
bash
location /mgr/ {
root /mnt/view/;
# 请求: /mgr/index.html
# 实际文件: /mnt/view/mgr/index.html
}
3 双向认证配置
目录层级:
bash
/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的私钥
bash
openssl genrsa -aes256 -out /mnt/certs/ca/ca.key 2048
caKey
3.1.2 生成请求证书
bash
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证书
bash
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 生成服务器的私钥
bash
openssl genrsa -aes256 -out /mnt/certs/server/server.key 2048
serverKey
3.2.2 生成证书请求
bash
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证书签署服务器证书
bash
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 生成客户端私钥
bash
openssl genrsa -aes256 -out /mnt/certs/client/client.key 2048
clientKey
3.3.2 生成证书请求
bash
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证书签署客户端证书
bash
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
bash
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 服务器配置
bash
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;
# 支持WebSocket
proxy_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
报错:
bash
[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 模块
bash
# 进入 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
bash
[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 开启端口
bash
# 防火墙开启2443端口
firewall-cmd --zone=public --add-port=2443/tcp --permanent
# 刷新
firewall-cmd --reload
3.6 客户端证书生成脚本
bash
[root@instance-1ays3hq4 certs]# cat script/batchCreateCilent.sh
#!/bin/bash
usernames=("zhoubowen")
# 定义随机密码生成函数
generate_password() {
local length=${1:-12} # 默认长度为 12
tr -dc 'A-Za-z0-9!@#$^*()_+' < /dev/urandom | head -c "$length"
}
# 创建必要的目录
mkdir -p /mnt/certs/client/{csr,crt,p12}
# 循环遍历用户名列表
for username in "${usernames[@]}"; do
echo "正在为用户 $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 ]; then
echo "生成 CSR 文件失败:$username"
continue
fi
# 使用 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 ]; then
echo "生成证书失败:$username"
continue
fi
# 生成随机密码
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 ]; then
echo "用户 $username 的 .p12 文件已成功生成!"
else
echo "生成 .p12 文件失败:$username"
fi
echo "$username:$password" >> /mnt/certs/client/passwords.txt
done
echo "所有用户的证书和 .p12 文件生成完成。"