C언어 - MD5 해시값 구하기 / 메모장에 저장된 디렉토리를 fgets()로 읽어서 해당 파일의 해시값 구하는 방법 질문드립니다.

조회수 543회
#pragma warning ( disable : 4996 )

#include <wchar.h>
#include <windows.h>
#include <Wincrypt.h>
#include <stdio.h>

#define BUFSIZE 1024
#define MD5LEN  16

void file_hash()
{
    char f_list[_MAX_PATH];
    char* t_list;
    int i = 0;

    FILE* fp = fopen("File_list.txt", "r");
    //md5_hash(L"C:\\search\\temp\\Test.txt");

    if (fp == NULL)
    {
        printf("No file.txt");
        exit(1);
    }

    while (!feof(fp))
    {
        t_list = fgets(f_list, _MAX_PATH, fp);
        t_list[strlen(t_list) - 1] = '\0';

        //printf("%s\n", t_list);
        md5_hash(t_list);

    }
    fclose(fp);
}
unsigned long md5_hash(char file_path[])
{
    DWORD dwStatus = 0;
    BOOL bResult = FALSE;
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    HANDLE hFile = NULL;
    BYTE rgbFile[BUFSIZE];
    DWORD cbRead = 0;
    BYTE rgbHash[MD5LEN];
    DWORD cbHash = 0;
    CHAR rgbDigits[] = "0123456789abcdef";
    //const char* filename = ((L"%s", file_path));
    LPCWSTR *filename = file_path;

    // Logic to check usage goes here.

    hFile = CreateFile(filename,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL);

    if (INVALID_HANDLE_VALUE == hFile)
    {
        dwStatus = GetLastError();
        printf("Error opening file %s\nError: %d\n", filename, dwStatus);
        return dwStatus;
    }

    // Get handle to the crypto provider
    if (!CryptAcquireContext(&hProv,
        NULL,
        NULL,
        PROV_RSA_FULL,
        CRYPT_VERIFYCONTEXT))
    {
        dwStatus = GetLastError();
        printf("CryptAcquireContext failed: %d\n", dwStatus);
        CloseHandle(hFile);
        return dwStatus;
    }

    if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
    {
        dwStatus = GetLastError();
        printf("CryptAcquireContext failed: %d\n", dwStatus);
        CloseHandle(hFile);
        CryptReleaseContext(hProv, 0);
        return dwStatus;
    }

    while (bResult = ReadFile(hFile, rgbFile, BUFSIZE,
        &cbRead, NULL))
    {
        if (0 == cbRead)
        {
            break;
        }

        if (!CryptHashData(hHash, rgbFile, cbRead, 0))
        {
            dwStatus = GetLastError();
            printf("CryptHashData failed: %d\n", dwStatus);
            CryptReleaseContext(hProv, 0);
            CryptDestroyHash(hHash);
            CloseHandle(hFile);
            return dwStatus;
        }
    }

    if (!bResult)
    {
        dwStatus = GetLastError();
        printf("ReadFile failed: %d\n", dwStatus);
        CryptReleaseContext(hProv, 0);
        CryptDestroyHash(hHash);
        CloseHandle(hFile);
        return dwStatus;
    }

    cbHash = MD5LEN;
    if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
    {
        printf("MD5 hash of file %s is: ", filename);
        for (DWORD i = 0; i < cbHash; i++)
        {
            printf("%c%c", rgbDigits[rgbHash[i] >> 4],
                rgbDigits[rgbHash[i] & 0xf]);
        }
        printf("\n");
    }
    else
    {
        dwStatus = GetLastError();
        printf("CryptGetHashParam failed: %d\n", dwStatus);
    }

    CryptDestroyHash(hHash);
    CryptReleaseContext(hProv, 0);
    CloseHandle(hFile);

    return dwStatus;
}

메모장에 저장된 파일의 디렉토리를 읽어와서 해시값을 계산하여 결과 값을 변수에 저장하는 함수를 구현하려고 합니다.

우선 메모장(File_list.txt)에는 아래와 같은 형식으로 저장되어 있습니다.

C:\search\temp\md5.c

C:\search\temp\md5.h

C:\search\temp\Test.txt

그리고 main에서 file_hash()를 동작 시키면 아래와 같은 실행결과가 출력됩니다.

Error opening file C:\search\temp\md5.c Error: 2

Error opening file C:\search\temp\md5.h Error: 2

Error opening file C:\search\temp\Test.txt Error: 123

이후 구글링을 통해서 에러의 내용을 살펴보니 Error 2는 시스템은 지정된 파일을 찾을 수 없습니다. Error 123은 파일 이름, 디렉터리 이름 또는 볼륨 레이블 구문이 잘못 되었습니다.

함수에 인자로 L"C:\search\temp\Test.txt"와 같이 직접 전달하면 작동을 하고 fgets()를 통해 메모장의 내용을 한줄씩 가져오면 위의 경우처럼 에러가 발생합니다. 그래서 fgets()가 문자열을 가져오는 방식 때문에 원하는대로 작동을 안하는건지

문자열을 유니코드로 변환(?) 과정의 문제인지 궁금합니다. 해당 MD5 해시함수의 예제는 https://docs.microsoft.com/ko-kr/windows/win32/seccrypto/example-c-program--creating-an-md-5-hash-from-file-content에서 가져와서 필요한 부분에 맞춰 수정하였습니다.

  • (•́ ✖ •̀)
    알 수 없는 사용자

1 답변

  • fgets 함수로 파일에서 한줄을 읽게되면 한줄 뿐만 아니라 그줄의 맨 뒤에있는 줄바꿈 문자('\n')까지도 같이 읽어 드립니다.

    예를 들어 아래와 같은 텍스트 파일이 있다면

    test2.txt
    test3.txt
    

    맨처음 fgets로 읽어 들인 값은 "test2.txt\n"입니다. 이상태에서 이 문자열로 test2.txt 파일을 열려고 하면 뒤의 \n때문에 없는 파일이라고 나옵니다.

    한편, 위 파일의 마지막줄인 test3.txt 뒤에는 \n이 없기 때문에 fgets로 읽어 들인 값은 "test3.txt"이고, 이 경우라면 test3.txt 파일을 열수 있겠지요.

    따라서 fgets로 파일 이름을 읽고 사용하시려면 한줄을 읽고 마지막 칸이\n이라면 그 칸을 널문자(0)로 채우고 사용하면 됩니다.

    아래 코드 참고하세요.

    • 코드
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        FILE* fp = fopen("test.txt", "r");
        if (fp == NULL) {
            fprintf(stderr, "file opening error 1\n");
            return 1;
        }
        else printf("opened 1\n");
    
        char buffer[80];
        fgets(buffer, 80, fp);
    
        FILE* fp2 = fopen(buffer, "r");
        if (fp2 == NULL)
            fprintf(stderr, "file opening error 2\n");
        else
            printf("opened 2\n");
    
        int file_name_size = strlen(buffer);
        if (buffer[file_name_size - 1] == '\n')
            buffer[file_name_size - 1] = 0;
    
        FILE* fp3 = fopen(buffer, "r");
        if (fp3 == NULL)
            fprintf(stderr, "file opening error 3\n");
        else
            printf("opened 3\n");
    
        return 0;
    }
    
    • 결과

    이미지

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • 아직 목표하는 바를 이루지는 못했지만 한가지 새롭게 알아갑니다. 답변 감사합니다. 알 수 없는 사용자 2021.11.27 22:09

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)