• 方案介紹
  • 附件下載
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

計(jì)算文件MD5值,判斷唯一性(win32-API)

7小時前
161
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

更多詳細(xì)資料請聯(lián)系.docx

共1個文件

一、前言

在當(dāng)今數(shù)字化世界中,數(shù)據(jù)的完整性和安全性變得日益重要。MD5(Message-Digest Algorithm 5)作為一種被廣泛接受的密碼散列函數(shù),它能夠生成一個固定長度的128位(16字節(jié))散列值,也稱為摘要,用于確保信息傳輸?shù)耐暾恢?。MD5算法的特點(diǎn)在于它能夠?qū)⑷我忾L度的信息轉(zhuǎn)換為一個固定長度的輸出,這個過程是單向的,即很難從散列值反推原始信息,同時即使是微小的變化也會導(dǎo)致完全不同的散列值,這使得MD5成為檢測文件篡改和數(shù)據(jù)一致性驗(yàn)證的有力工具。

在Windows環(huán)境下,尤其是在Win32 API中,計(jì)算文件的MD5值是判斷文件唯一性的一種常見做法。這是因?yàn)镸D5值可以視為文件的一個“指紋”,只要文件內(nèi)容有任何變化,其MD5值就會改變,從而可以迅速識別出文件是否被修改過。這一特性在軟件分發(fā)、數(shù)據(jù)備份、數(shù)字簽名以及密碼存儲等場景中尤為重要。例如,軟件開發(fā)者在分發(fā)軟件包時,會提供相應(yīng)的MD5值,用戶下載后可以計(jì)算下載文件的MD5并與官方提供的值進(jìn)行對比,以確認(rèn)下載文件未被篡改。

實(shí)現(xiàn)文件MD5值的計(jì)算在C語言中可以通過調(diào)用Windows的CryptoAPI來完成。CryptoAPI提供了加密和散列功能,其中包括MD5算法的實(shí)現(xiàn)。

下面是一個基于Win32 API的C語言示例,展示如何讀取文件并計(jì)算其MD5值:

#include <windows.h>
#include <stdio.h>
#include <wincrypt.h>

void print_hex(const BYTE *data, DWORD len) {
    for (DWORD i = 0; i < len; ++i) {
        printf("%02X", data[i]);
    }
}

int main() {
    HANDLE hFile;
    HCRYPTPROV hCryptProv;
    HCRYPTHASH hHash;
    DWORD dwDataLen;
    BYTE *pbData;
    DWORD dwHashSize = 0;
    BYTE *pbHash;

    // 打開文件
    hFile = CreateFile("testfile.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("CreateFile failed (%d)n", GetLastError());
        return 1;
    }

    // 獲取文件大小
    dwDataLen = GetFileSize(hFile, NULL);
    pbData = (BYTE *)malloc(dwDataLen);

    // 讀取文件
    ReadFile(hFile, pbData, dwDataLen, &dwDataLen, NULL);

    // 初始化CryptoAPI
    if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
        printf("CryptAcquireContext failed (%d)n", GetLastError());
        return 1;
    }

    // 創(chuàng)建散列對象
    if (!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash)) {
        printf("CryptCreateHash failed (%d)n", GetLastError());
        return 1;
    }

    // 計(jì)算散列值
    if (!CryptHashData(hHash, pbData, dwDataLen, 0)) {
        printf("CryptHashData failed (%d)n", GetLastError());
        return 1;
    }

    // 獲取散列值大小
    CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&dwHashSize, 0, 0);
    pbHash = (BYTE *)malloc(dwHashSize);

    // 獲取散列值
    CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashSize, 0);

    // 輸出MD5值
    print_hex(pbHash, dwHashSize);
    printf("n");

    // 清理
    CryptDestroyHash(hHash);
    CryptReleaseContext(hCryptProv, 0);
    free(pbData);
    free(pbHash);
    CloseHandle(hFile);

    return 0;
}

此程序先打開并讀取指定文件的內(nèi)容,然后利用CryptoAPI的函數(shù)初始化一個安全上下文,創(chuàng)建一個MD5散列對象,并將文件數(shù)據(jù)傳遞給CryptHashData函數(shù)來計(jì)算散列值。最后,CryptGetHashParam函數(shù)用于獲取散列值,然后將其輸出。

