認証サービス識別子がRPC_C_AUTHN_GSS_SCHANNELされる Secure Channel (Schannel) セキュリティ パッケージでは、SSL (Secure Sockets Layer) バージョン 2.0 および 3.0、トランスポート層セキュリティ (TLS) 1.0、およびプライベート 通信テクノロジ (PCT) 1.0 という公開キー ベースのプロトコルがサポートされています。 TLS 1.0 は、1999 年 1 月にインターネット エンジニアリング タスク フォース (IETF) によって発行された SSL 3.0 の標準化されたわずかに変更されたバージョンで、 RFC 2246 のドキュメントに記載されています。 TLS は標準化されているため、開発者は SSL ではなく TLS を使用することをお勧めします。 PCT は下位互換性のみを目的として含まれており、新しい開発には使用しないでください。 Schannel セキュリティ パッケージを使用すると、DCOM は、クライアントとサーバーの機能に応じて、最適なプロトコルを自動的にネゴシエートします。
次のトピックでは、TLS プロトコルとその DCOM の動作について簡単に説明します。
注
これらのセクションの TLS プロトコルに関するすべての情報は、SSL および PCT プロトコルにも適用されます。
TLS を使用するタイミング
TLS は、サーバーが匿名クライアントに ID を証明する必要がある場合に使用できる唯一のセキュリティ オプションです。 これは、クレジット カード番号などの機密情報の送信を保護するために、e コマースに参加する Web サイトにとって特に重要です。 TLS を使用すると、eコマースの顧客は、サーバーの ID の証明が与えられているため、ビジネスを行っているユーザーを特定できます。 また、eコマース サーバーは、各顧客の ID を認証することに気を付ける必要がない効率を実現します。
TLS では、すべてのサーバーが自分の ID をクライアントに証明する必要があります。 さらに、TLS では、クライアントが自分の ID をサーバーに証明するオプションも提供されます。 この相互認証は、大規模な企業イントラネット内の特定の Web ページのアクセスを制限する場合に役立ちます。
TLS は、最も強力な認証レベルをサポートし、暗号化の強度を時間の経過と共に増加させ、技術革新に対応できるオープン アーキテクチャを提供します。 TLS は、転送中のデータに対して最高レベルのセキュリティが必要な環境に最適な選択肢です。
TLS のしくみの概要
TLS は公開キー 基盤 (PKI) に基づいて構築されており、公開キーと秘密キーのペアを使用してデータの暗号化を有効にし、データの整合性を確立し、認証には X.509 証明書を使用します。
Kerberos v5 プロトコルなどの多くのセキュリティ プロトコルは、データの暗号化と暗号化解除の両方を行う単一のキーに依存しています。 したがって、このようなプロトコルは、暗号化キーの安全な交換に依存します。Kerberos プロトコルでは、これはキー配布センター (KDC) から取得したチケットを介して行われます。 そのためには、Kerberos プロトコルを使用するすべてのユーザーを KDC に登録する必要があります。これは、世界中から何百万人もの顧客を引き付けることを目的とした e コマース Web サーバーの実用的な制限です。 そのため、TLS は PKI に依存し、2 つのキーを使用してデータ暗号化を行います。ペアの 1 つのキーがデータを暗号化する場合、ペアのもう 1 つのキーのみが暗号化を解除できます。 この設計の原則の利点は、暗号化キーの安全な交換を必要とせずに暗号化を実行できることです。
PKI は、キーの 1 つが秘密に保持され、登録されているプリンシパルのみが使用できる手法を使用し、もう 1 つのキーは誰でもアクセスできるように公開されます。 キー ペアの所有者に秘密メッセージを送信する場合は、公開キーを使用してメッセージを暗号化し、秘密キーのみを使用してメッセージの暗号化を解除できます。
キー ペアは、送信されるデータの整合性を確認するためにも使用されます。 これを行うには、キー ペアの所有者は、データを送信する前にデジタル署名をデータにアタッチできます。 デジタル署名を作成するには、データのハッシュを計算し、秘密キーを使用してハッシュを暗号化する必要があります。 公開キーを使用してデジタル署名の暗号化を解除するユーザーは、デジタル署名が秘密キーを所有しているユーザーからのみ送信されている必要があります。 さらに、受信者は送信者と同じアルゴリズムを使用してデータのハッシュを計算できます。計算されたハッシュがデジタル署名で送信されたものと一致する場合、受信者は、デジタル署名後にデータが変更されていないことを確認できます。
PKI を使用して大量のデータ暗号化を行う場合の欠点の 1 つは、パフォーマンスが比較的遅い点です。 多くの数学が関係しているため、キー ペアに依存する非対称暗号を使用したデータの暗号化と暗号化解除は、1 つのキーのみに依存する対称暗号を使用した暗号化と暗号化解除の最大 1,000 倍遅くなる可能性があります。 そのため、TLS では、デジタル署名の生成と、クライアントとサーバーの両方で一括データの暗号化と暗号化解除に使用されるセッション固有の単一キーのネゴシエートにのみ PKI が使用されます。 TLS では、さまざまな単一キー対称暗号がサポートされており、今後追加の暗号が追加される可能性があります。
TLS ハンドシェイク プロトコルの詳細については、「 TLS ハンドシェイク プロトコル」を参照してください。
TLS プロトコルの背後にある暗号化の詳細については、「 Cryptography Essentials」を参照してください。
X.509 証明書
PKI で処理する必要がある重大な問題は、使用されている公開キーの信頼性を信頼できることです。 取引を行う会社に発行された公開キーを使用する場合は、クレジット カード番号を見つけたい盗人ではなく、キーが実際に会社に属していることを確認する必要があります。
キー ペアを保持しているプリンシパルの ID を保証するために、プリンシパルは証明機関 (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 のセキュリティ パラメーターは、 CoInitializeSecurity と CoSetProxyBlanket を呼び出すことによって設定できます。 次のセクションでは、これらの呼び出しを行う際の微妙な違いについて説明します。
サーバーがセキュリティ ブランケットを設定する方法
サーバーで TLS を使用する場合は、CoInitializeSecurity の asAuthSvc パラメーターで認証サービスとして 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 フラグを設定しないでください。 pVoid が IAccessControl オブジェクトを指している場合は、EOAC_ACCESS_CONTROL フラグを設定する必要があります。pVoid がSECURITY_DESCRIPTORを指している場合は設定しないでください。 設定できるその他のフラグについては、「 CoInitializeSecurity」を参照してください。
CoInitializeSecurity の使用の詳細については、「CoInitializeSecurity を使用したプロセス全体のセキュリティの設定」を参照してください。
クライアントがセキュリティ ブランケットを設定する方法
クライアントが TLS を使用する場合は、CoInitializeSecurity の pAuthList パラメーターで認証サービスの一覧に 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する必要があります。
- pAuthInfo は CERT_CONTEXTへのポインターであり、クライアントの X.509 証明書を表す void へのポインターとしてキャストされます。 クライアントに証明書がない場合、または証明書をサーバーに提示しない場合、 pAuthInfo は NULL である必要があり、サーバーとの匿名接続が試行されます。
- dwCapabilities は、追加のクライアント機能を示すフラグのセットです。 設定するフラグの詳細については、 CoInitializeSecurity を参照してください。
CoInitializeSecurity の使用の詳細については、「CoInitializeSecurity を使用したプロセス全体のセキュリティの設定」を参照してください。
クライアントがセキュリティ ブランケットを変更する方法
クライアントが TLS を使用するが、CoInitializeSecurity を呼び出した後でセキュリティ ブランケットを変更する場合は、CoInitializeSecurity の呼び出しで使用されるパラメーターと同様のパラメーターを使用して、CoSetProxyBlanket または IClientSecurity::SetBlanket を呼び出す必要があります。次の違いがあります。
- pServerPrincName は、msstd または fullsic 形式のサーバーのプリンシパル名を示します。 これらの形式の詳細については、「 プリンシパル名」を参照してください。 クライアントがサーバーの X.509 証明書を持っている場合は、 RpcCertGeneratePrincipalName を呼び出すことによってプリンシパル名を検索できます。
- pAuthInfo は CERT_CONTEXTへのポインターであり、クライアントの X.509 証明書を表すRPC_AUTH_IDENTITY_HANDLEへのポインターとしてキャストされます。 クライアントに証明書がない場合、または証明書をサーバーに提示しない場合、 pAuthInfo は NULL である必要があり、サーバーとの匿名接続が試行されます。
- 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();
}
関連トピック