OpenlSSL – 데이터 복호화

//      dec_evp.c
//      
//      Copyright 2009 Kim Sung-tae <pchero21@gmail.com>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.

#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/x509.h>
#include <openssl/rand.h>
#include <openssl/pem.h>

#define IN_FILE “encrypt.txt”
#define OUT_FILE “decrypt.txt”

unsigned char* readFile(char *file, int *readLen);
unsigned char* readFileBio(BIO *fileBIO, int *readLen);
unsigned char* addString(unsigned char *destString, int destLen, const unsigned char *addString, int addLen);

int main(int argc, char** argv)
{
    
    // 키와 IV 값은 직접 만든
    unsigned char key[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
    unsigned char iv[] = {1, 2, 3, 4, 5, 6, 7, 8};
    
    unsigned char* outbuf;    // 복호문이 저장될 버퍼
    int outlen, tmplen;
    
    BIO *errBIO = NULL;
    BIO *outBIO = NULL;
    
    // 에러 발생의 경우 해당 에러 스트링 출력을 위해 미리 에러 스트링들을 로딩.
    ERR_load_crypto_strings();
    
    // 표둔 화면 출력 BIO 생성
    if((errBIO = BIO_new(BIO_s_file())) != NULL)
        BIO_set_fp(errBIO, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
    
    // 파일 출력 BIO 생성
    outBIO = BIO_new_file(OUT_FILE, “wb”);
    
    if(!outBIO) {    // 에러가 발생한 경우
        BIO_printf(errBIO, “파일 [%s]을 생성하는데 에러가 발생 했습니다.”, OUT_FILE);
        ERR_print_errors(errBIO);
        exit(1);
    }
    
    // 파일에서 읽어오기
    int len;
    unsigned char* readBuffer = readFile(IN_FILE, &len);
    
    // 암호화 컨텍스트 EVP_CIPHER_CTX 생성, 초기화
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
    
    // 초기화
    EVP_DecryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);
    
    // 초기화가 끝난후에 해야 한다. 복호문 저장할 버퍼 생성
    outbuf = (unsigned char*)malloc(sizeof(unsigned char) * len);
    
    // 업데이트, 마지막 블록을 제외하고 모두 복호화
    if(!EVP_DecryptUpdate(&ctx, outbuf, &outlen, readBuffer, len)) {
        return 0;
    }
    
    // 종료. 마지막 블록을 복호화
    if(!EVP_DecryptFinal_ex(&ctx, outbuf + outlen, &tmplen)) {
        return 0;
    }
    
    // 복호문 길이는 업데이트, 종료 과정에서 나온 결과의 합
    outlen += tmplen;
    EVP_CIPHER_CTX_cleanup(&ctx);
    
    BIO_printf(errBIO, “복호화가 완료되었습니다. 복호문은 다음과 같습니다.nn”);
    outbuf[outlen] = 0;
    printf(“%s”, outbuf);
    
    // 파일에 같은 내용을 출력한다.
    BIO_write(outBIO, outbuf, outlen);

    // 객체 제거
    BIO_free(outBIO);
    
    return 0;
}

unsigned char* readFile(char *file, int *readLen)
{
    unsigned char *retBuffer = NULL;
    unsigned char *buffer = NULL;
    int length = 0;
    
    // 파일 BIO 정의
    BIO *fileBIO = NULL;
    
    // 인자로 넘어온 파일을 열고, 파일 BIO 생성
    fileBIO = BIO_new_file(file, “rb”);
    
    if(!fileBIO) {    // 파일을 여는데 에러가 발생한 경우
        printf(“입력 파일 [%s] 을 여는데 에러가 발생 했습니다.n”, file);
        exit(1);
    }
    
    // 임시로 1000 바이트 만큼의 읽은 데이터를 저장할 버퍼 생성
    buffer = (unsigned char*)malloc(1001);
    *readLen = 0;
    
    while(1) {
        // 파일 BIO에서 1000 바이트 만큼 읽어서 buffer에 저장한다.
        length = BIO_read(fileBIO, buffer, 1000);
        
        // 안전을 위해 버퍼의 끝은 NULL로 채운다.
        buffer[length] = 0;
        
        // 임시로 읽은 1000바이트의 데이터를 리턴 버퍼에 더한다.
        retBuffer = addString(retBuffer, *readLen, buffer, length);
        
        // 지금까지 읽은 데이터의 길이를 더한다.
        *readLen = *readLen + length;
        
        // 만약 지금 파일에서 읽은 데이터의 길이가 꼭 1000 바이트라면 앞으로 더 읽을
        // 데이터가 없을 것이다. 하지만 1000 바이트보다 작다면 더 이상 읽을 데이터가
        // 없을 것이므로 종료한다.
        if(length == 1000)
            // 파일 포인터를 1000 바이트 뒤로 옮긴다.
            BIO_seek(fileBIO, 1000);
        else
            break;
    }
    
    // 객체 삭제
    BIO_free(fileBIO);
    free(buffer);
    
    return retBuffer;
    
}

unsigned char* readFileBio(BIO *fileBIO, int *readLen)
{
    unsigned char* retBuffer = NULL;
    // 임시로 1000바이트 만큼의 읽은 데이터를 저장할 버퍼 생성
    unsigned char* buffer = (unsigned char*)malloc(1001);
    int length = 0;
    
    *readLen = 0;
    
    while(1) {
        // 파일 BIO에서 1000바이트 만큼 읽어서 buffer에 저장한다.
        length = BIO_read(fileBIO, buffer, 1000);
        
        // 안전을 위해 버퍼의 끝은 NULL로 채운다
        buffer[length] = 0;
        
        // 임시로 읽은 1000바이트의 데이터 리턴 버퍼에 더한다.
        retBuffer = addString(retBuffer, *readLen, buffer, length);
        
        // 지금까지 읽은 데이터의 길이를 더한다.
        *readLen = *readLen + length;
        
        // 만약 지금 파일에서 읽은 데이터의 길이가 꼭 1000 바이트라면 앞으로 더 읽을
        // 데이터가 있을 것이다. 하지만 1000 바이트보다 작다면 더 이상 읽을 데이터가
        // 없을 것이므로 종료 한다.
        if(length == 1000)
            // 파일 포인터를 1000바이트 뒤로 옮긴다.
            BIO_seek(fileBIO, 1000);
        else
            break;
    }
    
    return retBuffer;
}

unsigned char* addString(unsigned char *destString, int destLen, const unsigned char *addString, int addLen)
{
    // 리턴 할 버퍼 정의
    unsigned char *retString;
    
    int i;
    
    // 만약 덧붙일 대상 버퍼가 NULL, 이거나 길이가 0이면 덧붙일 대상버퍼가 없는 경우
    // 이므로 새로 생성하고, 덧붙일 버퍼의 내용을 복사한다.
    if((destString == NULL) || (destLen == 0)) {
        // 덧붙일 버퍼의 길이 만큼의 버퍼 생성
        retString = (unsigned char*)malloc(sizeof(unsigned char) * (addLen + 1));
        // 덧붙일 버퍼의 내용을 새로운 버퍼에 복사
        for(i = 0; i < addLen; i++) {
            retString[i] = addString[i];
        }
        
        // 안전을 위해 버퍼의 마지막에 NULL 바이트를 붙인다.
        retString[i] = NULL;
    }
    else {    // 덧붙일 대상 버퍼가 있는 경우 이므로 대상 버퍼의 길이와 덧붙일 버퍼의 길이를
            // 더한 만큼의 버퍼를 새로 생성하고, 두 버퍼의 내용을 새로운 버터에 복사한다.
        retString = (unsigned char*)malloc(sizeof(unsigned char) * (destLen + addLen + 1));
        
        // 덧붙일 대상 버퍼 내용을 새로운 버퍼에 복사
        for(i = 0; i < destLen; i++) {
            retString[i] = destString[i];
        }
        
        // 덧붙일 버퍼의 내용을 새로운 버퍼에 복사
        for(i = 0; i < addLen; i++) {
            retString[i + destLen] = addString[i];
        }
        
        // 안전을 위해 버퍼의 마지막에 NULL 바이트를 붙인다.
        retString[i + destLen] = NULL;
    }
    
    // 메모리에서 삭제
    free(destString);
    
    return retString;
}

OpenSSL – 데이터 암호화



#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/rand.h>
#include <openssl/pem.h>


 


#define IN_FILE “./plain.txt”   // plain file
#define OUT_FILE “./encrypt.txt”        // cipher file


 


unsigned char* readFile(char *file, int *readLen);
unsigned char *readFileBio(BIO *fileBIO, int *readLen);
unsigned char *addString(unsigned char *destString, int destLen, const unsigned char *addString, int addLen);


 


int main(int argc, char* argv[])
{
        // 키와 IV값은 편의를 위해 직접 만든다.


        unsigned char key[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
        unsigned char iv[] = {1, 2, 3, 4, 5, 6, 7, 8};


        int len;
        unsigned char *readBuffer, *outbuf;
        int outlen, tmplen;



        BIO *errBIO = NULL;
        BIO *outBIO = NULL;


 


        // 에러 발생의 경우 해당 에러 스트링 출력을 위해 미리 에러 스트링들을 로딩.
        ERR_load_crypto_strings();


 


        // 표준 화면 출력 BIO 생성
        if((errBIO = BIO_new(BIO_s_file())) != NULL)
                BIO_set_fp(errBIO, stderr, BIO_NOCLOSE|BIO_FP_TEXT);


 


        // 파일 출력 BIO 생성
        outBIO = BIO_new_file(OUT_FILE, “wb”);
        if(!outBIO) {
                BIO_printf(errBIO, “Can not create file [%s]!”, OUT_FILE);
                ERR_print_errors(errBIO);
                exit(1);
        }


 


        // 파일에서 데이터 읽어오기
        readBuffer = readFile(IN_FILE, &len);


 


        // 암호화 컨텍스트 EVP_CIPHER_CTX 생성, 초기화
        EVP_CIPHER_CTX ctx;
        EVP_CIPHER_CTX_init(&ctx);


 


        // 초기화
        EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);


 


        // 초기화가 끝난후에 해야 한다. 암호문 저장할 버퍼 생성
        outbuf = (unsigned char*)malloc(sizeof(unsigned char) * (len + EVP_CIPHER_CTX_block_size(&ctx)));


 


        // 업데이트. 마지막 블록을 제외하고 모두 암호화.
        if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, readBuffer, strlen((char *)readBuffer))) {
                return 0;
        }


 


        // 종료. 마지막 블록을 암호화
        if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen)) {
                return 0;
        }


 


        // 암호문 길이는 업데이트. 종료 과정에서 나온 결과의 합
        outlen += tmplen;
        EVP_CIPHER_CTX_cleanup(&ctx);


        BIO_printf(errBIO, “Create Cipher Complete.nSaving [%s] filen”, OUT_FILE);


 


        // 파일에 같은 내용을 출력한다.
        BIO_write(outBIO, outbuf, outlen);


 


        // 객체 제거
        BIO_free(outBIO);


 


        return 0;
}


 


 


