Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

US0

CH.3 Openssl를 활용한 암호화 프로그래밍<EVP API를 활용한 DES_CBC 암.복호화 실습 OP1.> 본문

Security/Cryptography

CH.3 Openssl를 활용한 암호화 프로그래밍<EVP API를 활용한 DES_CBC 암.복호화 실습 OP1.>

us0 2018. 11. 8. 02:21

제목만 보고 이게 뭔가 싶다면 앞의 Chapter들을 읽고오자

2018/11/05 - [Security/Cryptography] - CH.0 Openssl를 활용한 암호화 프로그래밍 <기초지식>

2018/11/05 - [Security/Cryptography] - CH.1 Openssl를 활용한 암호화 프로그래밍 <암호화 구조체>

2018/11/05 - [Security/Cryptography] - CH.2 Openssl를 활용한 암호화 프로그래밍 <비밀키 암호화 순서>


CH.3 Openssl를 활용한 암호화 프로그래밍

<EVP API를 활용한 DES_CBC 암.복호화 실습 OP1>


앞서 EVP API의 특징은 각각의 인터페이스가 같다는 것이다. 


그렇다면 이말은 즉!!!! 각각의 암호화할때 인터페이스도 같지만  암/복 호화를 할때에도 순서가 같고 인터페이스가 같다는 것이다!!!! 

(즉, 편리하다!!!!!!) 


초기화 -> 업데이트 -> 종료


우리는 제목과 같이 DES_CBC 암호화를  2가지 방법에 걸쳐 진행 할 것이다.




DES_CBC

호화 종류는 아주많고 또한 암호화 종류안의 종류가 있는데  지금 하는 암호화는 블록암호인 DES를 암호화 방식으로 진행할 것이고

암호화 모드중 CBC라는 것을 통해서 암호화를 진행 하겠다.

(암 복호화를 동시에 진행하는 만큼 그냥 main에 코드 다 있음!!)

 

DES_CBC 는  DES(암호화 종류)_CBC(암호화 방법??) 이라고도 일단 생각해두면 편할수도 있겠다. 



백문이불여일견이니 코드보면서 설명 시작!!!!!!!!!!!!!!!


중요한부분아니거나 간단한 설명들은 주석으로 설명 ㅇㅂㅇ



#include <stdio.h>

#include<stdlib.h>

#include<string.h>


#include <openssl/rand.h>    //openssl의 헤더파일

#include<openssl/evp.h>      //이것도


왜 이같은 헤더파일을 선언했냐??

 이름보면 알겠지만 openssl/evp.h 는 EVP패키지를 쓰기 위함이고 rand.h는 랜덤한 KEY와 iv를 받기 위해 사용했다.


