專案要求使用者掃描發票產生 PDF 上傳,伺服端需驗證檔案自產生後未被竄改。標準數位簽章/浮水印方案較複雜,尋求輕量替代方案。
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 ? "驗證通過" : "檔案已竄改或非本系統產生");