unsigned char* readFile(char *file, int *readLen)
{
        unsigned char *retBuffer = NULL;
        unsigned char *buffer = NULL;
        int length = 0;


 


        // 파일 BIO 정의
        BIO *fileBIO = NULL;


 


        // 인자로 넘어온 파일을 열고, 파일 BIO 생성
        fileBIO = BIO_new_file(file, “rb”);
        if(!fileBIO) {
                printf(“file open(%s) error!n”, file);
                exit(1);
        }


 


        // 임시로 1000 바이트 만큼의 읽은 데이터를 저장할 버퍼 생성.
        buffer = (unsigned char*)malloc(1001);
        *readLen = 0;


 


        while(1) {


                // 파일 BIO에서 1000 바이트 만큼 읽어서 buffer에 저장 한다.
                length = BIO_read(fileBIO, buffer, 1000);


 


                // 안전을 위해 버퍼의 끝은 NULL로 채운다.
                buffer[length] = 0;


 


                // 임시로 읽은 1000 바이트의 데이터를 리턴 버퍼에 더한다.
                retBuffer = addString(retBuffer, *readLen, buffer, length);


 


                // 지금까지 읽은 데이터의 길이를 더한다.
                *readLen = *readLen + length;


                // 만약 지금 파일에서 읽은 데이터의 길이가 꼭 1000 바이트라면 앞으로 더 읽을


                // 데이터가 있을 것이다. 하지만 1000 바이트보다 작다면 더 이상 읽을 데이터가


                // 없을 것이므로 종료한다.


                if(length == 1000)


                      // 파일 포인터를 1000 바이트 뒤로 옮긴다.
                      BIO_seek(fileBIO, 1000);
                else
                      break;
        }


 


        // 객체 삭제


        BIO_free(fileBIO);


        free(buffer);


 


        return retBuffer;
}


 



