3 常见签名算法:HMAC‑SHA256、RSA‑SHA256、ECDSA

签名算法是接口认证的“指纹”。你发给服务器的每个请求,都得带上一个别人伪造不了的标记。常见的方案就三种:对称的 HMAC‑SHA256,以及非对称的 RSA‑SHA256 和 ECDSA。我这些年接手的项目里,从内部 API 到开放平台,基本逃不出这三个圈子。下面逐个拆开聊。

3.1 HMAC‑SHA256:简单粗暴的对称签名

HMAC 全称是 Hash‑based Message Authentication Code。说白了就是:把共享密钥和消息揉在一起,算一次 SHA256,然后把结果塞进请求头。

为什么叫“对称”?因为签和验用的是同一个密钥。你俩事先约定好一个 secret,你拿它算签名,服务器拿它验签名。一旦泄露,谁都能伪造请求。

import hmac
import hashlib
import base64

def sign_hmac_sha256(secret: str, message: str) -> str:
    # 密钥、消息都转成 bytes
    h = hmac.new(secret.encode('utf-8'),
                 message.encode('utf-8'),
                 hashlib.sha256)
    # 通常用 Base64 对外传输,省得出现不可见字符
    return base64.b64encode(h.digest()).decode()
我的习惯:message 参数我一般会把 HTTP method、path、时间戳、请求体拼接成一个规范字符串,防止重放和篡改。千万不要只签名“hello”,那样跟没签一样。

HMAC‑SHA256 的优点是快。我测过一个用 Python 写的网关,每秒能验 2 万次签名,CPU 几乎没感觉。缺点是密钥分发麻烦——你和几百个第三方应用共享同一个 secret?那风险就大了。所以它更适合内部系统间调用。

场景推荐度原因
服务间 RPC★★★★★密钥可控,性能高
开放平台★★☆☆☆密钥泄露难以追溯
小程序/移动端★★★☆☆防逆向难度大,需配合动态密钥
我曾经踩过的坑:有一个项目直接把 Secret 硬编码在 H5 前端里,结果被爬虫扒走,刷了几百万次登录。记住,前端永远不要放密钥。HMAC 只适合后端到后端。

3.2 RSA‑SHA256:非对称老大哥

RSA 签名用的是公钥和私钥对。我拿私钥签名,你把我的公钥扔给任何人,他们都能验,但只有我能签。这就是非对称的魅力——你不需要保护公钥,只需要保护好私钥。

签名过程:先对消息算 SHA256 摘要,然后用私钥加密这个摘要。验证时用公钥解密,再对比摘要。当然实际实现用的是 PKCS#1 v1.5 或 PSS 填充,不是为了加密,而是为了抗攻击。

from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA

def sign_rsa_sha256(private_key_pem: str, message: str) -> str:
    key = RSA.import_key(private_key_pem)
    h = SHA256.new(message.encode())
    # pkcs1_15 是标准填充
    signature = pkcs1_15.new(key).sign(h)
    return base64.b64encode(signature).decode()

def verify_rsa_sha256(public_key_pem: str, message: str, signature_b64: str) -> bool:
    key = RSA.import_key(public_key_pem)
    h = SHA256.new(message.encode())
    try:
        pkcs1_15.new(key).verify(h, base64.b64decode(signature_b64))
        return True
    except (ValueError, TypeError):
        return False
关键点:RSA 签名长度固定,跟密钥强度有关。2048 位密钥,签名就是 256 字节。别小看这 256 字节,如果请求头空间有限(比如某些物联网设备),你可能需要换成 ECDSA。

我参与过一个开放平台项目,选择 RSA‑SHA256 是因为第三方应用可以只存公钥,不怕泄露。但刚上线时发现验签速度只有 3000 TPS,比 HMAC 慢了一个数量级。嗯,这里要注意:RSA 的验签比签名更慢,如果服务端需要验大量请求,最好加缓存或升级机器。

指标RSA‑SHA256HMAC‑SHA256
密钥管理私钥保密,公钥公开双方共享同一个 secret
签名性能较慢(约 5000 次/秒)极快(约 50000 次/秒)
验签性能更慢(约 2000 次/秒)与签名相同
签名长度256 字节(2048 bit)32 字节
个人经验:密钥长度我建议至少 2048 位。1024 位已经被认为不安全,别省那点计算量。另外,私钥文件一定要设置 600 权限,我见过有人把私钥直接传 GitHub 的……那是灾难。

