16 Jun
2015
Posted in: 코드
By    No Comments

[플밍노트] 초간단 암호화, 2001


[플밍노트] 초간단 암호화, 2001
by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaru, http://www.jiniya.net

자신의 데이터에 어떤 기준으로 락을 걸고 싶을때가 있습니다. 하지만 막상 금방 코드를 작성하려면 방법이 잘 떠오르지 않는것이 이런 암호와 관련된 것들이죠. 몇가지 간단한 방법들을 여기서 소개해 드리도록 하겠습니다. 실질적으로 높은 수준의 보안을 유지할 수는 없는 방법들입니다. 간단하고 이해하기 쉬우며 암호의 원리를 알아보도록 하죠.

암호는 기본적으로 상대방과 나와의 약속입니다. “내가 이렇게 원래 데이터를 바꿀테니 너는 이러이러케해서 원본데이터를 봐라”라고 하는 것들이 암호의 핵심입니다. 그런데 요즘 비번같은 암호의 경우는 비복원 방식을 사용하는 경우가 대부분입니다. 비복원이라는 말은 데이터가 암호와만 되고 역으로 추출해 내기는 힘들다는 이야기 입니다. 즉 abc가 zdsalkfj223으로 바뀌었을때, 이 바뀐 zds…이 abc로 돌아올 수는 없다는 이야기입니다. 반대로 복원 가능한 암호는 암호화 된 데이터로부터 원본 데이터를 그대로 복원해 낼 수 있습니다.

가장 간단한 암호와 기법으로는 고전적인 방식의 시저 암호가 있습니다. 즉 데이터를 상대방과 약속을 하고 일정한 수를 가감하거나 가산하는 방식을 말합니다. a=>b가 되면, c=>d가 되는 식입니다.. 코드의 구현은 아주 간단하겠죠.

#define SEED 2 

void Encrypt(char *data) 
{ 
    for(; *data; ++data) 
       *data += SEED; 
} 

void Decrypt(char *data) 
{ 
    for(; *data; ++data) 
       *data -= SEED; 
} 

다음으로는 더욱 간단한 방법으로 암호라고 하기는 힘들지만 데이터를 반전시키는 방법을 사용할 수 있습니다. 이런 경우의 암호는 주로 텍스트 파일 등을 에디터로 봐서는 보기 힘들게 하는 정도의 보안 수준에 사용하면 좋겠죠. 암호화와 복원이 한 함수로 이루어 집니다.

void EncDecrypt(char *data) 
{ 
    for(; *data; ++data) 
       *data = ~(*data); 
} 

끝으로는 사용자가 지정한 패스워드에 기반한 암호 알고리즘입니다. 소스 설명은 생략하도록 하겠습니다. 간단해서 보시면 금방 아! 하실수 있을것 같네염. 위 홤수와 마찬가지로 암호화와 복원이 한 함수로 이루어 지며, pwd에는 비번을 입력해 주시면 됩니다.

void EncDecrypt(char *data, char *pwd) 
{ 
   int i, size; 
   // 아래 맵은 256개의 문자로 초기화 시켜 주시면 됩니다... 
   char map[256] = "acvkjasewr23478cxvlkjdflk2349ds8fxcv,masfkjer234"; 

   size = strlen(pwd) - 1; 
   for(i=0; i<256; ++i) 
   { 
       map[i] = pwd[i % size] & map[i]; 
   } 

   for( ; *data; ++data) 
      *data ^= map[*data]; 
} 

---
일반적으로 실질적인 보안을 생각한다면 위와 같이 직접 만든 암호화 알고리즘을 사용하는 것은 좋지 않습니다. 수학적으로 검증된 기존 알고리즘을 사용하는 것이 안전합니다. 하지만 기존 암호화 알고리즘을 공부하고 또 그것을 코드로 직접 구현하는 일이 그리 쉽진 않지요. 그럴때에는 기존에 만들어둔 라이브러리를 사용하면 손쉽게 암호화, 복호화를 하실 수 있습니다. 아래는 윈도우에 포함된 암호화 API를 사용해서 RC4 알고리즘을 이용해서 암, 복호화를 수행하는 코드를 보여주고 있습니다.

RC4 암호화 함수

소스 코드 다운로드

//////////////////////////////////////////////////////////////////////////
//
//! RC4를 사용한 암호화 함수
//! \param pbKey 는 암호화 하는데 사용되는 키 버퍼
//! \param dwKeySize 는 암호화 하는데 사용되는 키 버퍼의 크기
//! \param pbPlaintText 는 암호화 하는데 사용되는 원문
//! \param pbCipherText 는 암호화 된 문장이 저장될 버퍼(원문의 크기 이상이어야 함)
//! \param dwHowMuch 는 암호화 하는데 사용되는 원문의 크기
//! \return GetLastError의 반환값, 성공한 경우 0 리턴
//! \remark 모든 버퍼는 문자열이 아닌 바이트 스트림으로 간주해야 함.

