Schannel

認証サービス識別子が RPC_C_AUTHN_GSS_SCHANNEL である Secure Channel (Schannel) セキュリティ パッケージは、SSL (Secure Sockets Layer) バージョン 2.0 および 3.0、Transport Layer Security (TLS) 1.0、Private Communication Technology (PCT) 1.0 の公開鍵ベースのプロトコルに対応しています。 TLS 1.0 は、1999 年 1 月に Internet Engineering Task Force (IETF) によって文書 RFC 2246 として発行された、SSL 3.0 をわずかに変更した標準化されたバージョンです。 TLS は標準化されているため、開発者は SSL ではなく TLS を使用することが推奨されます。 PCT は下位互換性のためにのみ含まれており、新規開発には使用しないでください。 Schannel セキュリティ パッケージを使用すると、DCOM はクライアントとサーバーの機能に応じて最適なプロトコルを自動的にネゴシエートします。

次のトピックでは、TLS プロトコルとその DCOM の動作について簡単に説明します。

Note

これらのセクションの TLS プロトコルに関するすべての情報は、SSL および PCT プロトコルにも適用されます。

 

TLS の用途

TLS は、サーバーが匿名クライアントに ID を証明する必要がある場合に使用できる唯一のセキュリティ オプションです。 これは、クレジット カード番号などの機密情報の送信を保護するのに役立つため、eコマースに参加したい Web サイトにとって特に重要です。 TLS では、eコマースの顧客にはサーバーの ID の証明が与えられるため、取引相手が誰であるかを確実に知ることができます。 また、eコマース サーバーは、各顧客の身元認証に煩わされる必要がなくなるという効率性も得られます。

TLS では、すべてのサーバーが自分の ID をクライアントに証明する必要があります。 さらに、TLS では、クライアントが自分の ID をサーバーに証明するオプションも提供されます。 この相互認証は、大規模な企業イントラネット内の特定の Web ページのアクセスを制限する場合に役立ちます。

TLS は最も強力な認証レベルをサポートし、技術革新に対応するために時間の経過とともに暗号化の強度を高めることができるオープン アーキテクチャを提供します。 TLS は、転送中のデータに対して最高レベルのセキュリティが必要な環境に最適な選択肢です。

TLS のしくみの概要

TLS は公開キー基盤 (PKI) に基づいて構築されており、公開キーと秘密キーのペアを使用してデータの暗号化を有効にし、データの整合性を確立して、認証に X.509 証明書を使用します。

Kerberos v5 プロトコルなどの多くのセキュリティ プロトコルは、データの暗号化と復号化の両方を単一のキーに依存します。 したがって、このようなプロトコルは暗号化キーの安全な交換に依存しています。Kerberos プロトコルでは、これはキー配布センター (KDC) から取得したチケットを通じて行われます。 これには、Kerberos プロトコルを使用する全員が KDC に登録する必要がありますが、これは世界中から何百万もの顧客を引き付けることを目的とした電子商取引 Web サーバーにとっては非現実的な制限となります。 したがって、TLS はデータ暗号化に 2 つのキーを使用する PKI に依存しています。ペアの一方のキーがデータを暗号化すると、ペアの他方のキーだけがそのデータを復号化できます。 この設計の主な利点は、暗号化キーの安全な交換を必要とせずに暗号化を実行できることです。

PKI は、キーの 1 つを非公開にして登録した本人だけが利用できるようにし、もう 1 つのキーは公開して誰でもアクセスできるようにする技術を使用します。 誰かがキー ペアの所有者にプライベート メッセージを送信したい場合、メッセージを公開キーで暗号化し、メッセージの復号化には秘密キーのみを使用できます。

キー ペアは、送信されるデータの整合性を検証するためにも使用されます。 これを行うには、キー ペアの所有者は、データを送信する前にデジタル署名をデータにアタッチできます。 デジタル署名を作成するには、データのハッシュを計算し、秘密キーを使用してハッシュを暗号化する必要があります。 公開キーを使用してデジタル署名を復号化する人は誰でも、そのデジタル署名は秘密キーの所有者のみからのものであることが保証されます。 さらに、受信者は送信者と同じアルゴリズムを使用してデータのハッシュを計算でき、計算されたハッシュがデジタル署名で送信されたハッシュと一致する場合、受信者はデータがデジタル署名後に変更されていないことを確信できます。