二、代碼實(shí)操

2.1 函數(shù)接口介紹

在Windows平臺下,使用Win32 API計(jì)算MD5值主要依賴于CryptoAPI(Cryptographic Application Programming Interface)。CryptoAPI是Windows操作系統(tǒng)提供的一組用于加密和散列操作的函數(shù)集,其中包括計(jì)算MD5散列值的能力。

以下是使用Win32 API計(jì)算MD5值涉及到的主要函數(shù)接口:

(1)CryptAcquireContext()

  • 功能:獲取一個加密服務(wù)提供者(CSP)的句柄。CSP是用于執(zhí)行加密操作的軟件模塊。
  • 參數(shù)
    • PHCRYPTPROV phCryptProv:返回的CSP句柄。
    • LPCSTR pszContainer:容器名稱,通常為NULL表示使用默認(rèn)容器。
    • LPCSTR pszProvider:提供者的名稱,如MS_ENH_RSA_AES_PROVPROV_RSA_FULL。
    • DWORD dwProvType:提供者類型。
    • DWORD dwFlags:標(biāo)志,如CRYPT_VERIFYCONTEXT。

(2)CryptCreateHash()

  • 功能:使用指定的算法創(chuàng)建一個散列對象。
  • 參數(shù)
    • HCRYPTPROV hCryptProv:從CryptAcquireContext()獲得的CSP句柄。
    • ALG_ID Algid:算法ID,對于MD5是CALG_MD5。
    • HCRYPTHASH hHash:返回的散列對象句柄。
    • DWORD dwFlags:標(biāo)志,通常為0

(3)CryptHashData()

  • 功能:向散列對象中添加數(shù)據(jù)。
  • 參數(shù)
    • HCRYPTHASH hHash:散列對象句柄。
    • const BYTE *pbData:指向要散列的數(shù)據(jù)的指針。
    • DWORD dwDataLen:數(shù)據(jù)的長度。
    • DWORD dwFlags:標(biāo)志,通常為0。

(4)CryptGetHashParam()

  • 功能:從散列對象中獲取參數(shù),如散列值。
  • 參數(shù)
    • HCRYPTHASH hHash:散列對象句柄。
    • HASHPARAM dwParam:請求的參數(shù)類型,對于散列值是HP_HASHVAL
    • BYTE *pbData:返回參數(shù)數(shù)據(jù)的緩沖區(qū)。
    • DWORD *pdwDataLen:緩沖區(qū)的長度。
    • DWORD dwFlags:標(biāo)志,通常為0。

(5)CryptDestroyHash()

  • 功能:銷毀散列對象。
  • 參數(shù)
    • HCRYPTHASH hHash:要銷毀的散列對象句柄。

(6)CryptReleaseContext()

  • 功能:釋放CSP句柄。
  • 參數(shù)
    • HCRYPTPROV hCryptProv:要釋放的CSP句柄。
    • DWORD dwFlags:保留供將來使用,通常為0。

一個典型的使用流程如下:

  1. 調(diào)用CryptAcquireContext()獲取CSP句柄。
  2. 使用CryptCreateHash()創(chuàng)建散列對象。
  3. 多次調(diào)用CryptHashData()將文件數(shù)據(jù)塊傳遞給散列對象。
  4. 調(diào)用CryptGetHashParam()獲取散列值。
  5. 使用CryptDestroyHash()銷毀散列對象。
  6. 最后,調(diào)用CryptReleaseContext()釋放CSP句柄。

這些函數(shù)通常會返回一個布爾值或錯誤代碼,用于指示操作是否成功,因此在調(diào)用這些函數(shù)后,應(yīng)檢查返回值并適當(dāng)處理錯誤。在處理文件數(shù)據(jù)時,通常需要將文件分成多個塊進(jìn)行處理,以防止內(nèi)存不足或提高性能。

2.2 計(jì)算指定文件的MD5值

