返回知识库中心
技术方案

SM3哈希链:构建不可篡改审计日志的国密方案

用国密 SM3 算法构建防篡改审计日志系统,满足等保 2.0 三级和 DA/T 94-2022 标准要求。

数凭电子会计档案··技术方案

SM3哈希链:构建不可篡改审计日志的国密方案

用国密 SM3 算法构建防篡改审计日志系统,满足等保 2.0 三级和 DA/T 94-2022 标准要求。


摘要

在信息安全等级保护(等保 2.0)三级和电子会计档案管理规范(DA/T 94-2022)的要求下,企业必须确保审计日志的完整性和不可篡改性。本文介绍基于国密 SM3 算法的哈希链技术方案。


一、为什么需要防篡改审计日志?

1.1 合规要求

标准规范核心要求
等保 2.0 三级审计记录应包括日期、时间、类型、主体标识,记录应保护免受删除、修改或覆盖
DA/T 94-2022电子会计档案管理系统应建立审计机制,记录应具有防篡改保护
GB/T 39784-2021审计员应能独立查看审计日志,系统应确保日志不可篡改

1.2 传统方案的问题

传统审计日志存储在数据库中,存在以下风险:

  • ❌ 数据库管理员可直接修改日志表
  • ❌ SQL 注入攻击可能篡改历史记录
  • ❌ 没有技术手段检测日志是否被修改
  • ❌ 审计证据缺乏法律效力

二、SM3 哈希链原理

2.1 核心思想

将每条日志与前一条日志的哈希值关联,形成链式结构。

日志1 → SM3(内容1 + NULL) → Hash1
                    ↓
日志2 → SM3(内容2 + Hash1) → Hash2
                    ↓
日志3 → SM3(内容3 + Hash2) → Hash3
                    ↓
日志N → SM3(内容N + HashN-1) → HashN

2.2 防篡改机制

如果篡改者修改了任意一条日志:

场景:修改日志2的内容
┌─────────────────────────────────────┐
│ 日志1: Hash1 = SM3(内容1 + NULL)    │ → 不变
│ 日志2: Hash2 = SM3(新内容2 + Hash1) │ → 被修改,但存储的 Hash2 值不变!
│ 日志3: Hash3 = SM3(内容3 + Hash2)    │ → 验证失败!
└─────────────────────────────────────┘

验证:从日志1开始重新计算,发现日志3的哈希不匹配

2.3 SM3 算法优势

特性说明
国密标准GM/T 0004-2012,国家密码管理局发布
安全性输出 256 位哈希值,抗碰撞性强
性能比 SHA-3 更快,硬件支持广泛
合规性满足信创环境要求

三、数据结构设计

3.1 日志表结构

CREATE TABLE sys_audit_log (
    id BIGSERIAL PRIMARY KEY,
    content JSONB NOT NULL,              -- 日志内容
    log_hash VARCHAR(64) NOT NULL,       -- 当前日志的SM3哈希
    prev_log_hash VARCHAR(64),           -- 前一条日志的哈希
    timestamp TIMESTAMP NOT NULL,        -- 时间戳
    operator VARCHAR(50),                -- 操作人
    operation VARCHAR(20),               -- 操作类型
    resource_type VARCHAR(50),           -- 资源类型
    resource_id VARCHAR(100),            -- 资源ID
    ip_address VARCHAR(45),              -- IP地址
    created_at TIMESTAMP DEFAULT NOW(),
    CONSTRAINT uk_audit_log_hash UNIQUE (log_hash)
);

-- 索引
CREATE INDEX idx_audit_log_timestamp ON sys_audit_log(timestamp);
CREATE INDEX idx_audit_log_operator ON sys_audit_log(operator);

3.2 日志内容示例

{
  "operator": "admin",
  "operation": "LOGIN",
  "resourceType": "USER",
  "resourceId": "user001",
  "ipAddress": "192.168.1.100",
  "userAgent": "Mozilla/5.0...",
  "result": "SUCCESS",
  "timestamp": "2026-03-22T10:30:00Z"
}

四、代码实现

4.1 SM3 哈希工具类

import org.bouncycastle.crypto.digests.SM3Digest;
import java.nio.charset.StandardCharsets;

public class Sm3Utils {

