HTTPS + 域名 + 双向证书认证(下)

文章目录

  • [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 主要特点

  1. 密码保护:必须用密码才能打开和使用
  2. 平台通用:Windows、macOS、Linux都支持
  3. 完整包:包含身份验证所需的所有文件
  4. 二进制格式:不是文本文件,不能直接查看内容

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指令

Nginxlocation 指令有多种匹配方式,每种都有不同的优先级和用途。

  1. 精确匹配 - 最高优先级
bash 复制代码
location = /exact-path {
    # 只完全匹配 /exact-path
    # 不匹配 /exact-path/ 或 /exact-path/other
}

示例:
location = /login {
    # 只匹配 http://example.com/login
    proxy_pass http://backend/login;
}
  1. 优先前缀匹配 - 第二优先级
bash 复制代码
location ^~ /static/ {
    # 匹配以 /static/ 开头的所有路径
    # 且跳过正则表达式检查
}

示例:
location ^~ /static/ {
    # 匹配 /static/, /static/js/, /static/css/app.css 等
    alias /var/www/static/;
}
  1. 正则表达式匹配 - 第三优先级
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 等图片文件
}
  1. 普通前缀匹配 - 最低优先级
bash 复制代码
location /api/ {
    # 匹配以 /api/ 开头的所有路径
}
  1. 命名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 客户端证书生成脚本

batchCreateCilent.sh

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 文件生成完成。"
相关推荐
DoWhatUWant17 小时前
域格YM310 X09移芯CAT1模组HTTPS连接服务器
服务器·网络协议·https
Whisper_Yu1 天前
计算机网络(一)基础概念
计算机网络·http·https·信息与通信
m0_749299951 天前
HTTP与HTTPS
网络协议·http·https
2501_916007472 天前
Transporter App 使用全流程详解:iOS 应用 ipa 上传工具、 uni-app 应用发布指南
android·ios·小程序·https·uni-app·iphone·webview
2501_915909062 天前
HTTPS 错误解析,常见 HTTPS 抓包失败、443 端口错误与 iOS 抓包调试全攻略
android·网络协议·ios·小程序·https·uni-app·iphone
乖女子@@@3 天前
协议_https协议
http·https·iphone
2501_915909063 天前
苹果上架App软件全流程指南:iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传与审核技巧详解
android·ios·小程序·https·uni-app·iphone·webview
2501_915918413 天前
App 苹果 上架全流程解析 iOS 应用发布步骤、App Store 上架流程
android·ios·小程序·https·uni-app·iphone·webview
2501_916007473 天前
苹果上架全流程详解,iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传与审核要点完整指南
android·ios·小程序·https·uni-app·iphone·webview