DWORD Encrypt(UCHAR *pbKey, 
              DWORD dwKeySize, 
              UCHAR *pbPlaintext, 
              UCHAR *pbCipherText, 
              DWORD dwHowMuch)
{
    HCRYPTPROV  hProv = NULL;
    HCRYPTKEY   hKey = NULL;
    HCRYPTHASH  hHash = NULL;

    DWORD       dwBuff = dwHowMuch;

    BOOL        dwRet = 0;
    
    memcpy(pbCipherText, pbPlaintext, dwHowMuch);

    try
    {
        if(CryptAcquireContext( &hProv, 
                                NULL, 
                                NULL, 
                                PROV_RSA_FULL, 
                                CRYPT_VERIFYCONTEXT) == FALSE)
            throw GetLastError();

        if(CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash) == FALSE)
            throw GetLastError();

        if(CryptHashData(hHash, pbKey, dwKeySize, 0) == FALSE)
            throw GetLastError();

        if(CryptDeriveKey(hProv, CALG_RC4, hHash, CRYPT_EXPORTABLE, &hKey) == FALSE)
            throw GetLastError();

        if(CryptEncrypt(hKey, 0, TRUE, 0, pbCipherText, &dwBuff, dwHowMuch) == FALSE)
            throw GetLastError();
    }
    catch(const DWORD dwLastError)
    {
        dwRet = dwLastError;
    }
    
    if(hKey)
        CryptDestroyKey(hKey);

    if(hHash)
        CryptDestroyHash(hHash);

    if(hProv)
        CryptReleaseContext(hProv, 0);

    return dwRet;
}

RC4 복호화 함수

//////////////////////////////////////////////////////////////////////////
//
//! RC4를 사용한 복호화 함수
//! \param pbKey 는 암호화 하는데 사용되는 키 버퍼
//! \param dwKeySize 는 암호화 하는데 사용되는 키 버퍼의 크기
//! \param pbCipherText 는 암호화된 버퍼
//! \param pbPlaintText 는 암호가 해독된 원문(암호화된 버퍼 크기 이상이어야 함)
//! \param dwHowMuch 는 암호화된 버퍼 크기
//! \return GetLastError의 반환값, 성공한 경우 0 리턴
//! \remark 모든 버퍼는 문자열이 아닌 바이트 스트림으로 간주해야 함.

DWORD Decrypt(UCHAR *pbKey, 
              DWORD dwKeySize, 
              UCHAR *pbCipherText, 
              UCHAR *pbPlaintext, 
              DWORD dwHowMuch)
{
    HCRYPTPROV  hProv = NULL;
    HCRYPTKEY   hKey = NULL;
    HCRYPTHASH  hHash = NULL;
    
    DWORD       dwBuff = dwHowMuch;
    DWORD       dwRet = 0;
    
    memcpy(pbPlaintext, pbCipherText, dwHowMuch);
    
    try
    {
        if(CryptAcquireContext(&hProv, 
                               NULL, 
                               NULL, 
                               PROV_RSA_FULL, 
                               CRYPT_VERIFYCONTEXT) == FALSE)
            throw GetLastError();
        
        if(CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash) == FALSE)
            throw GetLastError();
        
        if(CryptHashData(hHash, pbKey, dwKeySize, 0) == FALSE)
            throw GetLastError();
        
        if(CryptDeriveKey(hProv, CALG_RC4, hHash, CRYPT_EXPORTABLE, &hKey) == FALSE)
            throw GetLastError();
        
        if(CryptDecrypt(hKey, 0, TRUE, 0, pbPlaintext, &dwBuff) == FALSE)
            throw GetLastError();
    }
    catch(const DWORD dwLastError)
    {
        dwRet = dwLastError;
    }

    if(hKey)
        CryptDestroyKey(hKey);
    
    if(hHash)
        CryptDestroyHash(hHash);
    
    if(hProv)
        CryptReleaseContext(hProv, 0);

    return dwRet;
}

RC4를 이용한 암, 복호화 테스트 코드

//////////////////////////////////////////////////////////////////////////
//
//! 바이트 버퍼를 헥사값으로 출력하는 함수
//! \param pbText 는 출력될 바이트 버퍼
//! \param dwLength 는 출력될 바이트 버퍼 크기
//! \return 은 없음
//! \remark 이 함수는 출력을 완료한 후 개행함

void PrintByte(UCHAR *pbText, DWORD dwLength)
{
    DWORD   i;
    CHAR    *szHexaMap = "0123456789ABCDEF";
    
    for(i = 0; i < dwLength; ++i)
	{
        putchar(szHexaMap[pbText[i] & 0x0f]);
		putchar(szHexaMap[pbText[i] >> 4]);
        putchar(' ');
    }
    
    putchar('\n');
}

int main(int argc, char* argv[])
{
    UCHAR   szPlainText[] = "Hello World!";
    UCHAR   szChipherText[MAX_PATH];
    UCHAR   szDecryptText[MAX_PATH];
    UCHAR   szKey[] = "pohang";

    DWORD   dwBuffSize = sizeof szPlainText;
    DWORD   dwKeySize = sizeof szKey;

    PrintByte(szPlainText, dwBuffSize);
    Encrypt(szKey, dwKeySize, szPlainText, szChipherText, dwBuffSize);
    PrintByte(szChipherText, dwBuffSize);
    Decrypt(szKey, dwKeySize, szChipherText, szDecryptText, dwBuffSize);
    PrintByte(szDecryptText, dwBuffSize);
    return 0;
}


  • 트랙백 주소: http://www.jiniya.net/wp/archives/4975/trackback

관련 글