MD5 암호화 프로그램분석
1. 해시 함수와 MD5
1.1. 해시 함수
해시 함수(hash function) 또는 해시 알고리즘(hash algorithm)은 임의의 데이터로부터 일종의 짧은“전자 지문”을 만들어 내는 방법이다. 해시 함수는 데이터를 자르고 치환하거나 위치를 바꾸는 등의 방법을 사용해 결과를 만들어 내며, 이 결과를 흔히 해시 값(hash value)이라 한다. 해시 함수는 결정론적으로 작동해야 하며, 따라서 두 해시 값이 다르다면 그 해시 값에 대한 원래 데이터도 달라야 한다. (역은 성립하지 않는다) 해시 함수의 질은 기대되는 입력 영역에서 얼마나 적은 해시 충돌(서로 다른 두 데이터의 해시 값이 같은 경우)을 일으키느냐로 결정되는데, 충돌이 많이 날수록 서로 다른 데이터를 구별하기 어려워지고 데이터를 검색하는 비용을 늘리기 때문이다.
1.2. MD5(Message-Digest algorithm 5)
MD5(Message-Digest algorithm 5)는 128비트 암호화 해시 함수이다. RFC 1321로 지정되어 있으며, 주로 프로그램이나 파일이 원본 그대로인지를 확인하는 무결성 검사 등에 사용된다. 1991년에 로널드 라이베스트가 예전에 쓰이던 MD4를 대체하기 위해 고안했다. 현재 MD5의 설계상 결함이 발견되어 MD5 알고리즘을 보안 관련 용도로 쓰는 것은 권장하지 않으나, 무결성 검사를 위해 아직도 많이 이용되고 있다.
1.2.1. MD5 알고리즘
MD5는 임의의 길이의 메시지를 입력받아, 128비트짜리 고정 길이의 출력 값을 낸다. 입력 메시지는 512 비트 블록들로 쪼개지며, 메시지를 우선 패딩하여 512로 나누어 떨어 질 수 있는 길이가 되게 한다. 패딩은 첫 단일 비트 1을 메시지 끝부분에 추가한다. 그 후에 512의 배수의 길이보다 64비트가 적은 곳까지 0으로 채운다. 나머지 64비트는 오리지널메시지의 길이를 나타내는 64비트 정수 값으로 채워진다.
메인 MD5알고리즘은 A,B,C,D라고 이름이 붙은 32비트 워드 네 개로 이루어진 하나의 128비트 스테이트(state)에 대해 동작한다. A,B,C,D는 소정의 상수 값으로 초기화 된다. 메인 MD5 알고리즘은 각각의 512비트짜리 입력 메시지 블록에 대해 차례로 동작한다. 각 512비트 입력 메시지 블록을 처리하고 나면 128비트 스테이트의 값이 변하게 된다.
하나의 메시지 블록을 처리하는 단계는 총 4가지로 나뉘며, 한 단계를 “라운드”(round)라고 부른다. 각 라운드는 비선형 함수 F, 모듈라 덧셈, 레프트 로테이션에 기반한 16개의 동일 연산으로 이루어져 있다.

<그림 1>은 한 라운드에서 이루어지는 한 연산을 묘사하고 있으며, MD5에서는 이 단일 연산을 64번 수행한다. F는 각 라운드에서 사용하는 비선형 함수를 가리키며, 각 라운드에서는 각각 다른 함수를 사용한다. Mi는 입력 메시지의 32비트 블록을 의미한다. <<<s는 s칸 만큼의 레프트 로테이션을 가리키며, s는 각 연산 후 값이 변한다.田은 모듈로 232 덧셈을 말한다.

2. cmd5 프로그램 소스 분석
MD5 알고리즘 소스는 md5.h(md5 소스의 헤더파일), md5c.c(md5 소스의 본체), global.h(타입에 대한 선언) 총 3가지 파일이 있다. 본 소스는 RFC1321 문서에서 제공되며, MD5 알고리즘을 C와 C++에서 사용 할 수 있도록 코딩되어있다.
cmd5 프로그램은 vector클래스에 문자열을 삽입하여, 해당 문자열을 md5 알고리즘으로 암호화 하여 결과를 출력해주는 콘솔 프로그램이다. 이는 오픈소스 프로그램으로 웹에서 손쉽게 구할 수 있다. cmd5 프로그램은 RFC1321 문서에서 제공되는 MD5 암호화 알고리즘 소스를 기반으로 구현되었다. 알고리즘을 이용한 암호화 및 출력만을 제공하므로 소스코드는 상당히 간결하다.

<그림 3>은 cmd5 프로그램을 통해 ‘ssrc’문자열을 MD5 알고리즘을 통해 암호화 한 결과이다.
2.1. md5c.c
2.1.1. MD5 변환 루틴

