2015년 6월 30일 화요일

[C++] openssl add setting

OpenSSL을 사용할일이 생겼다 -_- 윈도우즈에서;;
리눅스에서는 이미 rpm패키지나 소스 컴파일시 별 무리 없이 설치가 되는데 윈도우즈에서는 뭥미~

일단 perl과 gcc를 기냥 사용가능한 MSYS에서 컴파일 해보기로 하고 일단 소스를 받아서 
./configure, make, make install을 때려봤다.... 역시나 안된다 크흑 ㅠㅠ

귀찮음을 안고서 구글님꼐 물어보러 ㄱㄱ

근데 바로 깔끔하게 정리된 블로그 발견 ~~ 님 감사 감사 ㅠㅠ

덕분에 금방 컴파일 했어요 ㅋ 이제 튀어나온 lib, dll 파일을 문서보고서 쓰기만 하면 된다능 ㅋ


출처 : http://blog.daum.net/aswip/8429385
--------------------------------------------------------------------------------------------

※ 들어가기에 앞서 ...

    설명의 편의상 아래와 같은 가정을 전제하고 읽어주세요~ ^^

    openssl-0.9.8k.tar.gz 파일은 H:\openssl-0.9.8k 디렉토리에 압축이 풀렸음.
    OpenSSL 설치 디렉토리는 C:\OpenSSL 이라고 가정함.

1. OpenSSL 다운로드
    http://www.openssl.org/source/openssl-0.9.8k.tar.gz

2. ActivePerl 설치
    openssl 라이브러리를 컴파일 하기 위해서는 perl 이 필요합니다.
    http://downloads.activestate.com/ActivePerl/Windows/5.10/ActivePerl-5.10.0.1004-MSWin32-x86-287188.msi
    다운로드 받은 ActivePerl을 설치합니다. (※ 설치시 Next 버튼만 계속 누르면 됩니다. ^^)

3. 전반적인 컴파일 환경 구축
    압축이 해제된 디렉토리로 이동하여, 아래와 같이 명령어를 입력합니다.
    > perl Configure VC-WIN32 --openssldir=C:\OpenSSL-x86-debug no-shared no-asm threads

    참고로 --openssldir=C:\OpenSSL 과 같이 입력하면 "C:\OpenSSL" 디렉토리에 라이브러리가 설치됩니다.
   
    

4. 구체적인 컴파일 환경 구축
    컴파일 환경은 다음과 같이 3가지 유형중 한 가지를 선택하여 사용이 가능합니다.

    어셈블리어를 사용하지 않는 경우       ms\do_ms.bat          
    NASM 어셈블리어를 사용하는 경우    ms\do_nasm.bat    
    MASM 어셈블리어를 사용하는 경우   ms\do_masm.bat

    만약, 어셈블리어를 사용하지 않을 경우에는, 아래와 같이 명령어를 입력합니다.
   

5. 소스 수정
    윈도우 환경에서는 아래와 같은 파일을, OpenSSL을 컴파일 하기 이전에 수정해 주어야 합니다.
    (아마 다음 OpenSSL 버전에서는 필요없는 과정이 될 수도 있겠군요.. ^^;;)

    1) openssl-0.9.8k\crypto\x509v3\v3_pci.c
        소스파일 최상단에 위치한 주석문에 특수문자가 포함되어 있어서 컴파일 오류가 발생하기 때문에,
        소스파일 가장 위에 주석문을 제거하고 저장함.

    2) openssl-0.9.8k\crypto\x509v3\v3_pcia.c
        소스파일 최상단에 위치한 주석문에 특수문자가 포함되어 있어서 컴파일 오류가 발생하기 때문에,
        소스파일 가장 위에 주석문을 제거하고 저장함.

    3) openssl-0.9.8k\crypto\cversion.c
        cversion.c 105 번째 라인을 아래와 같이 수정함.
        [수정 전] return "OPENSSLDIR: \"" OPENSSLDIR "\"";
        [수정 후] return "OPENSSLDIR: \" OPENSSLDIR \"";

    4) openssl-0.9.8k\crypto\cryptlib.h
        cryptlib.h 84~86번째 라인을 아래와 같이 수정함.
        

    5) openssl-0.9.8k\crypto\opensslconf.h 
        opensslconf.h 107~108번째 라인을 아래와 같이 수정함.
       

6. 컴파일 및 설치
    정적 라이브러리 빌드 인 경우 : nmake -f ms\nt.mak install
    동적 라이브러리 빌드 인 경우 : nmake -f ms\ntdll.mak install
    만약, 동적 라이브러리 컴파일 및 설치를 위해서는 아래와 같이 명령어를 입력합니다.
   

7. 설치 결과
    첨부된 "OpenSSL.zip"파일은 윈도우 환경에서 OpenSSL을 컴파일한 결과물을 압축한 파일입니다.

8. 테스트 코드
#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>