大容量データの暗号化に PKI を使用する場合の欠点の 1 つは、パフォーマンスが比較的遅いことです。 集中的な数学が必要となるため、キー ペアに依存する非対称暗号を使用したデータの暗号化と復号は、単一のキーのみに依存する対称暗号を使用した暗号化と復号に比べて最大 1,000 倍遅くなる可能性があります。 したがって、TLS は、デジタル署名の生成と、クライアントとサーバーの両方でバルク データの暗号化と復号化に使用されるセッション固有の単一キーのネゴシエーションにのみ PKI を使用します。 TLS はさまざまな単一キー対称暗号をサポートしており、将来的にはさらに暗号が追加される可能性があります。

TLS ハンドシェイク プロトコルの詳細については、「TLS ハンドシェイク プロトコル」を参照してください。

TLS プロトコルの背後にある暗号化の詳細については、「暗号化の必需品」を参照してください。

X.509 証明書

PKI で処理する必要がある重要な問題は、使用されている公開キーの信頼性を信頼できるかどうかです。 取引したい会社に発行された公開キーを使用する場合、そのキーが実際にあなたのクレジット カード番号を見つけようとする泥棒のものではなく、その会社のものであることを確認したいと考えます。

キー ペアを保持するプリンシパルの身元を保証するために、プリンシパルには証明機関 (CA) によって X.509 証明書が発行されます。 この証明書には、プリンシパルを識別する情報とプリンシパルの公開キーが含まれており、CA によってデジタル署名されています。 このデジタル署名は、証明書に含まれる公開キーが証明書によって識別されるプリンシパルに本当に属していると CA が信じていることを示します。

また、CA を信頼する方法は何ですか? CA 自体が、上位レベルの CA によって署名された X.509 証明書を保持しているためです。 この証明書署名のチェーンは、ルート CA (独自の証明書に署名する CA) に到達するまで続きます。 証明書のルート CA の整合性を信頼できる場合は、証明書自体の信頼性も信頼できるはずです。 したがって、信頼できるルート CA を選択することは、システム管理者の重要な義務です。

クライアント証明書

セキュリティ トランスポート層プロトコルが最初に登場したとき、その主な目的は、クライアントが本物のサーバーに接続していることを保証し、転送中のデータのプライバシーを保護することでした。 ただし、SSL 3.0 と TLS 1.0 には、プロトコルのハンドシェイク中にクライアントの証明書を送信するためのサポートも含まれています。 このオプション機能により、クライアントとサーバーの相互認証が可能になります。

クライアント証明書を使用するかどうかの決定は、アプリケーションのコンテキストで行う必要があります。 プライマリ要件がサーバーを認証する場合、クライアント証明書は不要です。 ただし、クライアント認証が必須の場合は、アプリケーション内のカスタム認証に依存するのではなく、クライアントの証明書を使用できます。 クライアント証明書の使用は、ユーザーにシングル サインオン シナリオを提供するため、カスタム認証よりも推奨されます。

COM での TLS の使用

TLS では、偽装 (RPC_C_IMP_LEVEL_IMPERSONATE) レベルの偽装のみがサポートされます。 COM がプロキシ上の認証サービスとして TLS をネゴシエートする場合、COM はプロセスの既定に関係なく、偽装レベルを設定して偽装します。 TLS で偽装が適切に機能するには、クライアントが X.509 証明書をサーバーに提供し、サーバーがその証明書をサーバー上の特定のユーザー アカウントにマップしている必要があります。 詳細については、「証明書をユーザー アカウントにマッピングするためのステップ バイ ステップ ガイド」を参照してください。

TLS はクローキングをサポートしていません。 CoInitializeSecurity または IClientSecurity::SetBlanket 呼び出しでクローキング フラグと TLS が指定されている場合は、E_INVALIDARG が返されます。

TLS は、認証レベルが None に設定されている場合は機能しません。 クライアントとサーバー間のハンドシェイクでは、それぞれが設定した認証レベルが検査され、接続に対してより高いセキュリティ設定が選択されます。

TLS のセキュリティ パラメーターは、CoInitializeSecurityCoSetProxyBlanket を呼び出すことによって設定できます。 次のセクションでは、これらの呼び出しを行う際の微妙な違いについて説明します。

サーバーがセキュリティ ブランケットを設定する方法

サーバーで TLS を使用する場合は、CoInitializeSecurityasAuthSvc パラメーターに認証サービスとして Schannel (RPC_C_AUTHN_GSS_SCHANNEL) を指定する必要があります。 クライアントが安全性の低い認証サービスを使用してサーバーに接続できないようにするには、CoInitializeSecurity を呼び出すときに Schannel のみを認証サービスとして指定する必要があります。 CoInitializeSecurity を呼び出した後、サーバーはセキュリティ ブランケットを変更できません。

