次の方法で共有


WinHTTP での SSL

Microsoft Windows HTTP Services (WinHTTP) は、クライアント証明書を含む Secure Sockets Layer (SSL) トランザクションをサポートしています。 このトピックでは、SSL トランザクションに関連する概念と、それらが WinHTTP を使用して処理される方法について説明します。

Secure Sockets Layer

SSL は、セキュリティで保護された HTTP トランザクションを確保するための確立された標準です。 SSL は、クライアントとサーバー間のすべてのトランザクションで最大 128 ビット暗号化を実行するメカニズムを提供します。 これにより、クライアントは、サーバー証明書を使用して、サーバーが信頼されたエンティティに属していることを確認できます。 また、サーバーはクライアント証明書を使用してクライアントの ID を確認することもできます。

これらの各問題の暗号化、サーバー ID、およびクライアント ID は、クライアントが最初に Secure Hypertext Transfer Protocol (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 パラメーターには、1 つ以上の詳細なエラー フラグが含まれています。 詳細については、WINHTTP_STATUS_CALLBACK を参照してください。

クライアント証明書

SSL ハンドシェイク中に、サーバーで認証が必要になる場合があります。 クライアントは、サーバーに有効なクライアント証明書を指定することによって認証されます。 WinHTTP を使用すると、ローカル 証明書ストアから証明書を選択して送信できます。 次のセクションでは、WinHTTP API または WinHttpRequestオブジェクトを使用するときにクライアント証明書を提供するプロセスについて説明します。

WinHTTP API

WinHttpSendRequestと WinHttpReceiveResponseどちらも、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) の一覧を指定します。 アプリケーションは発行者リストをフィルター処理して、必要な証明書を取得します。

WinHttp クライアント アプリケーションは、WinHttpSendRequest、または WinHttpReceiveResponseERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDEDを返場合に発行者リストを取得します。 このエラーが返されると、アプリケーションは 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するか、WinHttpRecieveResponseERROR_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 メソッドを使用して証明書の選択文字列を指定して、証明書を選択します。 証明書の選択文字列は、証明書の場所、証明書ストア、およびバックスラッシュで区切られたサブジェクト名で構成されます。 次の表に、この選択文字列のコンポーネントを示します。

コンポーネント 形容 使用可能な値
場所 証明書を格納するレジストリ キーを決定します。 指定できる値は、証明書ストアのHKEY_LOCAL_MACHINE 下にあることを示す "LOCAL_MACHINE" です。
証明書ストア が偽装されていないHKEY_CURRENT_USER の下にあることを示す "CURRENT_USER" を指定します。
このコンポーネントでは、大文字と小文字が区別されます。
証明書ストア 関連する証明書を含む 証明書ストア の名前を示します。 証明書ストア一般的なは、"MY"、"Root"、および "TrustedPeople" です。 このコンポーネントでは、大文字と小文字が区別されます。
サブジェクト名 指定した 証明書ストア内の証明書を識別します。 このコンポーネントに指定された文字列を含む最初の証明書が選択されます。 サブジェクト名には任意の文字列を指定できます。 空白の文字列は、証明書ストア の最初の証明書を使用する必要があることを示します。 このコンポーネントでは、大文字と小文字が区別されません。

証明書ストア 名前と場所は省略可能なコンポーネントです。 ただし、証明書ストアを指定する場合は、その 証明書ストアの場所も指定する必要があります。 既定の場所はCURRENT_USER、既定の 証明書ストア は "MY" です。

次のコード例では、"My Middle-Tier Certificate" というサブジェクトを持つ証明書を、レジストリの "個人用" 証明書ストアから選択HKEY_LOCAL_MACHINEの下に指定する方法を示します。

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

手記

一部の言語では、円記号はエスケープ文字です。 これを考慮して証明書の選択文字列を変更することを忘れないでください。 たとえば、Microsoft JScript では、1 つではなく 2 つの隣接する円記号を使用します。

証明書を指定せず、HTTPS サーバーにクライアント証明書が必要な場合、WinHTTP は既定の 証明書ストアで最初の証明書を選択します。 証明書が存在しない場合は、エラーが発生します。 証明書が受け入れられない場合、サーバーは要求を満たすことができないことを示す 403 状態コードを返します。 その後、SetClientCertificateを使用して、より適切な証明書選択し、要求を再送信できます。