Share via


WinHTTP의 SSL

Microsoft WinHTTP(Windows HTTP 서비스)는 클라이언트 인증서를 포함한 SSL(Secure Sockets Layer) 트랜잭션을 지원합니다. 이 항목에서는 SSL 트랜잭션과 관련된 개념과 WinHTTP를 사용하여 처리하는 방법에 대해 설명합니다.

SSL(Secure Sockets Layer)

SSL은 보안 HTTP 트랜잭션을 보장하기 위한 설정된 표준입니다. SSL은 클라이언트와 서버 간의 모든 트랜잭션에서 최대 128비트 암호화를 수행하는 메커니즘을 제공합니다. 이를 통해 클라이언트는 서버 인증서를 사용하여 서버가 신뢰할 수 있는 엔터티에 속하는지 확인할 수 있습니다. 또한 서버에서 클라이언트 인증서를 사용하여 클라이언트의 ID를 확인할 수 있습니다.

이러한 각 문제 암호화, 서버 ID 및 클라이언트 ID는 클라이언트가 HTTPS(보안 하이퍼텍스트 전송 프로토콜) 서버에서 리소스를 처음 요청할 때 발생하는 SSL 핸드셰이크에서 협상됩니다. 기본적으로 클라이언트와 서버는 각각 필수 및 기본 설정 목록을 제공합니다. 일반적인 요구 사항 집합을 합의하고 충족할 수 있는 경우 SSL 연결이 설정됩니다.

WinHTTP는 SSL을 사용하기 위한 높은 수준의 인터페이스를 제공합니다. SSL 핸드셰이크 및 트랜잭션의 세부 정보는 내부적으로 처리되지만 WinHTTP를 사용하면 암호화 수준을 검색하고, 보안 프로토콜을 지정하고, 서버 및 클라이언트 인증서와 상호 작용할 수 있습니다. 다음 섹션에서는 SSL 프로토콜 버전을 선택하고, 서버 인증서를 검사하고, HTTPS 서버로 보낼 클라이언트 인증서를 선택하는 WinHTTP 기반 애플리케이션을 만드는 방법에 대해 자세히 설명합니다.

서버 인증서

서버 인증서는 서버에서 클라이언트로 전송되므로 클라이언트가 서버에 대한 공개 키를 가져오고 인증 기관에서 서버를 확인했는지 확인할 수 있습니다. 인증서는 다양한 형식의 데이터를 포함할 수 있습니다. 예를 들어 X.509 인증서에는 인증서 형식, 인증서 일련 번호, 인증서 서명에 사용되는 알고리즘, 인증서를 발급한 CA(인증 기관) 이름, 인증서를 요청하는 엔터티의 이름 및 공개 키, CA의 서명이 포함됩니다.

WinHTTP API(애플리케이션 프로그래밍 인터페이스)를 사용하는 경우 WinHttpQueryOption 을 호출하고 WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT 플래그를 지정하여 서버 인증서를 검색할 수 있습니다. 서버 인증서는 WINHTTP_CERTIFICATE_INFO 구조로 반환됩니다. 인증서 컨텍스트를 검색하려면 대신 WINHTTP_OPTION_SERVER_CERT_CONTEXT 플래그를 지정합니다.

서버 인증서에 오류가 포함된 경우 상태 콜백 함수에서 오류에 대한 세부 정보를 가져올 수 있습니다. WINHTTP_CALLBACK_STATUS_SECURE_FAILURE 알림은 서버 인증서의 오류를 나타냅니다. lpvStatusInformation 매개 변수에는 하나 이상의 자세한 오류 플래그가 포함되어 있습니다. 자세한 내용은 WINHTTP_STATUS_CALLBACK 참조하세요.

클라이언트 인증서

SSL 핸드셰이크 중에 서버에 인증이 필요할 수 있습니다. 클라이언트는 서버에 유효한 클라이언트 인증서를 제공하여 인증됩니다. WinHTTP를 사용하면 로컬 인증서 저장소에서 인증서를 선택하고 보낼 수 있습니다. 다음 섹션에서는 WinHTTP API 또는 WinHttpRequest 개체를 사용할 때 클라이언트 인증서를 제공하는 프로세스에 대해 설명합니다.

WinHTTP API

WinHttpSendRequestWinHttpReceiveResponse는 HTTPS 서버에 인증이 필요하기 때문에 요청이 실패했음을 나타내지 못할 수 있습니다. 이러한 경우 GetLastError 를 호출하여 ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED 반환합니다. 이 오류가 수신되면 적절한 CryptoAPI 함수를 사용하여 적절한 인증서를 찾습니다. 이 인증서는 WINHTTP_OPTION_CLIENT_CERT_CONTEXT 플래그를 사용하여 WinHttpSetOption을 호출하여 다음 요청과 함께 전송되어야 함을 나타냅니다.

다음 코드 예제에서는 인증서 저장소 를 열고 ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED 오류가 반환된 후 주체 이름을 기반으로 인증서를 찾는 방법을 보여줍니다.

  if( !WinHttpReceiveResponse( hRequest, NULL ) )
  {
    if( GetLastError( ) == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED )
    {
      //MY is the store the certificate is in.
      hMyStore = CertOpenSystemStore( 0, TEXT("MY") );
      if( hMyStore )
      {
        pCertContext = CertFindCertificateInStore( hMyStore,
             X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
             0,
             CERT_FIND_SUBJECT_STR,
             (LPVOID) szCertName, //Subject string in the certificate.
             NULL );
        if( pCertContext )
        {
          WinHttpSetOption( hRequest, 
                            WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                            (LPVOID) pCertContext, 
                            sizeof(CERT_CONTEXT) );
          CertFreeCertificateContext( pCertContext );
        }
        CertCloseStore( hMyStore, 0 );

        // NOTE: Application should now resend the request.
      }
    }
  }

클라이언트 인증서가 포함된 요청을 다시 보내기 전에 지원되는 암호화 수준이 애플리케이션에 허용되는지 확인할 수 있습니다. WinHttpQueryOption을 호출하고 WINHTTP_OPTION_SECURITY_FLAGS 플래그를 지정하여 사용되는 암호화 수준을 결정합니다.

SSL 클라이언트 인증에 대한 발급자 목록 검색

WinHttp 클라이언트 애플리케이션이 SSL 클라이언트 인증이 필요한 보안 HTTP 서버에 요청을 보내면 애플리케이션이 클라이언트 인증서를 제공하지 않은 경우 WinHttp에서 ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED 반환합니다. Windows Server 2008 및 Windows Vista에서 실행되는 컴퓨터의 경우 WinHttp를 사용하면 애플리케이션이 인증 챌린지에서 서버에서 제공하는 인증서 발급자 목록을 검색할 수 있습니다. 발급자 목록은 서버에서 클라이언트 인증서를 발급할 수 있는 권한을 부여한 CA(인증 기관) 목록을 지정합니다. 애플리케이션은 발급자 목록을 필터링하여 필요한 인증서를 가져옵니다.

WinHttpSendRequest 또는 WinHttpReceiveResponseERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED 반환하면 WinHttp 클라이언트 애플리케이션이 발급자 목록을 검색합니다. 이 오류가 반환되면 애플리케이션은 WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST 옵션을 사용하여 WinHttpQueryOption을 호출합니다. lpBuffer 매개 변수는 SecPkgContext_IssuerListInfoEx 구조체에 대한 포인터를 포함할 수 있을 만큼 커야 합니다. 다음 코드 예제에서는 발급자 목록을 검색 하는 방법을 보여 주세요.

#include <windows.h>
#include <winhttp.h>
#include <schannel.h>

//...

void GetIssuerList(HINTERNET hRequest)
{
  SecPkgContext_IssuerListInfoEx* pIssuerList = NULL;
  DWORD dwBufferSize = sizeof(SecPkgContext_IssuerListInfoEx*);

  if (WinHttpQueryOption(hRequest,
           WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST,
           &pIssuerList,
           &dwBufferSize) == TRUE)
  {
    // Use the pIssuerList for cert store filtering.
    GlobalFree(pIssuerList); // Free the issuer list when done.
  }
}

아래 코드 예제와 같이 SecPkgContext_IssuerListInfoEx 구조의 정보인 cIssuersaIssuers를 사용하여 인증서를 검색할 수 있습니다. 자세한 내용은 CertFindChainInStore를 참조하세요.

PCERT_CONTEXT pClientCert = NULL;
PCCERT_CHAIN_CONTEXT pClientCertChain = NULL;

CERT_CHAIN_FIND_BY_ISSUER_PARA SrchCriteria;
::ZeroMemory(&SrchCriteria, sizeof(CERT_CHAIN_FIND_BY_ISSUER_PARA));
SrchCriteria.cbSize = sizeof(CERT_CHAIN_FIND_BY_ISSUER_PARA);

SrchCriteria.cIssuer = pIssuerList->cIssuers;
SrchCriteria.rgIssuer = pIssuerList->aIssuers;

pClientCertChain = CertFindChainInStore(
            hClientCertStore,
            X509_ASN_ENCODING,
            CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG |
            // Do not perform wire download when building chains.
            CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG,
            // Do not search pCacheEntry->_ClientCertStore 
            // for issuer certs.
            CERT_CHAIN_FIND_BY_ISSUER,
            &SrchCriteria,
            NULL);

