预共享密钥(PSK,Pre-Shared Key)是 TLS 1.3 引入的重要特性,允许通信双方在握手前共享对称密钥,简化握手流程并提升性能。本文详细介绍 PSK 的原理、使用场景以及 OpenSSL 命令行实战。

什么是 PSK?

PSK 是一种在 TLS 握手前就共享的对称密钥。在传统 TLS 握手过程中,客户端和服务端需要通过公钥密码学协商出共享密钥;而使用 PSK 时,双方直接使用预先共享的密钥进行加密通信。

PSK 的优势

  • 减少握手延迟:省去公钥协商步骤,缩短握手时间
  • 降低计算开销:无需执行昂贵的非对称加密运算
  • 适用于特定场景:物联网设备、CDN 加速等对性能要求高的场景

PSK 的使用场景

  1. 会话恢复(Session Resumption):TLS 会话恢复本质上使用 PSK
  2. 物联网设备:计算资源受限的设备间安全通信
  3. CDN 边缘节点:需要快速建立大量连接的场景
  4. 内部服务通信:在可信网络中使用对称密钥

PSK 密钥生成

PSK 密钥通常是 256 位(32 字节)的随机数据。使用 OpenSSL 生成:

1
2
# 生成 256 位 PSK 密钥(32 字节 = 64 个十六进制字符)
openssl rand -hex 32

输出示例:

1
779cfa0b22020c13ae4e841e0c96b1b90b3afa48f6bcb85192f1694156676aac

⚠️ 注意:PSK 密钥必须安全存储和传输,不要在代码中硬编码或通过网络明文传输。

OpenSSL s_client PSK 测试

基础连接测试

使用 PSK 连接到支持 PSK 的服务器:

1
2
3
4
# 使用 PSK 连接到服务器
openssl s_client -connect example.com:443 \
    -psk 779cfa0b22020c13ae4e841e0c96b1b90b3afa48f6bcb85192f1694156676aac \
    -psk_identity "my-client-identity"

参数说明:

  • -psk:PSK 密钥(十六进制格式,不带 0x 前缀)
  • -psk_identity:PSK 身份标识,服务端用于查找对应的密钥

PSK 会话复用

保存 PSK 会话用于复用:

1
2
3
4
5
# 保存 PSK 会话
openssl s_client -connect example.com:443 \
    -psk 779cfa0b22020c13ae4e841e0c96b1b90b3afa48f6bcb85192f1694156676aac \
    -psk_identity "my-client-identity" \
    -psk_session session.pem

复用会话:

1
2
3
# 使用保存的 PSK 会话
openssl s_client -connect example.com:443 \
    -psk_session session.pem

验证 PSK 握手

查看握手是否使用了 PSK:

1
2
3
4
openssl s_client -connect example.com:443 \
    -psk 779cfa0b22020c13ae4e841e0c96b1b90b3afa48f6bcb85192f1694156676aac \
    -psk_identity "my-client-identity" \
    -debug 2>&1 | grep -i "psk\|cipher"

Nginx PSK 配置

服务端配置 PSK 认证需要使用 OpenSSL 库支持。以下是 Nginx 配置示例:

生成服务端 PSK 密钥

1
2
# 服务端为客户端生成单独的 PSK
openssl rand -hex 32

Nginx 配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
server {
    listen 443 ssl;
    server_name psk.example.com;

    # 密码套件:仅允许支持 PSK 的套件
    ssl_ciphers 'TLS13-AES-256-GCM-SHA384';
    ssl_prefer_server_ciphers on;

    # PSK 密钥文件(格式:identity:hex-key)
    ssl_psk_file /etc/nginx/psk.keys;
    ssl_psk_hint "psk-auth";

    # 其他 SSL 配置
    ssl_certificate /path/to/certificate.pem;
    ssl_certificate_key /path/to/private.key;
}

PSK 密钥文件格式

/etc/nginx/psk.keys 文件格式:

1
2
my-client-identity:779cfa0b22020c13ae4e841e0c96b1b90b3afa48f6bcb85192f1694156676aac
client2:aa1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456

⚠️ 注意:Nginx 的 PSK 支持需要 OpenSSL 1.1.1+ 版本,且需要编译时启用 PSK 支持。

PSK 与 TLS 1.3 0-RTT

TLS 1.3 的 0-RTT(Zero Round Trip Time)模式与 PSK 密切相关。0-RTT 使用 PSK 允许客户端在首次握手时就发送加密数据。

0-RTT 数据的安全风险

0-RTT 数据存在重放攻击风险,详见 TLS 1.3 0-RTT 重放攻击与防护

使用 PSK 启用 0-RTT

1
2
3
4
# 启用 0-RTT(需要 PSK)
openssl s_client -connect example.com:443 \
    -early_data /path/to/early_data.txt \
    -psk_session session.pem

PSK 安全性最佳实践

1. 密钥管理

  • 使用安全的随机数生成器(OpenSSL rand 命令)
  • 定期轮换 PSK 密钥
  • 存储时使用安全的密钥管理系统(KMS)

2. 身份验证

PSK 本身不提供身份验证,建议结合:

  • 客户端证书(mTLS)
  • 应用层身份验证
  • 动态令牌(JWT 等)

3. 前向保密

PSK 不提供前向保密(PFS),因为密钥是静态的。如需 PFS:

  • 定期轮换 PSK
  • 结合 Diffie-Hellman 密钥交换
1
2
3
# 结合 DH 参数实现部分前向保密
ssl_ciphers 'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256';
ssl_dhparam /path/to/dhparam.pem;

验证 PSK 支持

检查 OpenSSL 版本是否支持 PSK:

1
2
openssl version
openssl s_client -help 2>&1 | grep -q psk && echo "PSK supported"

查看支持的密码套件:

1
openssl ciphers -s -tls1_3 | tr ':' '\n'

总结

PSK 是 TLS 1.3 强大的特性,适用于需要高性能、低延迟的安全通信场景。使用时需注意:

  • PSK 密钥必须安全存储和传输
  • 0-RTT 模式存在重放攻击风险,需谨慎使用
  • 建议结合其他认证机制增强安全性
  • 定期轮换 PSK 密钥

参考来源