TLS を使用するには、サーバーが CoInitializeSecurity を呼び出すときに、次のパラメーターを指定する必要があります。

  • pVoid は、IAccessControl オブジェクトへのポインターか、SECURITY_DESCRIPTOR へのポインターである必要があります。 NULL または AppID へのポインターにすることはできません。
  • cAuthSvc を 0 または -1 にすることはできません。 cAuthSvc が -1 の場合、COM サーバーは Schannel を選択しません。
  • asAuthSvc は、可能な認証サービスとして Schannel を指定する必要があります。 これを行うには、SOLE_AUTHENTICATION_LIST の Schannel メンバーに次の SOLE_AUTHENTICATION_SERVICE パラメーターを設定します。
    • dwAuthnSvc は RPC_C_AUTHN_GSS_SCHANNEL する必要があります。
    • dwAuthzSvc は RPC_C_AUTHZ_NONE である必要があります。 現在、それは無視されます。
    • pPrincipalName は、サーバーの X.509 証明書を表す OLECHAR へのポインターとしてキャストされる、CERT_CONTEXT へのポインターである必要があります。
  • dwAuthnLevel は、接続が成功するためにクライアントから受け入れられる最小認証レベルを示します。 RPC_C_AUTHN_LEVEL_NONE にすることはできません。
  • dwCapabilities には、EOAC_APPID フラグを設定しないでください。 pVoidIAccessControl オブジェクトを指している場合は、EOAC_ACCESS_CONTROL フラグを設定する必要があります。pVoid が SECURITY_DESCRIPTOR を指している場合は、設定しないでください。 設定できるその他のフラグについては、「CoInitializeSecurity」を参照してください。

CoInitializeSecurity の使用の詳細については、「CoInitializeSecurity を使用したプロセス全体のセキュリティの設定」を参照してください。

クライアントがセキュリティ ブランケットを設定する方法

クライアントが TLS を使用する場合は、CoInitializeSecuritypAuthList パラメーターで認証サービスの一覧に Schannel (RPC_C_AUTHN_GSS_SCHANNEL) を指定する必要があります。 CoInitializeSecurity が呼び出されたときに Schannel が可能な認証サービスとして指定されていない場合、認証サービスとして Schannel を指定しようとすると、後で CoSetProxyBlanket (または IClientSecurity::SetBlanket) を呼び出すと失敗します。

クライアントが CoInitializeSecurity を呼び出すときは、次のパラメーターを指定する必要があります。

  • dwAuthnLevel は、クライアントが使用する既定の認証レベルを指定します。 RPC_C_AUTHN_LEVEL_NONE にすることはできません。
  • dwImpLevel は RPC_C_IMP_LEVEL_IMPERSONATE である必要があります。
  • pAuthList には、リストのメンバーとして次の SOLE_AUTHENTICATION_INFO パラメーターが必要です。
    • dwAuthnSvc は RPC_C_AUTHN_GSS_SCHANNEL する必要があります。
    • dwAuthzSvc は RPC_C_AUTHZ_NONE である必要があります。
    • pAuthInfoCERT_CONTEXT へのポインターであり、クライアントの X.509 証明書を表す void へのポインターとしてキャストされます。 クライアントに証明書がない場合、または証明書をサーバーに提示しない場合、pAuthInfoNULL である必要があり、サーバーとの匿名接続が試行されます。
  • dwCapabilities は、追加のクライアント機能を示すフラグのセットです。 設定するフラグの詳細については、CoInitializeSecurity を参照してください。

CoInitializeSecurity の使用の詳細については、「CoInitializeSecurity を使用したプロセス全体のセキュリティの設定」を参照してください。

クライアントがセキュリティ ブランケットを変更する方法

クライアントが TLS を使用するが、CoInitializeSecurity を呼び出した後でセキュリティ ブランケットを変更する場合は、CoInitializeSecurity の呼び出しで使用されるパラメーターと同様のパラメーターを使用して、CoSetProxyBlanket または IClientSecurity::SetBlanket を呼び出す必要があります。次の違いがあります。

  • pServerPrincName は、msstd 形式または fullsic 形式のサーバーのプリンシパル名を示します。 これらの形式の詳細については、「プリンシパル名」を参照してください。 クライアントがサーバーの X.509 証明書を持っている場合は、RpcCertGeneratePrincipalName を呼び出すことによってプリンシパル名を検索できます。
  • pAuthInfoCERT_CONTEXT へのポインターであり、クライアントの X.509 証明書を表す RPC_AUTH_IDENTITY_HANDLE へのポインターとしてキャストされます。 クライアントに証明書がない場合、または証明書をサーバーに提示しない場合、pAuthInfoNULL である必要があり、サーバーとの匿名接続が試行されます。
  • dwCapabilities は、追加のクライアント機能を示すフラグで構成されます。 セキュリティ の一括設定を変更するには、EOAC_DEFAULT、EOAC_MUTUAL_AUTH、EOAC_ANY_AUTHORITY (このフラグは非推奨)、および EOAC_MAKE_FULLSIC の 4 つのフラグのみを使用できます。 詳細については、「CoSetProxyBlanket」を参照してください。

