OpenSSL은 암호화 및 보안 기능을 제공하는 오픈소스(Open Source) 라이브러리로, MD5 (Message-Digest Algorithm 5)해시 관련 함수들을 제공한다. MD5는 임의의 길이의 데이터를 문자열 또는 파일로 입력 받아 고정된 길이의 해시 값을 출력한다. 주로 데이터 무결성 확인이나 패스워드 등의 보안 목적으로 사용된다. 이 글에서는 C/C++ 언어에서 OpenSSL MD5를 사용하는 방법에 대해 알아본다.
OpenSSL 설치
먼저, 시스템에 OpenSSL을 설치한다. 다음은 Ubuntu 기준으로 설치하는 방법이다.
sudo apt-get update
sudo apt-get install libssl-dev
문자열 MD5 해시 생성 예제 (C)
#include <stdio.h>
#include <string.h>
#include <openssl/md5.h>
void md5_string(const char *str) {
MD5_CTX ctx;
unsigned char digest[MD5_DIGEST_LENGTH];
MD5_Init(&ctx);
MD5_Update(&ctx, str, strlen(str));
MD5_Final(digest, &ctx);
printf("MD5(%s) = ", str);
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
printf("%02x", digest[i]);
}
printf("\n");
}
int main() {
const char *input = "Hello, MD5!";
md5_string(input);
return 0;
}
- “Hello, MD5!” 문자열에 대한 md5 값을 출력한다.
$ ./a.out
MD5(Hello, MD5!) = 383e139e64e5f46de9d03ba3695da2d8
파일의 MD5 해시 생성 예제 (C++)
#include <openssl/md5.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdarg>
#include <memory>
/*
* file utils
*/
bool fileExists(const char *path) {
std::ifstream is(path);
return is.good();
}
char* readFile(const char* filePath)
{
if (!filePath)
return 0;
int fd = open(filePath, O_RDONLY);
if (fd == -1)
return 0;
FILE* f = fdopen(fd, "r");
if (!f) {
return 0;
}
fseek(f, 0L, SEEK_END);
long sz = ftell(f);
fseek( f, 0L, SEEK_SET );
if (sz <= 0) {
fclose(f);
return 0;
}
char* ptr = new char[sz+1];
if (!ptr) {
fclose(f);
return 0;
}
ptr[sz] = 0;
size_t result = fread(ptr, 1, sz, f);
if(result != (size_t)sz) {
fclose(f);
delete [] ptr;
return 0;
}
fclose(f);
return ptr;
}
namespace MD5 {
static void printMD5Sum(const char *msg, unsigned char *md)
{
char md5msg[40];
for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
sprintf(md5msg+(i*2), "%02x", md[i]);
printf("%s: md5msg : %s\n", msg, md5msg);
}
int getMD5SumFile(const char *path, unsigned char *md)
{
char *buffer = readFile(path);
if (buffer == NULL) {
printf("readFile(%s) failed", path);
return -1;
}
unsigned long bufsize = strlen(buffer);
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, (const void*)buffer, (unsigned long)bufsize);
MD5_Final(md, &ctx);
delete []buffer;
return 0;
}
int generateFile(const char *path)
{
unsigned char md[MD5_DIGEST_LENGTH];
if (getMD5SumFile(path, md)) {
return -1;
}
std::string md5Path(path);
md5Path += ".md5";
std::ofstream ofs;
ofs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
ofs.open(md5Path.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
}
catch (std::system_error& e) {
printf("md5: generateFile(): open(%s) failed : %s", md5Path.c_str(),
e.code().message().c_str());
return -2;
}
ofs.write((const char*)md, (long)MD5_DIGEST_LENGTH);
ofs.close();
return 0;
}
bool checkFile(const char *path)
{
unsigned char md[MD5_DIGEST_LENGTH];
bool ret = true;
if (getMD5SumFile(path, md)) {
printf("getMD5SumFile() fail");
return false;
}
std::string md5Path(path);
md5Path += ".md5";
char *data = readFile(md5Path.c_str());
if (data == NULL) {
return false;
}
/* compare */
if (memcmp((const void*)md, (const void*)data, (size_t)MD5_DIGEST_LENGTH) != 0) {
printf("[%s] %s: compare fail\n", __func__, path);
printMD5Sum("orig", md);
printMD5Sum("file", (unsigned char*)data);
ret = false;
}
delete []data;
return ret;
}
} /* namespace MD5 */
int main(int argc, const char *argv[])
{
char *filepath = (char *)argv[1];
MD5::generateFile(filepath);
return 0;
}
- 입력 받은 파일에 md5 확장자가 더해진 파일을 생성하고, 입력 파일의 MD5 값을 갖도록 한다.
$ ./a.out gen_md5.cpp139e64e5f46de9d03ba3695da2d8
$ hexdump gen_md5.cpp.md5
0000000 efab 2db2 40a5 a3ef cb51 7290 9e9e 19d4
0000010
참고 사이트
- OpenSSL Documentation: OpenSSL의 공식 문서 페이지.
- MD5 – Wikipedia: MD5 해시 함수에 대한 Wikipedia 페이지.