開發(fā)環(huán)境:在Windows下安裝一個VS即可。我當(dāng)前采用的版本是VS2020。

下面是一個使用C語言和Windows CryptoAPI計(jì)算文件MD5值的完整示例代碼。

此代碼將打開一個文件,讀取其內(nèi)容,并使用CryptoAPI計(jì)算MD5散列值,最后將結(jié)果以十六進(jìn)制形式輸出到控制臺。

#include <windows.h>
#include <stdio.h>
#include <wincrypt.h>

void PrintBufferAsHex(const BYTE* buffer, DWORD length) {
    for (DWORD i = 0; i < length; i++) {
        printf("%02X", buffer[i]);
    }
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        printf("Usage: %s <filename>n", argv[0]);
        return 1;
    }

    HANDLE hFile;
    HCRYPTPROV hCryptProv;
    HCRYPTHASH hHash;
    DWORD dwDataLen;
    BYTE* pbData;
    DWORD dwHashSize;
    BYTE* pbHash;

    // Open the file
    hFile = CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("Failed to open file: %dn", GetLastError());
        return 1;
    }

    // Get file size
    dwDataLen = GetFileSize(hFile, NULL);
    pbData = (BYTE*)malloc(dwDataLen);
    if (pbData == NULL) {
        printf("Memory allocation failed.n");
        CloseHandle(hFile);
        return 1;
    }

    // Read file into buffer
    DWORD bytesRead;
    if (!ReadFile(hFile, pbData, dwDataLen, &bytesRead, NULL) || bytesRead != dwDataLen) {
        printf("ReadFile failed: %dn", GetLastError());
        free(pbData);
        CloseHandle(hFile);
        return 1;
    }

    // Initialize CryptoAPI
    if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
        printf("CryptAcquireContext failed: %dn", GetLastError());
        free(pbData);
        CloseHandle(hFile);
        return 1;
    }

    // Create hash object
    if (!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash)) {
        printf("CryptCreateHash failed: %dn", GetLastError());
        CryptReleaseContext(hCryptProv, 0);
        free(pbData);
        CloseHandle(hFile);
        return 1;
    }

    // Hash data
    if (!CryptHashData(hHash, pbData, dwDataLen, 0)) {
        printf("CryptHashData failed: %dn", GetLastError());
        CryptDestroyHash(hHash);
        CryptReleaseContext(hCryptProv, 0);
        free(pbData);
        CloseHandle(hFile);
        return 1;
    }

    // Get hash size
    dwHashSize = sizeof(DWORD);
    if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&dwHashSize, &dwHashSize, 0)) {
        printf("CryptGetHashParam failed: %dn", GetLastError());
        CryptDestroyHash(hHash);
        CryptReleaseContext(hCryptProv, 0);
        free(pbData);
        CloseHandle(hFile);
        return 1;
    }

    // Allocate memory for hash result
    pbHash = (BYTE*)malloc(dwHashSize);
    if (pbHash == NULL) {
        printf("Memory allocation failed.n");
        CryptDestroyHash(hHash);
        CryptReleaseContext(hCryptProv, 0);
        free(pbData);
        CloseHandle(hFile);
        return 1;
    }

    // Get hash value
    if (!CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashSize, 0)) {
        printf("CryptGetHashParam failed: %dn", GetLastError());
        free(pbHash);
        CryptDestroyHash(hHash);
        CryptReleaseContext(hCryptProv, 0);
        free(pbData);
        CloseHandle(hFile);
        return 1;
    }

    // Print hash value in hex format
    printf("MD5 of '%s': ", argv[1]);
    PrintBufferAsHex(pbHash, dwHashSize);
    printf("n");

    // Clean up
    free(pbHash);
    free(pbData);
    CryptDestroyHash(hHash);
    CryptReleaseContext(hCryptProv, 0);
    CloseHandle(hFile);

    return 0;
}

在運(yùn)行此代碼之前,這個程序假設(shè)文件可以一次性讀入內(nèi)存;對于大文件,需要分塊讀取并多次調(diào)用CryptHashData()

  • 更多詳細(xì)資料請聯(lián)系.docx
    下載

相關(guān)推薦