數位簽章原理
- 發送端:對檔案計算雜湊值 (MD5/SHA1) → 以私鑰加密雜湊值產生簽章 → 傳送檔案 + 簽章。
- 接收端:對收到檔案計算雜湊值 → 取得發送者公鑰 → 驗證簽章 (解密簽章比對雜湊值)。
- 若檔案被竄改,雜湊值不同,驗證失敗。
CryptoAPI 關鍵函式
| 函式 | 用途 |
CryptAcquireContext | 取得 CSP Handle |
CryptCreateHash | 建立雜湊物件 (CALG_MD5/SHA1) |
CryptHashData | 累加檔案資料計算雜湊 |
CryptSignHash | 以私鑰 (AT_SIGNATURE) 加密雜湊值產生簽章 |
CryptGetUserKey | 取得公鑰 Handle (AT_SIGNATURE) |
CryptVerifySignature | 驗證簽章 |
建立檔案簽章 (C++Builder 程式碼)
// 變數宣告
HCRYPTPROV hProv;
HCRYPTHASH hHash;
const int BUF_SIZE = 4096;
BYTE pbBuffer[BUF_SIZE];
BYTE pbSignature[256];
DWORD dwSigLen = 256;
TFileStream* fs = new TFileStream("source.dat", fmOpenRead);
// 1. 連接預設 CSP
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) Error("CSP 連接失敗");
// 2. 建立 MD5 雜湊
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) Error("建立雜湊失敗");
// 3. 讀取檔案並累加雜湊
DWORD dwRead;
do {
dwRead = fs->Read(pbBuffer, BUF_SIZE);
if (!CryptHashData(hHash, pbBuffer, dwRead, 0)) Error("雜湊計算失敗");
} while (dwRead == BUF_SIZE);
// 4. 以私鑰簽署雜湊值
if (!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbSignature, &dwSigLen))
Error("簽章失敗");
// 5. 將 pbSignature (長度 dwSigLen) 另存為 .sig 檔案
// ...
驗證檔案簽章
// 變數宣告 (同建立簽章)
HCRYPTKEY hPubKey;
// ... 連接 CSP、建立雜湊、讀取檔案計算雜湊 (同上) ...
// 取得公鑰
if (!CryptGetUserKey(hProv, AT_SIGNATURE, &hPubKey)) Error("取得公鑰失敗");
// 讀取 .sig 檔案至 pbSignature, dwSigLen
// ...
// 驗證
if (!CryptVerifySignature(hHash, pbSignature, dwSigLen, hPubKey, NULL, 0)) {
if (GetLastError() == NTE_BAD_SIGNATURE)
ShowMessage("檔案已被修改!");
else
ShowMessage("驗證過程發生錯誤");
} else {
ShowMessage("檔案完整,未被竄改。");
}
實務建議
- 簽章資料可獨立儲存 (.sig 檔) 或附加於檔案尾端 (需自定格式)。
- 生產環境建議使用 SHA-256 (
CALG_SHA_256) 取代 MD5。
- 私鑰保護:啟用 CSP 使用者保護旗標、考慮硬體安全模組 (HSM)。
- 錯誤處理:每個 API 檢查回傳值,
GetLastError() 取得詳細碼。