int main(int argc, char **argv)
{
    int nRand = 0;
    
    RAND_bytes((unsigned char *)&nRand, 1);    
    printf("Random number is %d\n", nRand);

    return 0;
}


9. OpenSSL x64 컴파일 및 설치


   To compile the static libraries (both release and debug), this is what you need to do:
  1. Install Perl - www.activestate.com
  2. Run the "Visual Studio 2008 x64 Cross Tools Command Prompt" (Note: The regular command prompt WILL NOT WORK.)
  3. Configure with perl Configure VC-WIN64A no-shared no-idea
  4. Run: ms\do_win64a
  5. EDIT ms\nt.mak and change "32" to "64" in the output dirs:
# The output directory for everything intersting OUT_D=out64.dbg # The output directory for all the temporary muck TMP_D=tmp64.dbg # The output directory for the header files INC_D=inc64 INCO_D=inc64\openssl
  1. EDIT ms\nt.mak and remove bufferoverflowu.lib from EX_LIBS if you get an error about it.
  2. Run: nmake -f ms\nt.mak
  3. EDIT the ms\do_win64a file and ADD "debug" to all lines, except the "ml64" and the last two lines
  4. Run: ms\do_win64a
  5. Repeat steps 4 and 5
  6. EDIT the ms\nt.mak file and ADD /Zi to the CFLAG list!
  7. Run: nmake -f ms\nt.mak
      Itanium 장비환경에서는 
      perl configure VC-WIN64I no-shared no-idea --openssldir=C:\OpenSSL
      ms\do_win64i.bat

2015년 6월 24일 수요일

[MFC] OpenSSL


OpenSSL 윈도우에서 컴파일 및 설치하기

OpenSSL 1.0.0 버전을 활용했다.
윈도우에서 컴파일하는 방법은 OpenSSL을 다운로드 받으면
INSTALL.W32 파일에 자세하게 나와있다.

INSTALL.W32파일에 보면 다음 두개의 툴을 요구하는것을 알 수 있다.

http://www.activestate.com/ActivePerl.
http://nasm.sourceforge.net/

perl과 nasm을 다운받아 설치한다.

OpenSSL 빌드시 nasm을 사용하므로 nasm.exe가 존재하는 폴더를 PATH로 잡아줘야한다.

OpenSSL 폴더로 이동해서 설치를 진행한다.

다음절차를 통해 설치될 경로를 설정하여준다. 
> perl Configure VC-WIN32 --prefix=c:\some\openssl\dir

만일 설치될 폴더를 지정해주지 않는다면 현재 openssl폴더의 out32dll위치에 설치된다.

다음과 같이 nasm을 수행한다.

> ms\do_nasm

nmake를 활용하여 빌드한다.

> nmake -f ms\ntdll.mak

정상적으로 빌드되었는지 테스트하고

> nmake -f ms\ntdll.mak test

앞서 설정된 폴더에 설치를 시작한다.

> nmake -f ms\ntdll.mak install



VC++에서 컴파일하기

설치된 폴더에 존재하는 lib파일과 dll파일을 활용한다.

테스트를 위해 다음과 같은 코드를 준비한 뒤 환경을 설정하도록 한다.

SERVER_ADDRESS에 접속하여 SSL을 맺은 후 간단한 메시지 통신을 하는 클라이언트 코드이다.
 #include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#define PORT 443
#define SERVER_ADDRESS "202.30.50.88"