if (pClientCertChain)
{
    pClientCert = (PCERT_CONTEXT) pClientCertChain->rgpChain[0]->rgpElement[0]->pCertContext;

    CertDuplicateCertificateContext(pClientCert);

    CertFreeCertificateChain(pClientCertChain);

    pClientCertChain = NULL;
}

선택적 클라이언트 SSL 인증서

Windows Server 2008 및 Windows Vista부터 WinHttp API는 선택적 클라이언트 인증서를 지원합니다. 서버가 클라이언트 인증서, WinHttpSendRequest 또는 WinHttpRecieveResponse 를 요청하면 ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED 오류가 반환됩니다. 서버가 인증서를 요청하지만 인증서가 필요하지 않은 경우 애플리케이션은 인증서가 없음을 나타내기 위해 이 옵션을 지정할 수 있습니다. 서버는 다른 인증 체계를 선택하거나 서버에 대한 익명 액세스를 허용할 수 있습니다. 애플리케이션은 다음 코드 예제와 같이 WinHttpSetOptionlpBuffer 매개 변수에 WINHTTP_NO_CLIENT_CERT_CONTEXT 매크로를 지정합니다.

BOOL fRet = WinHttpSetOption ( hRequest,
                               WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                               WINHTTP_NO_CLIENT_CERT_CONTEXT,
                               0);

WINHTTP_NO_CLIENT_CERT_CONTEXT 설정되고 서버에 여전히 클라이언트 인증서가 필요한 경우 403 HTTP 상태 코드를 보낼 수 있습니다. 자세한 내용은 WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST 옵션을 참조하세요.

WinHttpRequest 개체

WinHttpRequest 개체의 SetClientCertificate 메서드를 사용하여 요청과 함께 서버에 보낼 클라이언트 인증서를 선택합니다. SetClientCertificate 메서드를 사용하여 인증서 선택 문자열을 지정하여 인증서를 선택합니다. 인증서 선택 문자열은 백 슬래시로 구분된 인증서 위치, 인증서 저장소 및 주체 이름으로 구성됩니다. 다음 표에서는 이 선택 문자열에 대한 구성 요소를 나열합니다.

구성 요소 Description 가능한 값
위치 인증서가 저장되는 레지스트리 키를 결정합니다. 가능한 값은 인증서 저장소가 HKEY_LOCAL_MACHINE 아래에 있음을 나타내는 "LOCAL_MACHINE"입니다.
및 "CURRENT_USER"을 사용하여 인증서 저장소 가 가장되지 않은 HKEY_CURRENT_USER 아래에 있음을 나타냅니다.
이 구성 요소는 대/소문자를 구분합니다.
인증서 저장소 관련 인증서가 포함된 인증서 저장소 의 이름을 나타냅니다. 일반적인 인증서 저장소 는 "MY", "Root" 및 "TrustedPeople"입니다. 이 구성 요소는 대/소문자를 구분합니다.
주체 이름 지정된 인증서 저장소 내에서 인증서를 식별합니다. 이 구성 요소에 대해 지정된 문자열을 포함하는 첫 번째 인증서가 선택됩니다. 주체 이름은 모든 문자열일 수 있습니다. 빈 문자열은 인증서 저장소 의 첫 번째 인증서를 사용해야 했음을 나타냅니다. 이 구성 요소는 대/소문자를 구분하지 않습니다.

인증서 저장소 이름 및 위치는 선택적 구성 요소입니다. 그러나 인증서 저장소를 지정하는 경우 해당 인증서 저장소의 위치도 지정해야 합니다. 기본 위치는 CURRENT_USER 기본 인증서 저장소 는 "MY"입니다.

다음 코드 예제에서는 "내 Middle-Tier 인증서"라는 제목의 인증서를 HKEY_LOCAL_MACHINE 레지스트리의 "개인" 인증서 저장소에서 선택하도록 지정하는 방법을 보여줍니다.

HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")

참고

일부 언어에서는 백슬래시가 이스케이프 문자입니다. 이를 고려하도록 인증서 선택 문자열을 수정해야 합니다. 예를 들어 Microsoft JScript에서 인접한 백슬라이시 두 개를 대신 사용합니다.

인증서를 지정하지 않고 HTTPS 서버에 클라이언트 인증서가 필요한 경우 WinHTTP는 기본 인증서 저장소에서 첫 번째 인증서를 선택합니다. 인증서가 없으면 오류가 발생합니다. 인증서가 수락되지 않으면 서버는 요청을 처리할 수 없음을 나타내는 403 상태 코드를 반환합니다. 그런 다음 SetClientCertificate 를 사용하여 더 적절한 인증서를 선택하고 요청을 다시 보냅니다.