CurveZMQ协议简介

本文最后更新于 2025年9月19日 晚上

本文主要介绍ZeroMQ的安全模型——CurveZMQ 协议,简单描述其流程。将个人理解记录为笔记。

简介

ZeroMQ的安全模型基于 CurveZMQ 协议,该协议基于 Curve25519 椭圆曲线加密算法,使用公钥加密和对称密钥加密相结合的方式。CurveZMQ 提供了端到端的加密通信,确保消息在传输过程中不会被窃听或篡改。同时,CurveZMQ 还实现了一个基于公钥/私钥对的认证机制,以验证通信节点的身份。

CurveZMQ 可以解决重放攻击、放大攻击、MIM攻击、密钥盗窃、客户端识别和各种拒绝服务攻击。

CurveZMQ 的安全性证明和详细过程参考:https://rfc.zeromq.org/spec/26/

其他参考网站:

https://libzmq.readthedocs.io/en/latest/zmq_curve.html

https://blog.csdn.net/weixin_45661808/article/details/145155486

https://developer.aliyun.com/article/1463834

CurveZMQ 加密通信过程

1. 密钥对生成

每个使用 CurveZMQ 的通信端(客户端和服务端)都需要生成一个公钥和私钥的密钥对。这个密钥对由以下两部分组成:

  1. 私钥(Secret Key):此密钥只能由持有者知道,用于生成共享密钥。
  2. 公钥(Public Key):这个密钥可以公开,用来交换给对方。

密钥对生成过程是通过 zmq_curve_keypair() 函数完成的(Curve25519 算法)。

客户端生成长期的客户端密钥对客户端公钥客户端私钥

服务端生成长期的服务端密钥对服务端公钥服务端私钥

2. 公钥交换

  • 在 CurveZMQ 协议中,公钥用于验证身份并生成共享密钥。因此,通信双方必须交换各自的公钥。
  • 交换公钥的方式可以是通过配置或预先共享的方式进行。也可以在一个安全的渠道(如 HTTPS)中进行。
  • 交换完成后,客户端知道服务端长期公钥,服务端知道客户端长期公钥

3. CurveZMQ握手过程

握手过程的官方详细介绍见:https://rfc.zeromq.org/spec/26/

符号定义

客户端长期密钥对记为C ,服务端长期密钥对记为S。

客户端临时密钥对记为C’,服务端临时密钥对记为S’。