int main(int argc, const char * argv[]) {



코드 시작!!

제일 처음부분은 우리가 ch1에서 배웠던 구조체들이다. ch1를 봐왔다면 왜 저런 코드가 나왔는지 이해가 될것이다.

그리


 /*

     *

     * Enctypt

     *

     *

     */

    

    //EVP API를 써서 des_cbc를 하기위해 초가화 과정

    const EVP_CIPHER *c= EVP_des_cbc();

    EVP_CIPHER_CTX *ctx_enc=EVP_CIPHER_CTX_new();

    

    if(ctx_enc == NULL){

        printf("EVP_CIPHER_CTX_new is NULL error!!\n");

        return -1;

    }

    

    if(!(EVP_EncryptInit_ex(ctx_enc, c, NULL, NULL, NULL))){ //콘텍스트에 EVP_des_cbc를 사용한다고 말해준다!!

        printf("EVP_EncryptInit_ex error!!\n");             //그래서 초반에 EVP_EncryptInit_ex()함수를 실행해준다.

        return -1;

    }


EVP_EncryptInit_ex는 초기화 과정을 하는 함수인데 지금 선언한건 진짜 초기화가 아닌 

ctx_enc에 des를 사용한다고 알려주는 역할을 하는거다 (des정보 잠시 저장).

<그래서 3~5 번째 매개변수가 NULL>



key, iv 설정

자 암호화를 시작하기 전에  무엇부터 해야할까??

우리는 key와 iv(처음 키)  값이 있어야 cbc모드로 암호화를 진행할 수 있다!!! 


그리고 아래 코드는 보면서 설명하겠다


/**************************************************************************************************

     

     key, iv initialized

     Random key, iv use RAND_bytes()

     **************************************************************************************************/

    printf("\n");

    printf("****************************Key, IV initailized********************************\n");

    printf("\n");

    

    unsigned char *key = (unsigned char*)malloc(EVP_CIPHER_CTX_key_length(ctx_enc));    //key

    unsigned char *iv = (unsigned char*)malloc(EVP_CIPHER_CTX_iv_length(ctx_enc));      //iv

    

    if(!RAND_bytes(key, EVP_CIPHER_CTX_key_length(ctx_enc))){

        printf("RAND_bytes function error!!\n");

        return -1;

    }

    

    if(!RAND_bytes(iv, EVP_CIPHER_CTX_iv_length(ctx_enc))){

        printf("RAND_bytes function error!!\n");

        return -1;

    }

    

    printf("Use key value : ");

    for(int i=0; i<(int)strlen((const char*)key); i++)          //key값 출력

        printf("0x%X ", key[i]);

    printf("\n");

    

    printf("Use iv value : ");

    for(int i=0; i<(int)strlen((const char*)iv); i++)          //iv값 출력

        printf("0x%X ", iv[i]);

    printf("\n");

    

    



이렇게 먼저 key와 iv값을 넣어줄 공간을 동적으로 할당해준다.

EVO_CIPHER_CTX_key_length()라는 함수로   des_cbc()정보가 저장되어있는 ctx_enc를 매개변수로 받아 


각각 DES의 키와 iv값에 맞는 길이가 각각 동적 할당 된다.

unsigned char *key = (unsigned char*)malloc(EVP_CIPHER_CTX_key_length(ctx_enc));    //key

unsigned char *iv = (unsigned char*)malloc(EVP_CIPHER_CTX_iv_length(ctx_enc));      //iv



그리고 이 문장은 2번 반복되는데(key랑 iv각각 한번 씩)  RAND_bytes(key,EVP_CIPHER_CTX_key_length(ctx_enc));

그냥 이 함수이름 그대로 해석을 해보면 랜덤한 바이트들을 키에 넣어준다 ctx_enc의 정보를 가진 키의 길이만큼 이라고 해석 되는데


즉, 그냥 des쓰고 있으니 그 길이만큼 랜덤으로 iv랑 key에  난수를  넣어줘라!! 라는 이말이다

if(!RAND_bytes(key, EVP_CIPHER_CTX_key_length(ctx_enc))){

        printf("RAND_bytes function error!!\n");

        return -1;

    }


 if(!RAND_bytes(iv, EVP_CIPHER_CTX_iv_length(ctx_enc))){

        printf("RAND_bytes function error!!\n");

        return -1;

    }



어렵게 만든키 확인해보고 싶으면 이 반복문 출력해보쟈

   for(int i=0; i<(int)strlen((const char*)key); i++)          //key값 출력

        printf("0x%X ", key[i]);

    printf("\n");



ps. RAND_bytes 함수는 성공시 1 실패시 0 을 각각 return 하기 때문에 !(not)연산을 통해 실패시 오류 검출을 해준것!!!




이제부터 본격적인 암호화 시작..!!!!!!!!(읭??)


먼저 암호화를 하기위한 평문을 생성해줘야 한다!!! (당연히 암호화 하려면 뭘 암호화해야할지 알아야하니까)


  unsigned char *plain_txt= (unsigned char*)"Hello hihi my name is honggildong nice to meet you  nice to meet you too!!.";             //Plain_Text

    

    printf("Plane_txt length = %d\n", (int)strlen((char*)plain_txt));

    

    unsigned char *cipher_txt= (unsigned char*)malloc(strlen((char*)plain_txt)+EVP_CIPHER_CTX_block_size(ctx_enc));        //cipher_txt

    

    

    


평문을 생성했으니 그 길이도 한번 출력해보고 


암호문이 들어갈 공간도 아까와 같이 동적할당해주자 근데!!!!!

(unsigned char*)malloc(strlen((char*)plain_txt)+EVP_CIPHER_CTX_block_size(ctx_enc)



여기서 EVP_CIPHER_CTX_block_size(ctx_enc)  이부분이 이해가 가지 않을수도 있는데 

이 함수는 패딩 해주는거라고 생각하자 즉, DES는 8바이트씩 암호화하는데 꼭 8바이트로 나눠 떨어지라는 법이 없으니

 그만큼 채워 주는것!! (DES라는 것을 알려주는 정보는 ctx_enc에 있다는것을 잊지말자!!!)




여기는 그냥 변수 선언 각각의 뜻은 

outl == out length의 줄임말이다 (나가는 길이 라는 말인데 지금까지 얼마나 출력 됬나로 쓸꺼당)

numBytesWritten 애는 각 암호화 과정마다 몇바이트를 썼나(write)를 알아보기위한 ㅂㅕㄴ수!! 


    int outl=0, numBytesWritten=0; //인덱스 값이 되는out_length와 numBytesWritten 변수 설정!

    

    printf("input plain Text : \n \t\t%s \n", plain_txt);

    

    EVP_EncryptInit_ex(ctx_enc,c ,NULL,key,iv);   //초기화 아까 한번 선언했지만 지금 선언한 이유는 key와 iv값을 알려주기 위해서이다!!!!!


맨 마지막줄에 아까 했던 초기화가 또 나와있다!!!! 이유는 주석에 ^.^


지금 그럼 EVP API 3단계 모드인 초기화 -> 업데이트 -> 종료중  초기화만 끝났다아ㄱ아아아아악!!!!!

하지만 이제 할거 없으니 걱정하지 말 것





이제 업데이트랑 종료를 한번에 보여줄건데 간단한 설명은 주석처리 되어있다.

//8의 배수단위로 즉 8바이트(DES는 64bit씩 암호화) 단위로 5번째 매개변수 숫자만큼 암호호화 시킨다

    EVP_EncryptUpdate(ctx_enc, &cipher_txt[outl], &numBytesWritten , &plain_txt[outl], (int)strlen((char*)plain_txt));

    outl += numBytesWritten;

    

    //나머지(패딩된 값들 암호화!!!

    EVP_EncryptFinal_ex(ctx_enc, &cipher_txt[outl], &numBytesWritten);

    outl += numBytesWritten;

    

    //동적할당 해줬으니 할당 풀어주자

    EVP_CIPHER_CTX_free(ctx_enc);

    

    printf("Cipher Text is : \n \t\t");

    

    printf("%s",cipher_txt);

    

    printf("\n");




업데이트과정인

EVP_EncryptUpdate(ctx_enc, &cipher_txt[outl], &numBytesWritten , &plain_txt[outl], (int)strlen((char*)plain_txt));


이 함수 너무 어렵게만 보이는데!! 하나씩 살펴보자 (한글로 풀어 써보잣!!)


한글 해석본: EVP업데이트과정 함수를 이용하는데 ctx_enc에 저장되있는 암호화 정보를 통해서 plain_txt[outl]을 시작점으로 plain_txt길이만큼 암호화 시킨걸 cipher_txt[outl] 에 집어 넣는다. 몇 바이트 암호화 했는지는 numBytesWritten에 저장~~~


근데 업데이트 과정에서 numBytesWritten은 항상 8의 배수를 가지고 있지 이유는??

des가 8바이트씩 암호화 하니까 결국 업데이트 과정에선 항상 8byte의 배수만큼만(패딩된 블럭을 제외한) 암호화가 된다!! 잊지말것!!!!




그리고 뭐 final은 매개변수가 3개밖에 안되는데 

EVP_EncryptFinal_ex(ctx_enc, &cipher_txt[outl], &numBytesWritten);

이것역시 한글 해석을 하자면 ctx_enc구조체의 암호화 정보를 가지고  &cipher_txt[outl]   부터 끝까지 암호화 시킨다. 

( 마지막이니까 굳이 plain_txt의 끝을 가리키지 안아도 됨!!!! 매개변수가 5개에서 3개로 줄어든 이유!! ) 


ps. 아 중간중간에 outl= numBytesWritten 이있는데 그이유는 outl는 인덱스 역할을 하는데 이는 암호문이 

따로따로 생성되는것(Update와 Final)을 인덱스를 통해 시작 주소를 알려줌으로써 암호문이 이어지게 해준다


//동적할당 해줬으니 할당 풀어주자

    EVP_CIPHER_CTX_free(ctx_enc);


그리고 고생해서 만든 printf 로 cipher_txt출력 한번 해준다

 printf("%s",cipher_txt);




그럼 암호화 끄으으으으으읕!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



하지만.. 암호화를 했으면 복호화도 해야하는데..... 

EVP장점이  다른 암호화를써도, 암호화를하거나 복호화를 할 때 에도 같은 인터페이스를 가진다고 했다!!!! 

따라서 함수명만 enc였던걸 dec 로 해주면 댐 ㅎㅎ


따라서 자세한 설명은 생략한다...





근데 코드는 있어용


공부겸 혼자 해석 해보자 ㅎㅎ



/*

     *

     *  Decryption

     *

     */

    

    //Decrypyt 부분은 EVP API특성상 인터페이스가 같으니 여기서 설명생략 ^^ ㅎ.ㅎ

    printf("\n");

    printf("******************************Decrypyion**********************************\n");

    printf("\n");

    

    

    const EVP_CIPHER *c_dec= EVP_des_cbc();

    EVP_CIPHER_CTX *ctx_dec = EVP_CIPHER_CTX_new();

    

    EVP_DecryptInit_ex(ctx_dec, c_dec, NULL, key, iv);

    

    int lenOfCiphertxt = outl;  //cipher_txt_length

    unsigned char *dec_plain_txt = (unsigned char*)malloc(lenOfCiphertxt + EVP_CIPHER_CTX_block_size(ctx_dec)+1);

    int outl_dec=0, tmp_dec=0 ,cipher_idx=0;

    

    

    printf("lenOfCiphertxt : %d \n" , lenOfCiphertxt);

    

    EVP_DecryptUpdate(ctx_dec, &dec_plain_txt[outl_dec], &tmp_dec, &cipher_txt[outl_dec], lenOfCiphertxt);

    outl_dec += tmp_dec;

    cipher_idx += tmp_dec;

    

    

    EVP_DecryptFinal_ex(ctx_dec, &dec_plain_txt[outl_dec], &tmp_dec);

    outl_dec += tmp_dec;

    

    dec_plain_txt[outl_dec] = '\0';

    

    

    printf("\n");

    printf("Decrypted: >>%s<<\n", dec_plain_txt);

    printf("\n");

    

    return 0; //메인 끝나는 부분

}