Step4에서 수행하는 block 처리 과정 중 레프트 로테이션 하는 횟수를 미리 선언한다. 또한 각 함수에 대한 프로토타입 리스트를 선언하며, 메시지를 패딩하기위해 패딩 함수를 선언한다. 패딩 함수는 첫 단일 비트에 1을 추가하고 나머지를 0으로 채우도록 초기화한다.
2.1.2. 메시지 블록 처리 선언

메시지 블록 처리 선언 부분이다. 비선형 함수 F 4가지를 선언하고, 레프트 로테이션을 32bit 단위의 연산으로 선언한다. 또한 각 라운드 별 연산을 선언한다.
2.1.3. MD5 수행 시작

MD5 수행을 시작하는 함수이다. magic 초기화 상수를 로드하여 512비트 블록들로 쪼개는 작업을 수행한다.
2.1.4. MD5 블록 업데이트

메시지 블록을 처리할 때마다 MD5 블록 업데이트 작업을 수행하는 함수이다. bit를 byte로 변환하여 mod 64를 수행한다. 패딩 시에 원본메시지의 길이를 bit로 넣어야 하기 때문에 bit를 byte로 업데이트 한다.
2.1.5. 최종 MD5 메시지 다이제스트

최종 MD5 메시지 다이제스트를 수행하는 함수이다. UINT4를 char 배열로 변환하고, mod 64가 되도록 패딩을 수행한다. 그 후에 원본 메시지의 길이를 패딩한다.
2.1.6. 각 라운드 수행


각 라운드(총 4라운드)를 수행하는 부분이다. 라운드 당 단일 연산 16번 씩 총 64번을 수행한다.
2.1.7. UINT4 타입을 char 배열로 변환

헤더파일에서 선언한 UINT4 타입을 char 배열로 변환을 수행하는 함수이다.
2.1.8. char 배열을 UINT4 타입으로 변환

char 배열을 헤더파일에서 선언한 UINT4 타입으로 변환을 수행하는 함수이다.
2.1.9. MD5 memcpy
표준 memcpy와 같이 메모리 복사를 수행하는 함수이다. memcpy함수를 MD5에 맞게 적용 할 수 있도록 선언 되었다.
2.1.10. MD5 memset

표준 memset과 같이 배열을 선언하면서 초기화하지 않았거나 또는 실행 중에 이미 값이 저장된 배열을 모두 특정 값으로 채우는 함수이다. memset함수를 MD5에 맞게 적용 할 수 있도록 선언 되었다.
2.2. md5class.h
md5class.h에는 cmd5 프로그램에서 사용하는 클래스가 선언 되어있다.

<그림 15>는 md5class.h 소스이다. 디폴트 생성자, 문자열 상수인 평문을 매개변수로 갖는 생성자, 가상함수로 선언 된 소멸자 등이 선언되어 있으며, 나머지 함수 및 함수의 용도는 md5class.cpp 소스 분석 부분에서 자세히 다루도록 한다.
2.3. md5class.cpp
md5class.cpp에는 md5class.h에서 선언 된 클래스 내부의 함수들을 초기화 한다.
2.3.1. 생성자와 소멸자

CMD5클래스의 생성자와 소멸자를 지정해준 부분이다. 아직 평문이 없기에 false로 초기화 하며, 다이제스트 문자열이 항상 32자 이므로 32번째 배열을 NULL로 초기화를 디폴트 생성자에서 수행한다. 평문을 인자로 가질 경우, 평문의 포인터를 가져온다.
2.3.2. 평문 설정

평문을 설정하는 함수이다. 평문의 const 속성을 제거하여 초기화한다.
2.3.3. MD5 다이제스트 얻기

MD5 다이제스트 값을 얻어온다. 평문이 설정되어 있지 않은 경우, 액세스 된 메시지 다이제스트는 0을 반환한다.
2.3.4. 다이제스트 연산

MD5 ‘C’코드를 호출하여 다이제스트 연산울 수행한다.
2.4. main.cpp
main.cpp는 평문을 초기화 하여, MD5를 이용한 암호문을 출력한다.

<그림 20>은 main.cpp 소스이다. string형 vector 객체를 선언하여 암호화 하고자 하는 평문으로 초기화 한다. 평문을 암호화하여 콘솔 창에 출력한다.
3. 결론
MD5 알고리즘을 C언어로 구현한 소스를 분석하고, 해당 소스를 이용한 콘솔기반 암호화 프로그램도 분석해보았다. 우리는 실제로 파일의 무결성을 검증하기 위해 MD5로 암호화 된 해시 값을 많이 사용하고 있다. 그러나 최근에는 MD5의 안전성이 떨어진다는 이야기가 나오며 다른 해시 알고리즘 사용을 권고하고 있다. 무결성 검증이 매우 중요시 되는 파일에서는 안전성이 더 높은 해시 알고리즘 사용을 권고하는 바이다.