JSON数据数字签名:原理、步骤与实践指南
在数字化时代,JSON(JavaScript Object Notation)因其轻量、易读、易解析的特性,成为数据交换的主流格式之一,JSON数据的明文传输或存储存在篡改、伪造风险——恶意攻击者可能修改交易金额、篡改用户权限信息,导致严重的安全问题,数字签名技术通过“非对称加密+哈希算法”的组合,为JSON数据提供了完整性验证和身份认证能力,确保数据在传输或存储过程中未被篡改,且来源可信,本文将详细介绍JSON数据数字签名的原理、具体步骤及实践注意事项。
数字签名:保障数据可信的“数字印章”
数字签名本质上是一种“加密的校验码”,它结合了哈希算法和非对称加密算法的核心功能:
- 哈希算法(如SHA-256、SHA-3):将任意长度的JSON数据压缩为固定长度的“哈希值”(也称“),确保数据任何细微改动都会导致哈希值完全不同(抗碰撞性)。
- 非对称加密算法(如RSA、ECDSA、EdDSA):使用公钥和私钥对,私钥由签名方保密,用于对哈希值加密生成“签名”;公钥由验证方公开,用于解密签名并还原哈希值,与重新计算的哈希值比对。
通过数字签名,JSON数据的接收方可以验证:
- 完整性:数据在传输/存储中未被篡改(哈希值匹配);
- 真实性:签名方确实拥有对应的私钥(签名能被公钥解密);
- 不可否认性:签名方无法否认对数据的签名(私钥仅由其持有)。
JSON数据数字签名的完整步骤
步骤1:准备JSON数据与密钥对
明确需要签名的JSON数据(如订单信息、用户凭证等),并生成一对非对称密钥(公钥+私钥),密钥生成方式取决于所选算法:
- RSA:可通过OpenSSL、Java KeyStore、Python的
cryptography库生成(如2048位或3072位密钥); - ECDSA:推荐使用P-256或P-384曲线,密钥更短且签名效率更高(适合移动端或物联网设备);
- EdDSA(如Ed25519):更现代的算法,签名速度快、安全性高,逐渐成为主流。
示例JSON数据:
{
"userId": "1001",
"amount": 1000.00,
"currency": "CNY",
"timestamp": "2023-10-01T12:00:00Z"
}
步骤2:计算JSON数据的哈希值
由于JSON数据可能存在格式差异(如空格、键顺序不同),导致相同内容生成不同哈希值,需先对JSON进行“规范化”(Canonicalization)处理,确保格式统一,常见的规范化方式包括:
- 键排序:按字典序对JSON对象的键进行排序(如
"amount"、"currency"、"timestamp"、"userId"); - 值格式化:数字、字符串、布尔值等按标准格式输出(如字符串加双引号、数字不补零);
- 空白符处理:移除所有不必要的空格、换行、制表符。
规范化后的JSON(示例):
{"amount":1000.00,"currency":"CNY","timestamp":"2023-10-01T12:00:00Z","userId":"1001"}
对规范化后的JSON字符串应用哈希算法(如SHA-256),生成固定长度的哈希值(256位,64个十六进制字符):
echo -n '{"amount":1000.00,"currency":"CNY","timestamp":"2023-10-01T12:00:00Z","userId":"1001"}' | sha256sum
# 输出:a1b2c3d4...(示例哈希值)
步骤3:使用私钥对哈希值进行签名
将步骤2生成的哈希值,通过签名方的私钥进行加密,生成数字签名,签名算法需与哈希算法匹配(如RSA-SHA256、ECDSA-SHA256)。
示例(Python使用cryptography库):
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend
import json
# 1. 加载私钥(假设已生成并保存为pem格式)
with open("private_key.pem", "rb") as f:
private_key = serialization.load_pem_private_key(
f.read(),
password=None,
backend=default_backend()
)
# 2. 规范化JSON并计算哈希
data = {"userId": "1001", "amount": 1000.00, "currency": "CNY", "timestamp": "2023-10-01T12:00:00Z"}
canonical_json = json.dumps(data, sort_keys=True, separators= (',', ':')) # 规范化
hash_value = hashes.Hash(hashes.SHA256(), backend=default_backend())
hash_value.update(canonical_json.encode('utf-8'))
digest = hash_value.finalize()
# 3. 签名
signature = private_key.sign(
digest,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
# 4. 将签名转为Base64(方便传输)
import base64
signature_b64 = base64.b64encode(signature).decode('utf-8')
print("Signature:", signature_b64)
步骤4:将签名与JSON数据一同传输/存储
签名本身是二进制数据,需编码为Base64或Hex等文本格式,与原始JSON数据绑定,常见的存储方式有两种:
方式1:嵌入JSON字段(推荐)
将签名作为JSON的一个新字段(如"signature"),与原始数据一起传输:
{
"data": {
"userId": "1001",
"amount": 1000.00,
"currency": "CNY",
"timestamp": "2023-10-01T12:00:00Z"
},
"signature": "MEUCIQC...(Base64编码的签名)",
"algorithm": "RSA-SHA256" // 可选:标注签名算法
}
方式2:独立存储(如HTTP头、文件附加)
若JSON数据结构固定,也可将签名存储在独立位置(如HTTP请求头X-Signature、附加文件),但需确保签名与数据的对应关系明确。
步骤5:验证签名(接收方操作)
接收方收到JSON数据及签名后,需执行以下步骤验证数据有效性:
- 提取签名与数据:从JSON中解析出原始数据(
data字段)和签名(signature字段),并获取公钥(可通过证书、预共享等方式获取)。 - 规范化数据并计算哈希:与签名方相同的规范化方式处理JSON,重新计算哈希值。
- 用公钥解密签名:通过公钥对签名进行解密(或验证签名有效性),还原出哈希值。
- 比对哈希值:将解密后的哈希值与重新计算的哈希值比对,若一致则验证通过,否则数据被篡改。
示例(Python验证):
# 1. 加载公钥
with open("public_key.pem", "rb") as f:
public_key = serialization.load_pem_public_key(
f.read(),
backend=default_backend()
)
# 2. 解析JSON与签名
received_data = {
"data": {
"userId": "1001",
"amount": 1000.00,
"currency": "CNY",
"timestamp": "2023-10-01T12:00:00Z"
},
"signature": "MEUCIQC...(Base64签名)"
}
signature_bytes = base64.b64decode(received_data["signature"].encode('utf-8'))
# 3. 规范化数据并计算哈希
canonical_json = json.dumps(received_data["data"], sort_keys=True, separators=(',', ':'))
hash_value = hashes.Hash(hashes.SHA256(), backend=default_backend())
hash_value.update(canonical_json.encode('utf-8'))
digest = hash_value.finalize()
# 4. 验证签名
try:
public_key.verify(
signature_bytes,
digest,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("Signature verified: Data is authentic and untampered.")
except Exception as


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