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 的通信端(客户端和服务端)都需要生成一个公钥和私钥的密钥对。这个密钥对由以下两部分组成:
- 私钥(Secret Key):此密钥只能由持有者知道,用于生成共享密钥。
- 公钥(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算法的原理,计算出的此秘钥与上方的共享秘钥相等),再加上相同的随机数(随报文发送),使用解密函数即可解密出明文。
客户端发起连接,首先生成一个临时密钥对。向服务端发出一个HELLO命令,其中包含客户端临时公钥。
1
2
3
4
5
6hello = %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)当服务器收到HELLO时,它会生成自己的临时密钥对,并将这个 服务器临时私钥 编码为“cookie”(cookie=客户端临时公钥+服务器临时私钥),作为WELCOME命令发送回客户端。WELCOME命令还包含服务器临时公钥,加密后只有客户端可以读取(welcome-box必须使用服务端长期公钥+客户端临时私钥才能解密)。然后丢弃服务端临时密钥对。
1
2
3
4
5
6
7welcome = %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)客户端收到WELCOME命令,解密得到服务器临时公钥和“cookie”。然后客户端发回一个INITIATE命令,其中包含“cookie”、经过加密的客户端永久公钥,只有服务端能够读取到(因为initiate-box必须用客户端临时公钥+服务端临时私钥才能解密)。服务端可从cookie中读取到客户端临时公钥+之前丢弃的服务器临时私钥。
1
2
3
4
5
6
7
8initiate = %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拒绝服务攻击。
服务器读取INITIATE命令,解密后可读取客户端永久公钥(在上方initiate-box中),然后可以对客户端身份进行验证。然后还可以读取cookie,获取客户端临时公钥、服务器临时私钥。随即服务器回复READY命令,表示握手成功。
1
2
3ready = %d5 "READY" ready-nonce ready-box
ready-nonce = 8OCTET ; Short nonce, prefixed by "CurveZMQREADY---"
ready-box = 16*OCTET ; Box [metadata](S'->C')至此,通过安全的方式,双方各自拥有了自己的临时私钥、对方的临时公钥。利用 Curve25519 椭圆曲线算法的特性,双方可以独立地计算出相同的共享密钥 (Shared Secret),该密钥将作为本次会话的会话密钥 (Session Key)。

4. 加密消息传输阶段
ZeroMQ 底层使用之前协商出的会话密钥、随机数和 Poly1305 认证加密算法对消息进行加密和认证。消息负载Payload使用发送方临时私钥+接收方临时公钥,和一个在报文中发送的随机数,来进行加密(Curve25519算法)。接收方使用发送方临时公钥+接收方临时私钥,和从报文中读取的随机数nonce,进行解密,得到消息负载Payload。
1 | |
大模型对于CurveZMQ的介绍
下面提供两份Deepseek对于CurveZMQ的介绍,结合阅读有助于理解: