JSON数据如何做数字签名:原理、步骤与最佳实践
在分布式系统、API通信、数据交换等场景中,JSON数据因其轻量级、易读性强被广泛应用,JSON数据在传输或存储过程中可能面临篡改、伪造等安全风险,数字签名作为一种核心的加密技术,能够确保数据的完整性(未被修改)和认证性(来源可信),是保障JSON数据安全的重要手段,本文将详细介绍JSON数据数字签名的原理、具体步骤、常见算法及最佳实践。
数字签名的基本原理
数字签名本质上是“非对称加密”与“哈希算法”的结合,其核心目标是实现“防伪”和“防篡改”,其原理流程如下:
- 哈希摘要:发送方对原始JSON数据计算一个固定长度的哈希值(,摘要具有“唯一性”(任何数据修改都会导致摘要变化)和“不可逆性”。
- 私钥加密:发送方使用自己的私钥对哈希摘要进行加密,生成“数字签名”。
- 数据传输:将原始JSON数据和数字签名一同发送给接收方。
- 验证过程:
- 接收方使用发送方的公钥解密数字签名,得到原始哈希摘要;
- 对接收到的JSON数据重新计算哈希摘要;
- 比较两个哈希值:若一致,说明数据未被篡改且来源可信;若不一致,则数据可能被修改或签名伪造。
JSON数据数字签名的具体步骤
1 准备工作:生成密钥对
数字签名依赖非对称加密算法(如RSA、ECDSA),需提前生成密钥对:
- 私钥(Private Key):由发送方保密存储,用于生成签名;
- 公钥(Public Key):由发送方公开,供接收方验证签名。
示例:使用OpenSSL生成RSA密钥对
# 生成2048位的RSA私钥(PEM格式) openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048 # 从私钥提取公钥 openssl rsa -pubout -in private_key.pem -out public_key.pem
2 步骤1:对JSON数据规范化(可选但推荐)
JSON数据可能因格式差异(如空格、键顺序不同)导致相同内容生成不同哈希值,影响签名验证,建议对JSON进行规范化处理:
- 排序键:按字典序对JSON对象的键进行排序;
- 统一格式:去除多余空格、换行符,确保字符串、数字等格式一致。
示例:原始JSON与规范化后的JSON
// 原始JSON(键无序,有空格)
{
"name": "Alice",
"age": 25,
"city": "New York"
}
// 规范化后JSON(键有序,无多余空格)
{"age":25,"city":"New York","name":"Alice"}
3 步骤2:计算JSON数据的哈希摘要
选择安全的哈希算法(如SHA-256、SHA-384),对规范化后的JSON数据计算哈希摘要。
示例:使用Python计算SHA-256摘要
import json
import hashlib
# 原始JSON数据
json_data = {"name": "Alice", "age": 25, "city": "New York"}
# 规范化JSON(键排序)
normalized_json = json.dumps(json_data, sort_keys=True)
# 计算SHA-256哈希摘要
hash_digest = hashlib.sha256(normalized_json.encode('utf-8')).hexdigest()
print("哈希摘要:", hash_digest) # 输出: 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
4 步骤3:使用私钥生成数字签名
用非对称加密算法(如RSA、ECDSA)对哈希摘要进行加密,生成签名。
示例:使用Python的cryptography库生成RSA签名
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.backends import default_backend
# 加载私钥(假设private_key.pem已存在)
with open("private_key.pem", "rb") as f:
private_key = load_pem_private_key(f.read(), password=None, backend=default_backend())
# 生成签名(RSA-SHA256)
signature = private_key.sign(
normalized_json.encode('utf-8'),
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("数字签名:", signature.hex()) # 输出16进制签名
5 步骤4:传输JSON数据与签名
将原始JSON数据、数字签名及公钥(或公钥ID)一同发送给接收方,实际场景中,可通过HTTP头、JSON字段或独立传输层携带签名。
示例:带签名的JSON数据结构
{
"data": {"name": "Alice", "age": 25, "city": "New York"},
"signature": "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069...",
"public_key_id": "rsa_2048_v1" // 可选,标识公钥
}
6 步骤5:验证数字签名
接收方通过以下步骤验证签名:
- 获取公钥:根据
public_key_id或约定方式获取发送方的公钥; - 重新计算哈希:对接收到的JSON数据同样进行规范化并计算哈希摘要;
- 解密签名:用公钥解密数字签名,得到发送方的原始哈希摘要;
- 比较哈希:若两个哈希值一致,验证通过;否则,拒绝数据。
示例:使用Python验证签名
from cryptography.hazmat.primitives.serialization import load_pem_public_key
# 加载公钥
with open("public_key.pem", "rb") as f:
public_key = load_pem_public_key(f.read(), backend=default_backend())
# 验证签名
try:
public_key.verify(
signature,
normalized_json.encode('utf-8'),
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("签名验证成功: 数据完整且来源可信")
except Exception as e:
print("签名验证失败:", e)
常见算法与工具选择
1 哈希算法
| 算法 | 输出长度 | 安全性 | 适用场景 |
|---|---|---|---|
| SHA-256 | 256位 | 高 | 当前主流推荐 |
| SHA-384 | 384位 | 更高 | 高安全性需求 |
| SHA-1 | 160位 | 已不安全 | 不推荐使用 |
2 非对称加密算法
| 算法 | 密钥长度 | 速度 | 安全性 | 适用场景 |
|---|---|---|---|---|
| RSA | 2048位+ | 较慢 | 高 | 通用场景 |
| ECDSA | 256位+ | 快速 | 高 | 移动端、物联网等资源受限场景 |
| EdDSA | 256位+ | 最快 | 高 | 现代化应用(如Web3) |
3 工具与库
- Python:
cryptography、PyJWT(JWT自带签名功能); - Java:
java.security包(Signature类); - Node.js:
crypto模块; - 命令行:
openssl(生成密钥、签名命令)。
最佳实践
1 优先使用标准化格式
-
JWT(JSON Web Token):若场景允许,直接使用JWT(Header.Payload.Signature),其内置了对JSON数据的签名机制,支持RS256、ES256等算法,无需手动处理签名流程。
import jwt import datetime # 生成JWT(含签名) token = jwt.encode( {"name": "Alice", "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)}, private_key, algorithm



还没有评论,来说两句吧...