int main( ) {
  
  char * server_name= SERVER_ADDRESS;
  unsigned short port = PORT;
  
  unsigned int addr;
  struct sockaddr_in server_add;
  struct hostent *host;
  
  WSADATA wsaData;
  SOCKET  conn_socket;
  int socket_type = SOCK_STREAM;
  int retval;
  // SSL 구조체 생성
  SSL_METHOD *meth;
  SSL_CTX* ctx;
  SSL*     ssl;
  X509*    server_cert;
  BIO * errBIO;
 
  if ((retval = WSAStartup(0x202,&wsaData)) != 0) {
    fprintf(stderr,"WSAStartup 함수에서 에러 발생.");
    WSACleanup();
    exit(1);
  }
  if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) {
    WSACleanup();
    exit(1);
  }
   
  if ((errBIO=BIO_new(BIO_s_file())) != NULL)
    BIO_set_fp(errBIO,stderr,BIO_NOCLOSE|BIO_FP_TEXT);
  SSL_load_error_strings();
  SSLeay_add_ssl_algorithms();
  meth = SSLv3_method();
  ctx = SSL_CTX_new(meth);
  if (ctx==NULL) {
    BIO_printf(errBIO,"SSL_CTX 생성 에러");
    ERR_print_errors(errBIO);
    exit(1);
  }
   
  // 서버 이름이 알파벳인DNS로 되어 있을 경우
  if (isalpha(server_name[0])) {    
    host = gethostbyname(server_name);
  }
  // 서버 이름이 IP로 되어 있을 경우
  else  {  
    addr = inet_addr(server_name);
    host = gethostbyaddr((char *)&addr,4,AF_INET);
  }
  if (host == NULL ) {
    fprintf(stderr,"알 수 없는 주소[%s] 입니다, 에러 코드: %d\n",server_name,WSAGetLastError());
    WSACleanup();
    exit(1);
  }
   
  memset(&server_add,0,sizeof(server_add));
  memcpy(&(server_add.sin_addr),host->h_addr,host->h_length);
  server_add.sin_family = host->h_addrtype;
  server_add.sin_port = htons(port);
  conn_socket = socket(AF_INET,socket_type,0);  
  if (conn_socket <0 ) {
    fprintf(stderr,"소켓 생성 에러, 에러 코드:%d\n",WSAGetLastError());
    WSACleanup();
    exit(1);
  }
  printf("[%s] 서버에 연결중..\n",server_name);
  if (connect(conn_socket,(struct sockaddr*)&server_add,sizeof(server_add))== SOCKET_ERROR) {
    fprintf(stderr,"connect 에러, 에러 코드:%d\n",WSAGetLastError());
    WSACleanup();
    exit(1);
  }
     // 세션키를 만들기 위한 랜덤 수 를 위한 Seed공급
  printf("랜덤 수 생성중....");
  RAND_screen();
  printf("랜덤 수 생성 완료.\n");
  ssl = SSL_new(ctx);
  if (ssl == NULL) {
    BIO_printf(errBIO,"SSL 생성 에러");
    ERR_print_errors(errBIO);
    exit(1);
  }
  SSL_set_fd(ssl, conn_socket);
  retval = SSL_connect(ssl);
  if (retval == -1)
  {
    BIO_printf(errBIO,"SSL accept 에러");
    ERR_print_errors(errBIO);
    exit(1);
  }
  const char * currentChipher = SSL_CIPHER_get_name(SSL_get_current_cipher(ssl));
  printf("SSL 연결, 사용 알고리즘 파라메터: [%s]\n",currentChipher);
 
  server_cert = SSL_get_peer_certificate (ssl);
  if (server_cert == NULL) {
    BIO_printf(errBIO,"서버 인증서를 받을 수 없음.");
    ERR_print_errors(errBIO);
    exit(1);
  }
  printf ("Server certificate:\n");
    
  char * retString = NULL;
  // 주체의 DN을 문자열로 얻음
  retString = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
  if (retString == NULL) {
    BIO_printf(errBIO,"서버 인증서에서 주체의 DN을 읽을 수 없음.");
    ERR_print_errors(errBIO);
    exit(1);
  }
  printf ("\t subject: %s\n", retString);
//  free (retString);
  // 발급자의 DN을 문자열로 얻음
  retString = X509_NAME_oneline (X509_get_issuer_name  (server_cert),0,0);
  
  if (retString == NULL) {
    BIO_printf(errBIO,"서버 인증서에서 발급자의 DN을 읽을 수 없음.");
    ERR_print_errors(errBIO);
    exit(1);
  }
  printf ("\t issuer: %s\n", retString);
//  free (retString);

  X509_free (server_cert);

  char buffer[1000];
 
  char message[100] = "GET / HTTP1.1\r\n\r\n.";
  retval = SSL_write (ssl, message, strlen(message));
  if (retval == -1)
  {
    BIO_printf(errBIO,"SSL write 에러");
    ERR_print_errors(errBIO);
    exit(1);
  }
    
  retval = SSL_read (ssl, buffer, sizeof(buffer) - 1); 
  if (retval == -1)
  {
    BIO_printf(errBIO,"SSL read 에러");
    ERR_print_errors(errBIO);
    exit(1);
  }
  buffer[retval] = '\0';
  printf ("서버로 부터 데이터 전송 :[%s], 길이:%d\n",buffer,retval);
  SSL_shutdown (ssl); 
  closesocket(conn_socket);
  SSL_free (ssl);
  SSL_CTX_free (ctx);
  WSACleanup();
}

openssl\include 폴더가 아니라
openssl\inc32 폴더를 VC의 도구->옵션->디렉토리의 포함파일에 포함한다.

라이브러리파일은 OpenSSL을 컴파일하면서 생성된 폴더를 지정해준다.
즉 openssl\out32dll 폴더를 VC의 도구->옵션->디렉토리의 라이브러리파일에 포함한다.

다음 코드를 프로젝트에 포함한다.
openssl\ms\applink.c

위 소스코드를 돌리기 위해 프로젝트의 링크옵션에 포함된 라이브러리 파일은 다음과 같다.
ws2_32.lib ssleay32.lib libeay32.lib

dll 오류가 발생한다면, openssl\out32dll위치에 있는 dll파일을 실행파일 위치에 두면 정상적으로 동작할 것이다.

[MFC] 라이브러리 참조

tools > project and solution > vc directory ++ > include library > path
linker > input > file.lib