CoSetProxyBlanket の使用の詳細については、「インターフェイス プロキシ レベルでのセキュリティの設定」を参照してください。

例: クライアントがセキュリティ ブランケットを変更する

次の例は、クライアントが X.509 証明書を提供するというサーバーからの要求に対応するために、クライアントがセキュリティ ブランケットを変更する方法を示しています。 簡潔にするため、コード処理は省略しています。

void ClientChangesSecurity ()
{
  HCRYPTPROV                   provider           = 0;
  HCERTSTORE                   cert_store         = NULL;
  PCCERT_CONTEXT               client_cert        = NULL;
  PCCERT_CONTEXT               server_cert        = NULL;
  WCHAR                        *server_princ_name = NULL;
  ISecret                      *pSecret           = NULL;
  MULTI_QI                     server_instance;
  COSERVERINFO                 server_machine;
  SOLE_AUTHENTICATION_LIST     auth_list;
  SOLE_AUTHENTICATION_INFO     auth_info[1];



  // Specify all the authentication info. 
  // The client is willing to connect using SChannel,
  //   with no client certificate.
  auth_list.cAuthInfo     = 1;
  auth_list.aAuthInfo     = auth_info;
  auth_info[0].dwAuthnSvc = RPC_C_AUTHN_GSS_SCHANNEL;
  auth_info[0].dwAuthzSvc = RPC_C_AUTHZ_NONE;
  auth_info[0].pAuthInfo  = NULL;  // No certificate

  // Initialize client security with no client certificate.
  CoInitializeSecurity( NULL, -1, NULL, NULL,
                        RPC_C_AUTHN_LEVEL_PKT,
                        RPC_C_IMP_LEVEL_IMPERSONATE, &auth_list,
                        EOAC_NONE, NULL );
  
  // Specify info for the proxy.
  server_instance = {&IID_ISecret, NULL, S_OK};
  server_machine  = {0, L"ServerMachineName", NULL, 0};
  
  // Create a proxy.
  CoCreateInstanceEx( CLSID_Secret, NULL, CLSCTX_REMOTE_SERVER, 
                      &server_machine, 1, &server_instance);
  pSecret = (ISecret *) server_instance.pItf;

  //** The client obtained the server's certificate during the handshake.
  //** The server requests a certificate from the client.

  // Get the default certificate provider.
  CryptAcquireContext( &provider, NULL, NULL, PROV_RSA_SCHANNEL, 0 );

  // Open the certificate store.
  cert_store = CertOpenSystemStore( provider, L"my" );

  // Find the client's certificate.
  client_cert = 
    CertFindCertificateInStore( cert_store,
                                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                                0,
                                CERT_FIND_SUBJECT_STR,
                                L"ClientName",  // Use the principal name
                                NULL );

  // Find the fullsic principal name of the server.
  RpcCertGeneratePrincipalName( server_cert, RPC_C_FULL_CERT_CHAIN, 
                                &server_princ_name );

  // Change the client's security: 
  // Increase the authentication level and attach a certificate.
  CoSetProxyBlanket( pSecret, RPC_C_AUTHN_GSS_SCHANNEL,
                     RPC_C_AUTHZ_NONE,
                     server_princ_name, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
                     RPC_C_IMP_LEVEL_IMPERSONATE, 
                     (RPC_AUTH_IDENTITY_HANDLE *) client_cert,
                     EOAC_NONE );

  cleanup:
  if (server_princ_name != NULL)
    RpcStringFree( &server_princ_name );
  if (client_cert != NULL)
    CertFreeCertificateContext(client_cert);
  if (server_cert != NULL)
    CertFreeCertificateContext(server_cert);
  if (cert_store != NULL)
    CertCloseStore( cert_store, CERT_CLOSE_STORE_CHECK_FLAG );
  if (provider != 0 )
    CryptReleaseContext( provider, 0 );
  if (pSecret != NULL)
    pSecret->Release();
  CoUninitialize();
}

COM およびセキュリティ パッケージ