unsigned char* addString(unsigned char* destString, int destLen, const unsigned char *addString, int addLen)
{


        // 리턴할 버퍼 정의
        unsigned char *retString;
        int i;


 


        // 만약 덧붙일 대상 버퍼가 NULL, 이거나 길이가 0이면 덧붙일 대상버퍼가 없는 경우


        // 이므로 새로 생성하고, 덧붙일 버퍼의 내용을 복사 한다.


        if((destString == NULL) || (destLen == 0)) {


                // 덧붙일 버퍼 길이 만큼의 버퍼 생성
                retString = (unsigned char*)malloc(sizeof(unsigned char) * (addLen + 1));


 


                // 덧붙일 버퍼의 내용을 새로운 버퍼에 복사
                for(i = 0; i < addLen; i++) {
                      retString[i] = addString[i];
                }


                // 안전을 위해 버퍼의 마지막에 NULL 바이트를 뭍인다.
                retString[i] = NULL;
        }


        // 덧붙일 대상 버퍼가 있는 경우이므로 덧붙일 대상 버퍼의 길이를


        // 더한 만큼의 버퍼를 새로 생성하고, 두 버퍼의 내용을 새로운 버퍼에 복사한다.
        else {


                // 대상 버퍼의 길이와 덧붙일 길이를 더한 만큼의 버퍼 생성
                retString = (unsigned char*)malloc(sizeof(unsigned char) * (destLen + addLen + 1));


                // 덧붙일 대상 버퍼 내용을 새로운 버퍼에 복사
                for(i = 0; i < destLen; i++) {
                      retString[i] = destString[i];
                }


                // 덧붙일 버퍼의 내용을 새로운 버퍼에 복사
                for(i = 0; i < addLen; i++) {
                      retString[i + destLen] = addString[i];
                }


                // 안전을 위해 버퍼의 마지막에 NULL 바이트를 붙인다.
                retString[i + destLen] = NULL;
        }


 


        // 메모리에서 삭제


        free(destString);


        return retString;
}