定义 Box[X] (C->S) 表示一个加密盒子,它将明文X,“从C加密到S”,这意味着只有C可以创建盒子,只有S可以打开它。盒子是从C到S的单向信息传输。创建和打开盒子的实际步骤是:

  • 发送方C 使用C的私钥S的公钥计算一个共享秘钥(使用Curve25519算法计算出的ECDH秘钥),再生成一个随机数Nonce。然后使用加密函数对明文进行加密(该函数内部通常使用XSalsa20流密码进行加密,并使用Poly1305消息认证码来确保密文的完整性和真实性)
  • 接收方S 使用C的公钥S的私钥计算相同的共享秘钥(根据Curve25519算法的原理,计算出的此秘钥与上方的共享秘钥相等),再加上相同的随机数(随报文发送),使用解密函数即可解密出明文。
  1. 客户端发起连接,首先生成一个临时密钥对。向服务端发出一个HELLO命令,其中包含客户端临时公钥。

    1
    2
    3
    4
    5
    6
    hello = %d5 "HELLO" hello-version hello-padding hello-client hello-nonce hello-box
    hello-version = %x1 %x0 ; CurveZMQ major-minor version
    hello-padding = 72%x00 ; Anti-amplification padding
    hello-client = 32OCTET ; Client public transient key C'
    hello-nonce = 8OCTET ; Short nonce, prefixed by "CurveZMQHELLO---"
    hello-box = 80OCTET ; Signature, Box [64 * %x0](C'->S)
  2. 当服务器收到HELLO时,它会生成自己的临时密钥对,并将这个 服务器临时私钥 编码为“cookie”(cookie=客户端临时公钥+服务器临时私钥),作为WELCOME命令发送回客户端。WELCOME命令还包含服务器临时公钥,加密后只有客户端可以读取(welcome-box必须使用服务端长期公钥+客户端临时私钥才能解密)。然后丢弃服务端临时密钥对。

    1
    2
    3
    4
    5
    6
    7
    welcome = %d7 "WELCOME" welcome-nonce welcome-box
    welcome-nonce = 16OCTET ; Long nonce, prefixed by "WELCOME-"
    welcome-box = 144OCTET ; Box [S' + cookie](S->C')

    cookie = cookie-nonce cookie-box
    cookie-nonce = 16OCTET ; Long nonce, prefixed by "COOKIE--"
    cookie-box = 80OCTET ; Box [C' + s'](K)
  3. 客户端收到WELCOME命令,解密得到服务器临时公钥和“cookie”。然后客户端发回一个INITIATE命令,其中包含“cookie”、经过加密的客户端永久公钥,只有服务端能够读取到(因为initiate-box必须用客户端临时公钥+服务端临时私钥才能解密)。服务端可从cookie中读取到客户端临时公钥+之前丢弃的服务器临时私钥。

    1
    2
    3
    4
    5
    6
    7
    8
    initiate = %d8 "INITIATE" initiate-cookie initiate-nonce initiate-box 
    initiate-cookie = cookie ; Server-provided cookie
    initiate-nonce = 8OCTET ; Short nonce, prefixed by "CurveZMQINITIATE"
    initiate-box = 144*OCTET ; Box [C + vouch + metadata](C'->S')

    vouch = vouch-nonce vouch-box
    vouch-nonce = 16OCTET ; Long nonce, prefixed by "VOUCH---"
    vouch-box = 80OCTET ; Box [C',S](C->S')

    服务端临时密钥对:服务器在发送WELCOME时生成,但是并不保存。而是在cookie中传输给客户端。直到客户端回传回有效的INITIATE命令后,重新从cookie中读取出服务器临时私钥。其目的是将连接状态(服务器临时私钥的密文)交给客户端保存,自己无需在内存中为大量未完成的握手连接保存状态,能有效防御DoS拒绝服务攻击。

  4. 服务器读取INITIATE命令,解密后可读取客户端永久公钥(在上方initiate-box中),然后可以对客户端身份进行验证。然后还可以读取cookie,获取客户端临时公钥、服务器临时私钥。随即服务器回复READY命令,表示握手成功。

    1
    2
    3
    ready = %d5 "READY" ready-nonce ready-box 
    ready-nonce = 8OCTET ; Short nonce, prefixed by "CurveZMQREADY---"
    ready-box = 16*OCTET ; Box [metadata](S'->C')
  5. 至此,通过安全的方式,双方各自拥有了自己的临时私钥、对方的临时公钥。利用 Curve25519 椭圆曲线算法的特性,双方可以独立地计算出相同的共享密钥 (Shared Secret),该密钥将作为本次会话的会话密钥 (Session Key)。

4. 加密消息传输阶段

ZeroMQ 底层使用之前协商出的会话密钥、随机数和 Poly1305 认证加密算法对消息进行加密和认证。消息负载Payload使用发送方临时私钥+接收方临时公钥,和一个在报文中发送的随机数,来进行加密(Curve25519算法)。接收方使用发送方临时公钥+接收方临时私钥,和从报文中读取的随机数nonce,进行解密,得到消息负载Payload。

1
2
3
4
5
6
7
8
; MESSAGE command, 33+ octets 
message = %d7 "MESSAGE" message_nonce message-box
message-nonce = 8OCTET ; Short nonce, prefixed by "CurveZMQMESSAGE-"
message-box = 17*OCTET ; Box [payload](S'->C') or (C'->S')
; This is the text sent encrypted in the box
payload = payload-flags payload-data
payload-flags = OCTET ; Explained below
payload-data = *octet ; 0 or more octets

大模型对于CurveZMQ的介绍

下面提供两份Deepseek对于CurveZMQ的介绍,结合阅读有助于理解:

文件1:

文件2:


CurveZMQ协议简介
https://leo.zz.ac/posts/f088.html
作者
AquariusLeo
发布于
2025年9月7日
更新于
2025年9月19日
许可协议