일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
29 | 30 | 31 |
- Django REST framework
- flutter firestore
- Django REST
- FLUTTER
- mfc
- Android P
- Kotlin
- UWP
- Rxjava2
- Android
- C/C++
- android architecture component
- Python
- RxJava
- 알고리즘
- Flutter TextField
- 코틀린
- 안드로이드
- RxAndroid
- Java
- C
- NDK
- livedata
- C++
- dart
- kodility
- 안드로이드 구글맵
- 프로그래머스
- android push
- Django REST Android
- Today
- Total
개발하는 두더지
[C/C++/MFC] C++ HTTP Web Server에서 HTTPS/SSL 사용하기 (AJAX 통신) 본문
# HTTP VS HTTPS
HTTPS에서 마지막의 S는 Over Secure Socket Layer의 약자로 강화된 HTTP이다.
HTTP는 암호화되지 않은 방법으로 데이터를 전송하므로 서버와 클라이언트가 주고 받는 메시지를 감청하는 것이 매우 쉽다.
예를들어 로그인을 위해서 서버로 비밀번호를 전송하거나, 또는 중요한 기밀 문서를 열람하는 과정에서 악의적인 감청이나
데이터의 변조등을 보안하는 것이 HTTPS다.
# SSL 디지털 인증서
SSL 인증서는 클라이언트와 서버간의 통신을 제 3자가 보증해주는 전자화된 문서다. 클라이언트가 서버에 접속한 직후에
서버는 클라이언트에게 이 인증서 정보를 전달한다. SSL과 SSL 디지털 인증서를 이용했을 때의 이점은 아래와 같다.
- 통신 내용이 공격자에게 노출되는 것을 막을 수 있다.
- 클라이언트가 접속하려는 서버가 신뢰할수 있는 서버인지를 판단할 수 있다.
- 통신 내용의 악의적인 변경을 방지할 수 있다.
SSL 인증서의 역할은 다소 복잡하기 때문에 인증서의 매커니즘을 이해하기 위한 몇가지 지식들을 알고 있어야 한다.
인증서의 기능은 크게 두가지다.
1. 클라이언트가 접속한 서버가 신뢰할 수 있는 서버임을 보장한다.
2. SSL 통신에 사용할 공개키를 클라이언트에게 제공한다.
# CA
SSL을 통해서 암호화된 통신을 제공하려는 서비스는 CA를 통해서 인증서를 구입해야 한다.
# 사설 인증기관
개발이나 사적인 목적을 위해서 SSL의 암호화 기능을 이용하려한다면 자신이 직접 CA의 역할을 할수도 있지만
공인된 인증서가 아니기 때문에 경고를 출력한다.
# SSL 인증서의 내용
SSL 인증서에는 다음과 같은 정보가 포함되어 있다.
1. 서비스의 정보 ( 인증서를 발급한 CA, 서비스의 도메인 등등) : 클라이언트가 접속한 서버가 의도한 서버가 맞는지
2. 서버 측 공개키 ( 공개키의 내용 , 공개키의 암호화 방법 ) : 서버와 통신할 때 사용할 공개키와 그 공개키의 암호화 방법
# 구현 코드
Client 쪽 요청 보내기/받기 코드
- AJAX 통신
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | function makeRequest(url) { try{ //BrowserType Set BrowserType = CheckBrowser(); if(BrowserType >= 6 && BrowserType <= 7) // code for IE8이하 { httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } else // code for IE9+, Firefox, Chrome, Opera, Safari { httpRequest = new XMLHttpRequest(); } if (!httpRequest) { alert('Giving up :( Cannot create an XMLHTTP instance'); return false; } httpRequest.onreadystatechange = alertContents; httpRequest.open('POST', url, true); httpRequest.send(); } catch(e){ alert("catch : " + e.responseText); } } function alertContents() { if (httpRequest.readyState === 4) { if (httpRequest.status === 200) { if(httpRequest.responseText == "Success"){ } else if(httpRequest.responseText >= VERSION){ // 버전 일치 ShowWaitMsg(false); document.location = LIST_URL; } else{ alert("업데이트 필요"); ShowWaitMsg(false); } } else { //alert('There was a problem with the request.'); setBlock(); } } } | cs |
서버에서 HttpReceiveHttpRequest 를 실행한 후에 클라이언트의 요청을 대기한다.
클라이언트에서 httpRequest.send를 실행하면 서버의 HttpReceiveHttpRequest 다음 코드가 진행된다.
서버에서 요청을 처리 한 후 SendHttpResponse 를 실행하면 클라이언트의 alertContents가 동작한다.
readyState = 4 , status = 200 을 받으면 다음 AJAX 코드가 진행된다.
SERVER 쪽 요청 받기/응답 코드
- 1. 초기화
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | int Init(void) { ULONG retCode; int UrlAdded = 0; HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1; // // Initialize HTTP Server APIs // retCode = HttpInitialize( HttpApiVersion, HTTP_INITIALIZE_SERVER | HTTP_INITIALIZE_CONFIG, // Flags NULL // Reserved ); if (retCode != NO_ERROR) { wprintf(L"HttpInitialize failed with %lu \n", retCode); return retCode; } // // Create a Request Queue Handle // retCode = HttpCreateHttpHandle( &m_hReqQueue, // Req Queue 0 // Reserved ); if (retCode != NO_ERROR) { wprintf(L"HttpCreateHttpHandle failed with %lu \n", retCode); return retCode; } // HTTPS BYTE hash[] = {0x57, 0xfd, 0x3f, 0xee, 0x8f, 0x47, 0x3f, 0x7e, 0xbe, 0x0d, 0xb8, 0xa8, 0x1a, 0x38, 0x0e, 0x44, 0x56, 0xa5, 0x91, 0x9f}; GUID gidReference = {0x3997d7bf, 0xae86, 0x4665, {0xb5, 0x06, 0xe2, 0x4e, 0x32, 0x9a, 0x47, 0xe1}}; SOCKADDR_IN sa; memset(&sa,0,sizeof(sa)); sa.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); sa.sin_family = AF_INET; sa.sin_port = htons(12332); HTTP_SERVICE_CONFIG_SSL_SET scssl; scssl.KeyDesc.pIpPort = (SOCKADDR*)&sa; scssl.ParamDesc.SslHashLength = sizeof(hash); scssl.ParamDesc.pSslHash = (void *)hash; scssl.ParamDesc.AppId = gidReference; scssl.ParamDesc.pSslCertStoreName = L"root"; scssl.ParamDesc.DefaultCertCheckMode = 0; scssl.ParamDesc.DefaultRevocationFreshnessTime = 0; scssl.ParamDesc.DefaultRevocationUrlRetrievalTimeout = 0; scssl.ParamDesc.pDefaultSslCtlIdentifier = NULL; scssl.ParamDesc.pDefaultSslCtlStoreName = NULL; scssl.ParamDesc.DefaultFlags = 0; DWORD retVal = ::HttpSetServiceConfiguration(NULL, HttpServiceConfigSSLCertInfo, &scssl, sizeof(scssl), NULL); if(retVal != NO_ERROR) { return -1; } return 0; } | cs |
* HttpInitialize 에서 HTTP_INITIALIZE_CONFIG 가 반드시 있어야 한다. 없으면 HTTPS를 이용 못한다.
- 2. 추가
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int AddUrl(LPCWSTR wszUrl) { ULONG retCode; retCode = HttpAddUrl( m_hReqQueue, // Req Queue wszUrl, // Fully qualified URL NULL // Reserved ); if (retCode != NO_ERROR) { wprintf(L"HttpAddUrl failed with %lu \n", retCode); return retCode; } } | cs |
- 3. HTTP 요청 받기
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | DWORD DoReceiveRequests( IN HANDLE hReqQueue ) { ULONG result; HTTP_REQUEST_ID requestId; DWORD bytesRead; PHTTP_REQUEST pRequest; PCHAR pRequestBuffer; ULONG RequestBufferLength; // // Allocate a 2 KB buffer. This size should work for most // requests. The buffer size can be increased if required. Space // is also required for an HTTP_REQUEST structure. // RequestBufferLength = sizeof(HTTP_REQUEST) + 2048; pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength ); if (pRequestBuffer == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } pRequest = (PHTTP_REQUEST)pRequestBuffer; // // Wait for a new request. This is indicated by a NULL // request ID. // HTTP_SET_NULL_ID( &requestId ); for(;;) { RtlZeroMemory(pRequest, RequestBufferLength); result = HttpReceiveHttpRequest( hReqQueue, // Req Queue requestId, // Req ID 0, // Flags pRequest, // HTTP request buffer RequestBufferLength,// req buffer length &bytesRead, // bytes received NULL // LPOVERLAPPED ); if(NO_ERROR == result) { // // Worked! // switch(pRequest->Verb) { case HttpVerbGET: wprintf(L"Got a GET request for %ws \n", pRequest->CookedUrl.pFullUrl); result = SendHttpResponse( hReqQueue, pRequest, 200, "OK", "Hey! You hit the server \r\n" ); break; case HttpVerbPOST: wprintf(L"Got a POST request for %ws \n", pRequest->CookedUrl.pFullUrl); result= SendHttpPostResponse(hReqQueue, pRequest); break; default: wprintf(L"Got a unknown request for %ws \n", pRequest->CookedUrl.pFullUrl); result = SendHttpResponse( hReqQueue, pRequest, 503, "Not Implemented", NULL ); break; } if(result != NO_ERROR) { break; } // // Reset the Request ID to handle the next request. // HTTP_SET_NULL_ID( &requestId ); } else if(result == ERROR_MORE_DATA) { // // The input buffer was too small to hold the request // headers. Increase the buffer size and call the // API again. // // When calling the API again, handle the request // that failed by passing a RequestID. // // This RequestID is read from the old buffer. // requestId = pRequest->RequestId; // // Free the old buffer and allocate a new buffer. // RequestBufferLength = bytesRead; FREE_MEM( pRequestBuffer ); pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength ); if (pRequestBuffer == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; break; } pRequest = (PHTTP_REQUEST)pRequestBuffer; } else if(ERROR_CONNECTION_INVALID == result && !HTTP_IS_NULL_ID(&requestId)) { // The TCP connection was corrupted by the peer when // attempting to handle a request with more buffer. // Continue to the next request. HTTP_SET_NULL_ID( &requestId ); } else { break; } } if(pRequestBuffer) { FREE_MEM( pRequestBuffer ); } return result; } | cs |
- 4. HTTP 응답 보내기
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | DWORD SendHttpResponse( IN HANDLE hReqQueue, IN PHTTP_REQUEST pRequest, IN USHORT StatusCode, IN PSTR pReason, IN PSTR pEntityString ) { HTTP_RESPONSE response; HTTP_DATA_CHUNK dataChunk; DWORD result; DWORD bytesSent; // // Initialize the HTTP response structure. // INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason); // // Add a known header. // ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html"); if(pEntityString) { // // Add an entity chunk. // dataChunk.DataChunkType = HttpDataChunkFromMemory; dataChunk.FromMemory.pBuffer = pEntityString; dataChunk.FromMemory.BufferLength = (ULONG) strlen(pEntityString); response.EntityChunkCount = 1; response.pEntityChunks = &dataChunk; } // // Because the entity body is sent in one call, it is not // required to specify the Content-Length. // result = HttpSendHttpResponse( hReqQueue, // ReqQueueHandle pRequest->RequestId, // Request ID 0, // Flags &response, // HTTP response NULL, // pReserved1 &bytesSent, // bytes sent (OPTIONAL) NULL, // pReserved2 (must be NULL) 0, // Reserved3 (must be 0) NULL, // LPOVERLAPPED(OPTIONAL) NULL // pReserved4 (must be NULL) ); if(result != NO_ERROR) { wprintf(L"HttpSendHttpResponse failed with %lu \n", result); } return result; } | cs |
'C,C++' 카테고리의 다른 글
C++ 11/14/17 이란? (0) | 2016.07.22 |
---|---|
[C/C++/MFC] URLEncode, URLDecode (2) | 2016.07.22 |
[C/C++/MFC] 윈도우 서비스에서 응용 프로그램 실행하기(CreateProcessAsUser) (8) | 2016.07.22 |
[C/C++/MFC] Unicode Multibyte UTF8 변환 (0) | 2016.07.22 |
[C/C++/MFC] 윈도우 서비스 생성/시작/중지/삭제 (0) | 2016.07.22 |