OpenSSL- 키와 IV 생성 프로그램

 


 


#include <stdio.h>


#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/rand.h>


 


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


 


        // salt bufer length = 8
        unsigned char salt[8];
        // EVP_CIPHER = Cipher structure
        const EVP_CIPHER *cipher = NULL;
        // password pointer. password = “aaaa”
        char *password = “aaaa”;


        // 키와 IV가 저장될 변수를 정의하고 길이는 OpenSSL에서 알아서 정해준다.
        unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];


 


        int ret = 0;


        // PRNG를 통해 랜덤 수를 만들고 그 값을 Salt에 저장한다. 길이는 8
        ret = RAND_pseudo_bytes(salt, 8);


        // PRNG에서 에러가 발생할 경우 에러 메시지를 출력하고 프로그램을 종료한다.
        if(ret < 0) {
                printf(“Can’t generate random number.n”);
                return 0;
        }


 


        // 암호화 구조체의 인스턴스를 생성. 여기서는 DES의 ECB 모드의 암호화 구조체 생성
        cipher = EVP_des_ecb();


 


        // 키와 IV를 생성함. 인자는 암호화 구조체, 다이제스트 구조체, salt 값, 패스워드


        // 카운트는 생성될 키와 IV를 저장할 변수


        // 다이제스트 구조체는 EVP_md5() 함수를 통해 생성. 카운트는 한번
        EVP_BytesToKey(cipher, EVP_md5(), salt, (unsigned char *)password, strlen(password), 1, key, iv);


        // display salt
        for(i = 0; i < sizeof(salt); i++) {
                printf(“%02X”, salt[i]);
        }


        // Display key
        if(cipher->key_len > 0) {
                for(i = 0; i < cipher->key_len; i++) {
                        printf(“%02X”, key[i]);
                }
        }


 


        // Display IV
        if(cipher->iv_len > 0) {
                for(i = 0; i < cipher->iv_len; i++) {
                        printf(“%02X”, iv[i]);
                }
        }


 


        return 0;
}

OpenSSL-RANDOM 함수 사용하기

#include <stdio.h>
#include <openssl/err.h>
#include <openssl/rand.h>


int main(int argc, char* argv[])
{
        int i;
        int retVal = 0;
        // 랜덤 수의 길이는 64로 한다.


        int length = 64;
        // PRNG에 공급할 seed 생성
        RAND_status();
        // 생성할 랜덤 수 길이만큼의 버퍼 생성
        unsigned char *buffer = (unsigned char*)malloc(sizeof(unsigned char) * length);


        // run PRNG
        retVal = RAND_bytes(buffer, length);
        if(retVal <= 0) {
                printf(“error encount!n”);
                return 0;
        }


        // print random number
        printf(“Random number = “);


        for(i = 0; i < length; i++)
                printf(“%c”, buffer[i]);


        return 1;
}


 


 Windows의 경우 RAND_status() 를 RAND_screen() 으로 바꿔주면 된다.