3.3 ECDSA:短小精悍的现代方案

ECDSA 是基于椭圆曲线的签名算法。它跟 RSA 一样是非对称的,但密钥更短、签名更紧凑。256 位椭圆曲线安全性等价于 3072 位 RSA。你想想看,同样安全等级,ECDSA 签名只有 64 字节(r + s 各 32 字节),对于带宽敏感的场景简直是救命。

实现上,Python 标准库没有直接提供 ECDSA,但 cryptography 包很好用。用 P‑256 曲线(也叫 secp256r1)最流行。

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives import serialization

def sign_ecdsa_sha256(private_key_pem: str, message: str) -> str:
    key = serialization.load_pem_private_key(private_key_pem.encode(), password=None)
    # 使用 ECDSA + SHA256,编码为 DER 格式
    signature = key.sign(message.encode(), ec.ECDSA(hashes.SHA256()))
    return base64.b64encode(signature).decode()

def verify_ecdsa_sha256(public_key_pem: str, message: str, signature_b64: str) -> bool:
    from cryptography.exceptions import InvalidSignature
    key = serialization.load_pem_public_key(public_key_pem.encode())
    try:
        key.verify(base64.b64decode(signature_b64), message.encode(), ec.ECDSA(hashes.SHA256()))
        return True
    except InvalidSignature:
        return False
一个容易被忽略的细节:ECDSA 签名不是确定性的(除非用 RFC 6979)。这意味着同样消息、同样私钥,每次签名结果都不同。这是特性,不是 bug!但如果你的测试用例里硬编码了预期的签名值,那就会跑不过。我曾经被这个坑过一下午,后来在文档里特意写了一句“ECDSA 签名不可重复比较,只能验签”。

ECDSA 的验签速度接近 RSA,但签名速度更快。我记得有一个 IoT 项目,设备用 ESP32,算力有限。用 RSA 2048 签名需要 500ms,换成 ECDSA P‑256 只用了 80ms——设备电池寿命提升了一大截。选什么算法,还得看你的硬件跑不跑得动。

算法密钥对生成签名速度(参考)验签速度签名长度
HMAC‑SHA256不适用~50 000 /s~50 000 /s32 B
RSA‑SHA256 2048慢(秒级)~5 000 /s~2 000 /s256 B
ECDSA P‑256快(毫秒级)~8 000 /s~2 500 /s64 B
选型建议:如果只做后端对后端,HMAC 最简单最省事。如果涉及第三方,优先考虑 ECDSA —— 安全性高、签名短、性能不差。RSA 虽然生态成熟,但新项目我建议尽量往 ECDSA 靠。
三种签名算法核心流程对比 HMAC‑SHA256 共享密钥 + 消息 SHA256 哈希 签名 = Base64(摘要) RSA‑SHA256 私钥 SHA256(消息) 私钥加密摘要 签名 (256B) ECDSA (P‑256) 私钥 SHA256(消息) ECDSA 签名运算 签名 (64B) 服务端验签 关键区别 • HMAC: 对称密钥,速度最快,签名长度固定 32 字节 • RSA: 非对称,签名长 256 字节,验签慢,适合存量兼容 • ECDSA: 非对称,签名仅 64 字节,性能与安全性平衡最佳

最后来一个总对比表,方便你决策时快速参考。

维度HMAC‑SHA256RSA‑SHA256ECDSA
密钥类型对称(一个 secret)非对称(私钥+公钥)非对称(私钥+公钥)
签名长度32 字节256 字节(2048 bit)64 字节(P‑256)
签名速度★★★★★★★★☆☆★★★★☆
验签速度★★★★★★★☆☆☆★★★☆☆
密钥分发难度高(双方需安全信道)低(公钥可公开)低(公钥可公开)
抗量子计算弱弱弱(但可升级到 ECC 安全等级)
推荐场景内部 RPC、微服务开放平台、遗留系统移动端、IoT、新项目
一句话建议:如果你能控制两端密钥,用 HMAC 省心省力;如果暴露给第三方,用 ECDSA 更优雅。别迷信 RSA,它真的老了。