US0
CH.5 Openssl를 활용한 암호화 프로그래밍<EVP API를 활용한 AES_CBC 암호화 ver.1.> 본문
CH.5 Openssl를 활용한 암호화 프로그래밍<EVP API를 활용한 AES_CBC 암호화 ver.1.>
us0 2018. 11. 10. 20:09CH.4 Openssl를 활용한 암호화 프로그래밍
<EVP API를 활용한 AES_CBC 암호화 ver.1.>
2018/11/05 - [Security/Cryptography] - CH.0 Openssl를 활용한 암호화 프로그래밍 <기초지식>
2018/11/05 - [Security/Cryptography] - CH.1 Openssl를 활용한 암호화 프로그래밍 <암호화 구조체>
2018/11/05 - [Security/Cryptography] - CH.2 Openssl를 활용한 암호화 프로그래밍 <비밀키 암호화 순서>
2018/11/08 - [Security/Cryptography] - CH.3 Openssl를 활용한 암호화 프로그래밍
2018/11/08 - [Security/Cryptography] - CH.4 Openssl를 활용한 암호화 프로그래밍
그동안 EVP API패키지를 활용하여 기본 프로그램상 평문 입력을 통해 DES_CBC 암호화를 진행해 보았다.
이번엔 파일 입/출력을 통해 실제 파일을 암호화 시켜보는 시간을 가져보자!!!
기본적으로 다시 한번 말하지만
초기화 -> 업데이트 -> 종료 순으로 암호화가 이루어진다는 것 과
EVP API 의 인터페이스는 항상 동일 하다는 것을 잊지말고 보자!!!
일단 기본적인 헤더파일은 이정도이다.
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<openssl/rand.h>
#include<openssl/evp.h>
이 헤더파일의 뜻은 이제 다 알거라고 생각한다!!!
그래도 혹시모르니 <openssl/rand.h>는 key값과 iv값을 랜덤으로 초기화 하기 위한 함수가 들어있는 헤더파일이고
<openssl/evp.h> 는 이름과 같이 EVP API를 활용하기 위한 함수들이 들어있는 헤더파일이다.
그리고 32byte씩 암호화 하기위해(256bit씩) 매크로 선언 ㅎ,ㅎ
#define NUM_BYTES_READ 32
아직 베타버전이기 때문에 main 함수에 다 때려 박아놨다 ㅠㅅㅠ
int main(int argc, const char * argv[]) {
//시작!!!!
일단 제일먼저 파일 입/출력을 위해 input파일과 output파일을 스트림을 만들어주자!!
자세한 설명은 주석으루 ㅎ.ㅎ
//FILE입출력을 통한 암호화를 진행하기 위해 경로를 설정해준다!!
//input 파일은 기존에 미리 있어야한다!! 나와 같이 경로를 꼭 설정 해줘야하는것이 아닌 그냥 파일명만 써도되는데 그건 프로젝트 디렉토리일떄만 가능하다!!!!
FILE *ifp = fopen( "/Users/usun/Desktop/Study/2018/Security/AES_enc_input_txt.txt", "r");
//이건 출력파일인데 이것은 없으면 새로 생성되고, 있어도 새로 덮어씌워진다.
FILE *ofp =fopen("/Users/usun/Desktop/Study/2018_2학기/Security/AES_enc_output_txt.txt", "w");
그리고 파일 포인터가 잘 만들어졌나 검사한번 해주구
//fopen error check
if(ifp==NULL){
printf("file open error!!\n");
return -1;
}
if(ofp==NULL){
printf("file open error!!\n");
return -1;
}
콘텍스트를 만들어준다
콘텍스트가 뭔지 까먹었으면
2018/11/05 - [Security/Cryptography] - CH.1 Openssl를 활용한 암호화 프로그래밍 <암호화 구조체>
여기 참조!!!!!!
//EVP API를 써서 aes_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;
}
그리고 암호화를 진행하면서 제일 첫 번째 단계인 EVP API의 특징 초기화를 해주었다 하지만!!!!!!!!1
이것은 완전한 초기화가 아닌 암호화 컨텍스트에 aes_128_cbc를 이용하는거라고 말해주는 것이다!!!
if(!(EVP_CipherInit_ex(ctx_enc, EVP_aes_128_cbc(), NULL, NULL, NULL, 1))){
printf("EVP_CipherInit_ex error!!\n");
return -1;
}
ps..여기서 주의!!!!
우리는 이전글에 DES_CBC를 활용하면서 초기화 할때 EVP_CipherInit_ex() 함수 대신 EVP_EncryptInit_ex()라는 함수를 썼다!! 여기서 매개변수는 5개였는데
이런 함수였다
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
ENGINE *impl, const unsigned char *key, const unsigned char *iv);
근데 여기서 쓴건??
int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc);
이거다 다 똑같고 매개변수가 6개인데!! 마지막 int enc 이라는 int형 매개변수가 붙었다 이게뭐냐??면
enc이니까 암호화 관련된거라고 추측할수 있다. 이건 일종의 flag인데 1일경우 암호화 0일경우 복호화이다!!!
그러니까 우리는 지금 암호화를 해야하니까 flag를 1로 설정해주었다.
자 다시 진행해보자!!!
KEY와 IV를 설정해주고 (이부분은 계속 앞서 말해왔으니 생략하겠다.!!!!)
//key와 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
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;
}
CipherInit_ex() 초기화 과정을 또 해주었다.
이게 바로 진짜 암호화 시작전의 초기화 과정이다악!!!!
EVP_CipherInit_ex(ctx_enc,EVP_aes_128_cbc() ,NULL,key,iv,1);
그리고 함수를 호출할 때 마다 들어가는 input length 와 암호화된 텍스트의 길이를 알기위해 변수를 선언 해준다.
int inlength =0 ,ci_txtlength =0;
암호화 시작!!!!
printf("\n");
printf("******************************Encrypyion**********************************\n");
printf("\n");
파일 입/출력을 통한 암호화시키기 때문에 이 할당공간은 잠시 거치는 공간 밖에 되지 않는다.
그리고 32byte씩 암호화 하기위해 아까 선언했던 매크로 NUM_BYTES_READ 만큼 메모리를 할당해주고
cipher_buf는 EVP_CIPHER_CTX_block_size() 콘텍스트 크기만큼 덧붙여준다. ( 이에 대한설명은 앞으로 추가시키 겠다!!)
unsigned char *plain_buf= (unsigned char*)malloc(NUM_BYTES_READ);
unsigned char *cipher_buf= (unsigned char*)malloc(NUM_BYTES_READ+ EVP_CIPHER_CTX_block_size(ctx_enc));
이제 여기가 진짜 암호화 부분인데!!!!!
32bytes 만큼 암호화해주기때문에 무한반복문을 돌리면서 Inlength를 검사해주면서 while문을 빠져나갈 break 문을 넣어준다.
자세한 설명은 주석으로!!!
while(1){
//여기서 fread는 ifp에서 1바이트씩 읽어 들여와 총 32바이트를 plain_buf에 저장하는 역할을 한다.
//만약 성공적으로 지정된 바이트 수만큼 읽어 들였다면 읽어들인 원소의 개수가 size_t 자료형으로 반환 된다.
//아니면 0을 반환한다.
inlength= (int)fread(plain_buf, 1, NUM_BYTES_READ, ifp);
//정해진 바이트 수에 충족하지 못하면 Final함수를 호출하기위해 반복문을 빠져나간다. 여기선 NUM_BYTES_READ
if(inlength <=0)
break;
//EVP_CipherUpdate(<#EVP_CIPHER_CTX *ctx#>, <#unsigned char *out#>, <#int *outl#>, <#const unsigned char *in#>, <#int inl#>)
//ctx_enc를 참조해서 plain_buf에서 inlength만큼 cipher_buf에 암호문을 쓴다 그리고 암호화된 바이트 수만큼 ci_txtlength로 반환된다.
if( 1 != EVP_CipherUpdate(ctx_enc, cipher_buf, &ci_txtlength, plain_buf, inlength)){
printf("EVP_CipherUpdate error!!! \n");
return -1;
}
printf("\n inlen = %d , ctxlen = %d", inlength, ci_txtlength);
printf("\n\n");
//암호문 출력
fwrite(cipher_buf, 1, ci_txtlength, ofp);
}
그리고 종료과정이다 ㅎㅎ
이것도 역시 주석처리문으로 설명~~
//ctx_enc는 cipher_buf가 무슨파일이 어디까지 암호화 되있는지 알고있다 따라서 cipher_buf에 ci_txtlength만 인자로 넘겨주면 된다.
//EVP_CipherFinal(<#EVP_CIPHER_CTX *ctx#>, <#unsigned char *outm#>, <#int *outl#>)
EVP_CipherFinal(ctx_enc, cipher_buf, &ci_txtlength);
fwrite(cipher_buf, 1, ci_txtlength, ofp);
printf("\n ctxlen = %d \n\n" , ci_txtlength);
return 0;
}
Summary
이러한 암호화 알고리즘은 do_crpyt라는 함수로 뺴줘서 따로 함수를 작성하여 구성을 해보면 아래와 같은 코드가 나온다
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<openssl/rand.h>
#include<openssl/evp.h>
//읽어 들이는 바이트 수
#define NUM_BYTES_READ 32
int do_crypt(const unsigned char *key, const unsigned char *iv, FILE *ifp, FILE *ofp, int isEncrypt);
int main(int argc, const char * argv[]) {
//EVP API를 써서 aes_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_CipherInit_ex(ctx_enc, EVP_aes_128_cbc(), NULL, NULL, NULL, 1))){
printf("EVP_CipherInit_ex error!!\n");
return -1;
}
/**************************************************************************************************
key, iv initialized
Random key, iv use RAND_bytes()
**************************************************************************************************/
printf("\n");
printf("****************************Key, IV initailized********************************\n");
printf("\n");
//key와 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
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;
}
//key 와 iv 값 확인!!
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");
/*
*
* Enctypt
*
*
*/
printf("\n");
printf("******************************Encrypyion**********************************\n");
printf("\n");
//FILE입출력을 통한 암호화를 진행하기 위해 경로를 설정해준다!!
//input 파일은 기존에 미리 있어야한다!! 나와 같이 경로를 꼭 설정 해줘야하는것이 아닌 그냥 파일명만 써도되는데 그건 프로젝트 디렉토리일떄만 가능하다!!!!
FILE *ifp = fopen( "/Users/usun/Desktop/Study/2018_2학기/Security/homework/Seconds_AES_CBC/caesar", "r");
//이건 출력파일인데 이것은 없으면 새로 생성되고, 있어도 새로 덮어씌워진다.
FILE *ofp =fopen("/Users/usun/Desktop/Study/2018_2학기/Security/homework/Seconds_AES_CBC/caesar.usun", "w");
//fopen error check
if(ifp==NULL){
printf("file open error!!\n");
return -1;
}
if(ofp==NULL){
printf("file open error!!\n");
return -1;
}
do_crypt(key, iv, ifp, ofp, 1);
fclose(ifp);
fclose(ofp);
/*
*
* Decryption
*
*/
//FILE입출력을 통한 암호화를 진행하기 위해 경로를 설정해준다!!
//input 파일은 기존에 미리 있어야한다!! 나와 같이 경로를 꼭 설정 해줘야하는것이 아닌 그냥 파일명만 써도되는데 그건 프로젝트 디렉토리일떄만 가능하다!!!!
ifp = fopen( "/Users/usun/Desktop/Study/2018_2학기/Security/homework/Seconds_AES_CBC/caesar.usun", "r");
//이건 출력파일인데 이것은 없으면 새로 생성되고, 있어도 새로 덮어씌워진다.
ofp =fopen("/Users/usun/Desktop/Study/2018_2학기/Security/homework/Seconds_AES_CBC/Dec_caesar", "w");
//fopen error check
if(ifp==NULL){
printf("file open error!!\n");
return -1;
}
if(ofp==NULL){
printf("file open error!!\n");
return -1;
}
do_crypt(key, iv, ifp, ofp, 0);
//각각 할당을 해제시켜준다.
fclose(ifp);
fclose(ofp);
return 0;
}
int do_crypt(const unsigned char *key, const unsigned char *iv, FILE *ifp, FILE *ofp, int isEncrypt){
EVP_CIPHER_CTX *ctx=EVP_CIPHER_CTX_new();
if(ctx == NULL){
printf("EVP_CIPHER_CTX_new is NULL error!!\n");
return -1;
}
if(!(EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, isEncrypt))){
printf("EVP_CipherInit_ex error!!\n");
return -1;
}
unsigned char *input_buf= (unsigned char*)malloc(NUM_BYTES_READ);
unsigned char *output_buf= (unsigned char*)malloc(NUM_BYTES_READ+ EVP_CIPHER_CTX_block_size(ctx));
if(input_buf ==NULL){
printf("cipher_buf_dec allocaion error!! \n");
return -1;
}
if(output_buf ==NULL){
printf("plain_buf_dec allocaion error!! \n");
return -1;
}
int inlen =0 ,outtxtlen =0;
while(1){
//여기서 fread는 ifp에서 1바이트씩 읽어 들여와 총 32바이트를 plain_buf에 저장하는 역할을 한다.
//만약 성공적으로 지정된 바이트 수만큼 읽어 들였다면 읽어들인 원소의 개수가 size_t 자료형으로 반환 된다.
//아니면 0을 반환한다.
inlen= (int)fread(input_buf, 1, NUM_BYTES_READ, ifp);
//정해진 바이트 수에 충족하지 못하면 Final함수를 호출하기위해 반복문을 빠져나간다. 여기선 NUM_BYTES_READ
if(inlen<=0)
break;
//EVP_CipherUpdate(<#EVP_CIPHER_CTX *ctx#>, <#unsigned char *out#>, <#int *outl#>, <#const unsigned char *in#>, <#int inl#>)
//ctx_enc를 참조해서 plain_buf에서 inlength만큼 cipher_buf에 암호문을 쓴다 그리고 암호화된 바이트 수만큼 ci_txtlength로 반환된다.
if( 1 != EVP_CipherUpdate(ctx, output_buf, &outtxtlen, input_buf, inlen)){
printf("EVP_CipherUpdate error!!! \n");
return -1;
}
printf("\n inlen = %d , ctxlen = %d", inlen, outtxtlen);
printf("\n\n");
//암호문 출력
fwrite(output_buf, 1, outtxtlen, ofp);
}
//ctx_enc는 cipher_buf가 무슨파일이 어디까지 암호화 되있는지 알고있다 따라서 cipher_buf에 ci_txtlength만 인자로 넘겨주면 된다.
//EVP_CipherFinal(<#EVP_CIPHER_CTX *ctx#>, <#unsigned char *outm#>, <#int *outl#>)
EVP_CipherFinal(ctx, output_buf, &outtxtlen);
fwrite(output_buf, 1, outtxtlen, ofp);
printf("\n ctxlen = %d \n\n" , outtxtlen);
EVP_CIPHER_CTX_free(ctx);
free(input_buf);
free(output_buf);
return 0;
}
고생하셨습니다~~~
'Security > Cryptography' 카테고리의 다른 글
[OpenSSL/RSA] RSA Private key와 Public key 나누기!!(Serialization) (0) | 2018.11.21 |
---|---|
[OpenSSL/RSA] RSA Sructure & Function (0) | 2018.11.15 |
[OpenSSL] <openssl/evp.h>EVP 함수 정리 (0) | 2018.11.10 |
CH.4 Openssl를 활용한 암호화 프로그래밍<EVP API를 활용한 DES_CBC 암.복호화 실습. OP2.> (0) | 2018.11.08 |
CH.3 Openssl를 활용한 암호화 프로그래밍<EVP API를 활용한 DES_CBC 암.복호화 실습 OP1.> (0) | 2018.11.08 |