    /**
     * 计算字符串的 SM3 哈希值
     */
    public static String hash(String input) {
        SM3Digest digest = new SM3Digest();
        byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8);
        digest.update(inputBytes, 0, inputBytes.length);

        byte[] hashBytes = new byte[digest.getDigestSize()];
        digest.doFinal(hashBytes, 0);

        return bytesToHex(hashBytes);
    }

    /**
     * 计算日志哈希(内容 + 前序哈希)
     */
    public static String hashLog(String content, String prevHash) {
        String input = content + (prevHash != null ? prevHash : "");
        return hash(input);
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}

4.2 审计日志服务

@Service
public class AuditLogService {

    @Autowired
    private AuditLogRepository repository;

    /**
     * 记录审计日志
     */
    public AuditLog record(String content) {
        // 获取前一条日志的哈希
        String prevHash = getLastLogHash();

        // 计算当前日志哈希
        String currentHash = Sm3Utils.hashLog(content, prevHash);

        // 创建日志记录
        AuditLog log = new AuditLog();
        log.setContent(content);
        log.setLogHash(currentHash);
        log.setPrevLogHash(prevHash);
        log.setTimestamp(LocalDateTime.now());

        return repository.save(log);
    }

    /**
     * 验证哈希链完整性
     */
    public boolean verifyChain(LocalDateTime start, LocalDateTime end) {
        List<AuditLog> logs = repository.findByTimestampBetweenOrderByTimestamp(start, end);

        String prevHash = null;
        for (AuditLog log : logs) {
            String expectedHash = Sm3Utils.hashLog(log.getContent(), prevHash);
            if (!expectedHash.equals(log.getLogHash())) {
                return false;
            }
            prevHash = expectedHash;
        }
        return true;
    }
}

五、验证与监控

5.1 定期验证任务

@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点
public void scheduledVerification() {
    LocalDateTime now = LocalDateTime.now();
    LocalDateTime yesterday = now.minusDays(1);

    boolean isValid = auditLogService.verifyChain(yesterday, now);

    if (!isValid) {
        alertService.sendAlert("⚠️ 审计日志哈希链验证失败,可能存在篡改!");
    }
}

5.2 日志备份

public void exportBackup(LocalDate date) {
    List<AuditLog> logs = auditLogService.getByDate(date);

    BackupFile backup = new BackupFile();
    backup.setLogs(logs);
    backup.setExportTime(LocalDateTime.now());
    backup.setVerificationResult(
        auditLogService.verifyChain(date.atStartOfDay(), date.plusDays(1).atStartOfDay())
    );

    // 签名备份文件
    String signature = signBackup(backup);
    backup.setSignature(signature);

    // 保存到备份位置
    backupStorage.save(date.toString() + ".json", backup.toJson());
}

六、最佳实践

6.1 安全建议

措施说明
数据库权限审计日志表只允许 INSERT,禁止 UPDATE/DELETE
定期备份每日自动备份,备份文件单独签名保存
多级存储热数据(在线)、温数据(归档)、冷数据(离线)
审计员独立审计员角色独立,只能查看不能修改

6.2 性能优化

// 使用批量插入提高性能
@Transactional
public void batchRecord(List<String> contents) {
    String prevHash = getLastLogHash();
    List<AuditLog> logs = new ArrayList<>();

    for (String content : contents) {
        String currentHash = Sm3Utils.hashLog(content, prevHash);
        AuditLog log = new AuditLog();
        log.setContent(content);
        log.setLogHash(currentHash);
        log.setPrevLogHash(prevHash);
        logs.add(log);
        prevHash = currentHash;
    }

    repository.saveAll(logs);
}

七、总结

SM3 哈希链技术是实现审计日志防篡改的有效方案:

特点说明
不可篡改任意修改都会破坏哈希链,立即被发现
可验证任何人都可以验证日志完整性
国密算法符合信创环境要求
实现简单核心代码不到 100 行

在等保 2.0 三级和 DA/T 94-2022 的要求下,企业应采用此类技术确保审计日志的完整性和可信度。


相关阅读

了解更多申请试用 | 预约演示

相关推荐

想要了解 DigiVoucher 如何助您实现合规归档?

我们的专家团队可为您提供定制化的电子会计档案单套制实施建议。