本文目录导读:

- 核心思路
- 方案一:批量验证 OpenPGP 签名(如 .asc /.sig 文件)
- 方案二:批量验证 HMAC-SHA256 签名(如 API 回调/文件完整性)
- 方案三:批量验证 JWT 签名(如 API 测试/日志分析)
- 方案四:使用 OpenSSL 批量验证数字签名(.sig / .p7s)
- 总结与建议
可以的,实用脚本完全可以实现批量验签功能。
批量验签是开发、运维和安全审计中非常常见的需求,比如验证一批下载文件的完整性、批量校验API回调的签名等。
下面提供几种常见的脚本实现方式和示例,你可以根据你的应用场景(如文件校验、API签名、JWT等)选择合适的方案。
核心思路
- 遍历文件或数据列表。
- 读取签名和原始数据,签名可能存储在单独的文件(
.sig)、文件末尾(如某些PGP签名)、或一个清单文件中。 - 执行验签命令。
- 汇总结果。
批量验证 OpenPGP 签名(如 .asc /.sig 文件)
这是验证下载的软件包(如Linux ISO、数据库驱动)最常见的情况。
场景: 你有一个文件夹,里面既有 file.zip,又有对应的 file.zip.asc,需要验证所有文件。
脚本:batch_gpg_verify.sh
#!/bin/bash
# 批量验证当前目录下所有 .zip.asc 签名文件
# 需要提前导入公钥
SIGNED_FILE_EXT=".zip.asc"
PUBLIC_KEY_FILE="./public-key.asc" # 可选,如果没有导入到keyring
echo "开始批量验签..."
# 先导入公钥(如果提供了)
if [ -f "$PUBLIC_KEY_FILE" ]; then
echo "导入公钥..."
gpg --import "$PUBLIC_KEY_FILE"
fi
FAIL_COUNT=0
PASS_COUNT=0
for sig_file in *"$SIGNED_FILE_EXT"; do
# 获取原始文件名(去掉 .asc 后缀)
original_file="${sig_file%.asc}"
if [ ! -f "$original_file" ]; then
echo "[跳过] 找不到原始文件: $original_file"
continue
fi
echo -n "验证: $original_file ... "
# 执行验签
gpg --verify "$sig_file" "$original_file" 2>/dev/null
if [ $? -eq 0 ]; then
echo "✅ 通过"
PASS_COUNT=$((PASS_COUNT + 1))
else
echo "❌ 失败! (签名无效或公钥缺失)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done
echo ""
echo "========= 汇总 ========="
echo "通过: $PASS_COUNT"
echo "失败: $FAIL_COUNT"
exit $FAIL_COUNT
运行:
chmod +x batch_gpg_verify.sh ./batch_gpg_verify.sh
批量验证 HMAC-SHA256 签名(如 API 回调/文件完整性)
场景: 你有多个文件,以及一个包含文件名和签名的CSV文件(或计算后追加了签名)。
脚本:batch_hmac_verify.py (Python,更灵活)
假设有一个清单文件 files.csv 内容如下:
filename,expected_signature
data_1.csv,abc123def456...
data_2.csv,789ghi012jkl...
#!/usr/bin/env python3
import csv
import hmac
import hashlib
import sys
import os
# 你的密钥(请替换为实际密钥)
SECRET_KEY = b"your_secret_key_here"
def calculate_hmac(filepath):
"""计算文件的 HMAC-SHA256"""
h = hmac.new(SECRET_KEY, digestmod=hashlib.sha256)
with open(filepath, 'rb') as f:
# 对于大文件,分块读取
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
def main():
manifest_file = sys.argv[1] # CSV清单文件路径
base_dir = os.path.dirname(manifest_file) or '.'
pass_count = 0
fail_count = 0
with open(manifest_file, 'r') as f:
reader = csv.DictReader(f)
for row in reader:
filename = row['filename']
expected_sig = row['expected_signature'].strip()
filepath = os.path.join(base_dir, filename)
if not os.path.exists(filepath):
print(f"⚠️ 文件不存在: {filename}")
fail_count += 1
continue
calculated_sig = calculate_hmac(filepath)
# 使用 hmac.compare_digest 防止计时攻击
if hmac.compare_digest(calculated_sig, expected_sig):
print(f"✅ {filename} 验签通过")
pass_count += 1
else:
print(f"❌ {filename} 验签失败 (期望: {expected_sig[:10]}..., 计算: {calculated_sig[:10]}...)")
fail_count += 1
print(f"\n结果: 通过 {pass_count}, 失败 {fail_count}")
return fail_count
if __name__ == "__main__":
sys.exit(main())
运行:
python3 batch_hmac_verify.py files.csv
批量验证 JWT 签名(如 API 测试/日志分析)
场景: 你有一个文本文件,每行是一个JWT字符串,需要验证其签名是否有效。
脚本:batch_jwt_verify.py (使用 PyJWT)
#!/usr/bin/env python3
import jwt
import sys
# 你的验证密钥(对称算法用 secret,非对称用 public key)
SECRET = "your_256_bit_secret"
PUBLIC_KEY = None # 如用 RS256,加载公钥
def verify_jwt(token):
"""验证单个JWT"""
try:
# 对于 HS256 (对称)
decoded = jwt.decode(token, SECRET, algorithms=["HS256"])
return True, decoded
except jwt.ExpiredSignatureError:
return False, "Token已过期"
except jwt.InvalidTokenError as e:
return False, f"无效签名: {e}"
# 对于 RS256 (非对称)
# decoded = jwt.decode(token, PUBLIC_KEY, algorithms=["RS256"])
def main():
if len(sys.argv) < 2:
print("用法: python3 batch_jwt_verify.py <tokens.txt>")
sys.exit(1)
input_file = sys.argv[1]
pass_count = 0
fail_count = 0
with open(input_file, 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
token = line.strip()
if not token:
continue
is_valid, result = verify_jwt(token)
if is_valid:
print(f"行{line_num}: ✅ 验证通过. Payload: {result}")
pass_count += 1
else:
print(f"行{line_num}: ❌ 验证失败 - {result}")
fail_count += 1
print(f"\n汇总: 通过 {pass_count}, 失败 {fail_count}")
sys.exit(fail_count)
if __name__ == "__main__":
main()
运行:
pip install pyjwt python3 batch_jwt_verify.py collected_tokens.txt
使用 OpenSSL 批量验证数字签名(.sig / .p7s)
场景: 验证由私钥签名的文件(如软件发布签名)。
脚本:batch_openssl_verify.sh
假设签名的文件格式为 file.bin.sig (原始签名)和 file.bin。
#!/bin/bash
# 批量验证 OpenSSL dgst 签名
# 签名生成时的命令: openssl dgst -sha256 -sign private.pem -out file.bin.sig file.bin
PUBLIC_KEY="public.pem"
SIG_DIR="./sigs" # 签名文件存放目录
DATA_DIR="./data" # 原始数据存放目录
echo "使用公钥: $PUBLIC_KEY"
echo "开始批量验签..."
FAIL_COUNT=0
PASS_COUNT=0
for sig_file in "$SIG_DIR"/*.sig; do
base_name=$(basename "$sig_file" .sig)
data_file="$DATA_DIR/$base_name"
if [ ! -f "$data_file" ]; then
echo "[跳过] 找不到数据文件: $data_file"
continue
fi
echo -n "验证: $base_name ... "
openssl dgst -sha256 -verify "$PUBLIC_KEY" -signature "$sig_file" "$data_file" 2>/dev/null
if [ $? -eq 0 ]; then
echo "✅"
PASS_COUNT=$((PASS_COUNT + 1))
else
echo "❌"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done
echo ""
echo "通过: $PASS_COUNT, 失败: $FAIL_COUNT"
exit $FAIL_COUNT
总结与建议
| 场景 | 推荐工具 | 速度 | 适用性 |
|---|---|---|---|
| PGP/GPG 签名验证 | Shell + gpg |
中 | 软件发布包、邮件加密 |
| HMAC/对称完整性校验 | Python + hmac |
快 | API签名、内部文件校验 |
| JWT 验证 | Python + pyjwt |
快 | Web API测试、令牌分析 |
| 非对称签名 (OpenSSL) | Shell + openssl |
中 | 数字证书、软件签名 |
| PKCS#7 / 数字信封 | Shell + openssl smime |
慢 | Windows二进制签名 |
几点实用建议:
- 多线程/多进程:如果需要验证成千上万的文件,建议在Python脚本中引入
concurrent.futures来并行处理,可以显著提升速度。 - 错误处理:一定要处理文件不存在、签名格式错误等情况,避免整个脚本中断。
- 日志输出:建议将失败的文件名和错误信息输出到另一个文件(如
verify_fail.log),方便后续排查。 - 密钥管理:对于HMAC场景,不要在脚本中硬编码密钥(尤其是提交到Git时),推荐使用环境变量或密钥管理服务(如KMS、Vault)。
是的,实用脚本完全可以高效地实现批量验签,根据你的具体场景(文件签名类型、性能要求、环境限制),选择上述合适的脚本方案即可,如果需要进一步定制(比如针对某种特定格式的签名),可以再提供具体细节。