首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 實作 PDF 檔案完整性驗證:基於 MD5 與檔尾附加的輕量方案

     編輯:關於C#

需求背景

專案要求使用者掃描發票產生 PDF 上傳,伺服端需驗證檔案自產生後未被竄改。標準數位簽章/浮水印方案較複雜,尋求輕量替代方案。

核心原理

  1. 客戶端(掃描程式):計算 PDF 檔案完整內容的 MD5 雜湊,將 32 字元十六進位字串附加於檔案末尾,寫入磁碟。PDF 閱讀器忽略檔尾多餘資料,正常開啟無提示。
  2. 伺服端:讀取上傳檔案,排除最後 32 位元組計算 MD5,與檔尾儲存的 MD5 比對。一致則通過驗證,否則拒絕。
  3. 加入 金鑰字串 混雜計算(MD5(MD5(file) + key)),防止第三方偽造雜湊。

完整實作程式碼


using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public static class PdfIntegrity
{
    private const int HashLength = 32; // MD5 輸出 32 字元

    /// 計算位元組陣列 MD5,回傳 32 字元大寫十六進位字串
    private static string ComputeMd5(byte[] buffer, int offset, int count)
    {
        using (MD5 md5 = MD5.Create())
        {
            byte[] hash = md5.ComputeHash(buffer, offset, count);
            return BitConverter.ToString(hash).Replace("-", "");
        }
    }

    /// 對字串計算 MD5
    private static string ComputeMd5(string s) => ComputeMd5(Encoding.ASCII.GetBytes(s), 0, s.Length);

    /// 客戶端:為 PDF 附加完整性標籤
    /// PDF 檔案路徑
    /// 金鑰字串(雙方共享)
    /// 計算出的標籤值
    public static string TagFile(string path, string key)
    {
        try
        {
            byte[] fileBytes = File.ReadAllBytes(path);
            string hash = ComputeMd5(fileBytes, 0, fileBytes.Length);
            hash = ComputeMd5(hash + key); // 混入金鑰

            byte[] tagBytes = Encoding.ASCII.GetBytes(hash);
            using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite))
            {
                fs.Write(fileBytes, 0, fileBytes.Length); // 重寫原內容
                fs.Write(tagBytes, 0, tagBytes.Length);   // 附加標籤
            }
            return hash;
        }
        catch (Exception ex) { return ex.ToString(); }
    }

    /// 伺服端:驗證 PDF 完整性
    public static bool VerifyFile(string path, string key)
    {
        try
        {
            byte[] fileBytes = File.ReadAllBytes(path);
            if (fileBytes.Length < HashLength) return false;

            int contentLen = fileBytes.Length - HashLength;
            string calcHash = ComputeMd5(fileBytes, 0, contentLen);
            calcHash = ComputeMd5(calcHash + key);

            string storedHash = Encoding.ASCII.GetString(fileBytes, contentLen, HashLength);
            return calcHash == storedHash;
        }
        catch { return false; }
    }
}

使用範例


const string SecretKey = "MyApp$2024!"; // 雙方約定金鑰

// 掃描端產生 PDF 後
string tag = PdfIntegrity.TagFile(@"C:\Scans\invoice.pdf", SecretKey);
Console.WriteLine($"標籤已寫入: {tag}");

// 伺服端接收上傳後
bool ok = PdfIntegrity.VerifyFile(@"C:\Uploads\invoice.pdf", SecretKey);
Console.WriteLine(ok ? "驗證通過" : "檔案已竄改或非本系統產生");

適用範圍與限制

  • 適用於附加尾端資料不影響格式解析的二進位檔案(PDF、影像、壓縮檔等)。
  • 若檔案格式對尾端資料敏感(如某些執行檔、簽章過的 PDF),需改用標準數位簽章。
  • 金鑰必須妥善保管,洩漏將導致偽造風險。
  • MD5 雖不再適合密碼學強度需求,但完整性檢查配合金鑰在非敵對環境足夠。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved