次の方法で共有


SAML トークン プロバイダー

このサンプルでは、カスタム クライアント SAML トークン プロバイダーを実装する方法を示します。 Windows Communication Foundation (WCF) のトークン プロバイダーは、セキュリティ インフラストラクチャに資格情報を提供するために使用されます。 一般に、トークン プロバイダーはターゲットを調べ、適切な資格情報を発行して、セキュリティ インフラストラクチャがメッセージをセキュリティで保護できるようにします。 WCF には、既定の資格情報マネージャー トークン プロバイダーが付属しています。 WCF には、CardSpace トークン プロバイダーも付属しています。 カスタム トークン プロバイダーは、次の場合に役立ちます。

  • これらのトークンプロバイダーが対応できないクレデンシャルストアがある場合。

  • ユーザーが詳細を提供した時点から WCF クライアント フレームワークが資格情報を使用するタイミングまで、資格情報を変換するための独自のカスタム メカニズムを提供する場合。

  • カスタム トークンを構築する場合。

このサンプルでは、WCF クライアント フレームワークの外部から取得した SAML トークンを使用できるようにするカスタム トークン プロバイダーを構築する方法を示します。

要約すると、このサンプルでは次の例を示します。

  • カスタム トークン プロバイダーを使用してクライアントを構成する方法。

  • カスタム クライアント資格情報に SAML トークンを渡す方法。

  • WCF クライアント フレームワークに SAML トークンを提供する方法。

  • サーバーの X.509 証明書を使用してクライアントによってサーバーが認証される方法。

サービスは、構成ファイル App.configを使用して定義された、サービスと通信するための 2 つのエンドポイントを公開します。各エンドポイントは、アドレス、バインディング、およびコントラクトで構成されます。 バインディングは、メッセージ セキュリティを使用する標準の wsFederationHttpBindingで構成されます。 一方のエンドポイントでは、対称証明キーを使用する SAML トークンを使用してクライアントが認証されることを想定し、もう 1 つのエンドポイントでは、非対称証明キーを使用する SAML トークンでクライアントが認証されることを想定しています。 サービスは、 serviceCredentials 動作を使用してサービス証明書も構成します。 serviceCredentials動作では、サービス証明書を構成できます。 サービス証明書は、サービスを認証し、メッセージ保護を提供するためにクライアントによって使用されます。 次の構成では、このトピックの最後にあるセットアップ手順の説明に従って、サンプルのセットアップ中にインストールされた "localhost" 証明書を参照します。 serviceCredentials動作では、SAML トークンに署名するために信頼されている証明書を構成することもできます。 次の構成では、サンプル中にインストールされた 'Alice' 証明書を参照します。

<system.serviceModel>
 <services>
  <service
          name="Microsoft.ServiceModel.Samples.CalculatorService"
          behaviorConfiguration="CalculatorServiceBehavior">
   <host>
    <baseAddresses>
     <!-- configure base address provided by host -->
     <add
  baseAddress="http://localhost:8000/servicemodelsamples/service/" />
    </baseAddresses>
   </host>
   <!-- use base address provided by host -->
   <!-- Endpoint that expect SAML tokens with Symmetric proof keys -->
   <endpoint address="calc/symm"
             binding="wsFederationHttpBinding"
             bindingConfiguration="Binding1"
             contract="Microsoft.ServiceModel.Samples.ICalculator" />
   <!-- Endpoint that expect SAML tokens with Asymmetric proof keys -->
   <endpoint address="calc/asymm"
             binding="wsFederationHttpBinding"
             bindingConfiguration="Binding2"
             contract="Microsoft.ServiceModel.Samples.ICalculator" />
  </service>
 </services>

 <bindings>
  <wsFederationHttpBinding>
   <!-- Binding that expect SAML tokens with Symmetric proof keys -->
   <binding name="Binding1">
    <security mode="Message">
     <message negotiateServiceCredential ="false"
              issuedKeyType="SymmetricKey"
              issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"  />
    </security>
   </binding>
   <!-- Binding that expect SAML tokens with Asymmetric proof keys -->
   <binding name="Binding2">
    <security mode="Message">
     <message negotiateServiceCredential ="false"
              issuedKeyType="AsymmetricKey"
              issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"  />
    </security>
   </binding>
  </wsFederationHttpBinding>
 </bindings>

 <behaviors>
  <serviceBehaviors>
   <behavior name="CalculatorServiceBehavior">
    <!--
    The serviceCredentials behavior allows one to define a service certificate.
    A service certificate is used by a client to authenticate the service and provide message protection.
    This configuration references the "localhost" certificate installed during the setup instructions.
    -->
    <serviceCredentials>
     <!-- Set allowUntrustedRsaIssuers to true to allow self-signed, asymmetric key based SAML tokens -->
     <issuedTokenAuthentication allowUntrustedRsaIssuers ="true" >
      <!-- Add Alice to the list of certs trusted to issue SAML tokens -->
      <knownCertificates>
       <add storeLocation="LocalMachine"
            storeName="TrustedPeople"
            x509FindType="FindBySubjectName"
            findValue="Alice"/>
      </knownCertificates>
     </issuedTokenAuthentication>
     <serviceCertificate storeLocation="LocalMachine"
                         storeName="My"
                         x509FindType="FindBySubjectName"
                         findValue="localhost"  />
    </serviceCredentials>
   </behavior>
  </serviceBehaviors>
 </behaviors>

</system.serviceModel>

次の手順では、カスタム SAML トークン プロバイダーを開発し、WCF と統合する方法を示します。

  1. カスタム SAML トークン プロバイダーを作成します。

    このサンプルでは、構築時に提供される SAML アサーションに基づいてセキュリティ トークンを返すカスタム SAML トークン プロバイダーを実装しています。

    このタスクを実行するために、カスタム トークン プロバイダーは SecurityTokenProvider クラスから派生し、 GetTokenCore メソッドをオーバーライドします。 このメソッドは、新しい SecurityTokenを作成して返します。

    protected override SecurityToken GetTokenCore(TimeSpan timeout)
    {
     // Create a SamlSecurityToken from the provided assertion
     SamlSecurityToken samlToken = new SamlSecurityToken(assertion);
    
     // Create a SecurityTokenSerializer that will be used to
     // serialize the SamlSecurityToken
     WSSecurityTokenSerializer ser = new WSSecurityTokenSerializer();
     // Create a memory stream to write the serialized token into
     // Use an initial size of 64Kb
     MemoryStream s = new MemoryStream(UInt16.MaxValue);
    
     // Create an XmlWriter over the stream
     XmlWriter xw = XmlWriter.Create(s);
    
     // Write the SamlSecurityToken into the stream
     ser.WriteToken(xw, samlToken);
    
     // Seek back to the beginning of the stream
     s.Seek(0, SeekOrigin.Begin);
    
     // Load the serialized token into a DOM
     XmlDocument dom = new XmlDocument();
     dom.Load(s);
    
     // Create a KeyIdentifierClause for the SamlSecurityToken
     SamlAssertionKeyIdentifierClause samlKeyIdentifierClause = samlToken.CreateKeyIdentifierClause<SamlAssertionKeyIdentifierClause>();
    
    // Return a GenericXmlToken from the XML for the
    // SamlSecurityToken, the proof token, the valid from and valid
    // until times from the assertion and the key identifier clause
    // created above
    return new GenericXmlSecurityToken(dom.DocumentElement, proofToken, assertion.Conditions.NotBefore, assertion.Conditions.NotOnOrAfter, samlKeyIdentifierClause, samlKeyIdentifierClause, null);
    }
    
  2. カスタム セキュリティ トークン マネージャーを記述します。

    SecurityTokenManager クラスは、SecurityTokenProvider メソッドで渡される特定の SecurityTokenRequirementCreateSecurityTokenProvider を作成するために使用されます。 セキュリティ トークン マネージャーは、トークン認証子とトークン シリアライザーの作成にも使用されますが、このサンプルでは扱いません。 このサンプルでは、カスタム セキュリティ トークン マネージャーは、 ClientCredentialsSecurityTokenManager クラスから継承し、渡されたトークン要件が SAML トークンが要求されたことを示す場合にカスタム SAML トークン プロバイダーを返す CreateSecurityTokenProvider メソッドをオーバーライドします。 クライアント資格情報クラス (手順 3 を参照) でアサーションが指定されていない場合、セキュリティ トークン マネージャーによって適切なインスタンスが作成されます。

    public class SamlSecurityTokenManager : ClientCredentialsSecurityTokenManager
    {
     SamlClientCredentials samlClientCredentials;
    
     public SamlSecurityTokenManager ( SamlClientCredentials samlClientCredentials)
      : base(samlClientCredentials)
     {
      // Store the creating client credentials
      this.samlClientCredentials = samlClientCredentials;
     }
    
     public override SecurityTokenProvider CreateSecurityTokenProvider ( SecurityTokenRequirement tokenRequirement )
     {
      // If token requirement matches SAML token return the
      // custom SAML token provider
      if (tokenRequirement.TokenType == SecurityTokenTypes.Saml ||
          tokenRequirement.TokenType == "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1")
      {
       // Retrieve the SAML assertion and proof token from the
       // client credentials
       SamlAssertion assertion = this.samlClientCredentials.Assertion;
       SecurityToken prooftoken = this.samlClientCredentials.ProofToken;
    
       // If either the assertion of proof token is null...
       if (assertion == null || prooftoken == null)
       {
        // ...get the SecurityBindingElement and then the
        // specified algorithm suite
        SecurityBindingElement sbe = null;
        SecurityAlgorithmSuite sas = null;
    
        if ( tokenRequirement.TryGetProperty<SecurityBindingElement> ( "http://schemas.microsoft.com/ws/2006/05/servicemodel/securitytokenrequirement/SecurityBindingElement", out sbe))
        {
         sas = sbe.DefaultAlgorithmSuite;
        }
    
        // If the token requirement is for a SymmetricKey based token..
        if (tokenRequirement.KeyType == SecurityKeyType.SymmetricKey)
        {
         // Create a symmetric proof token
         prooftoken = SamlUtilities.CreateSymmetricProofToken ( tokenRequirement.KeySize );
         // and a corresponding assertion based on the claims specified in the client credentials
         assertion = SamlUtilities.CreateSymmetricKeyBasedAssertion ( this.samlClientCredentials.Claims, new X509SecurityToken ( samlClientCredentials.ClientCertificate.Certificate ), new X509SecurityToken ( samlClientCredentials.ServiceCertificate.DefaultCertificate ), (BinarySecretSecurityToken)prooftoken, sas);
        }
        // otherwise...
        else
        {
         // Create an asymmetric proof token
         prooftoken = SamlUtilities.CreateAsymmetricProofToken();
         // and a corresponding assertion based on the claims
         // specified in the client credentials
         assertion = SamlUtilities.CreateAsymmetricKeyBasedAssertion ( this.samlClientCredentials.Claims, prooftoken, sas );
        }
       }
    
       // Create a SamlSecurityTokenProvider based on the assertion and proof token
       return new SamlSecurityTokenProvider(assertion, prooftoken);
      }
      // otherwise use base implementation
      else
      {
       return base.CreateSecurityTokenProvider(tokenRequirement);
      }
    }
    
  3. カスタム クライアント資格情報を書き込みます。

    クライアント資格情報クラスは、クライアント プロキシ用に構成された資格情報を表すために使用され、トークン認証子、トークン プロバイダー、トークン シリアライザーを取得するために使用されるセキュリティ トークン マネージャーを作成します。

    public class SamlClientCredentials : ClientCredentials
    {
     ClaimSet claims;
     SamlAssertion assertion;
     SecurityToken proofToken;
    
     public SamlClientCredentials() : base()
     {
      // Set SupportInteractive to false to suppress Cardspace UI
      base.SupportInteractive = false;
     }
    
     protected SamlClientCredentials(SamlClientCredentials other) : base ( other )
     {
      // Just do reference copy given sample nature
      this.assertion = other.assertion;
      this.claims = other.claims;
      this.proofToken = other.proofToken;
     }
    
     public SamlAssertion Assertion { get { return assertion; } set { assertion = value; } }
    
     public SecurityToken ProofToken { get { return proofToken; } set { proofToken = value; } }
     public ClaimSet Claims { get { return claims; } set { claims = value; } }
    
     protected override ClientCredentials CloneCore()
     {
      return new SamlClientCredentials(this);
     }
    
     public override SecurityTokenManager CreateSecurityTokenManager()
     {
      // return custom security token manager
      return new SamlSecurityTokenManager(this);
     }
    }
    
  4. カスタム クライアント資格情報を使用するようにクライアントを構成します。

    このサンプルでは、クライアントがカスタム クライアント資格情報を使用できるように、既定のクライアント資格情報クラスを削除し、新しいクライアント資格情報クラスを提供します。

    // Create new credentials class
    SamlClientCredentials samlCC = new SamlClientCredentials();
    
    // Set the client certificate. This is the cert that will be used to sign the SAML token in the symmetric proof key case
    samlCC.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "Alice");
    
    // Set the service certificate. This is the cert that will be used to encrypt the proof key in the symmetric proof key case
    samlCC.ServiceCertificate.SetDefaultCertificate(StoreLocation.CurrentUser, StoreName.TrustedPeople, X509FindType.FindBySubjectName, "localhost");
    
    // Create some claims to put in the SAML assertion
    IList<Claim> claims = new List<Claim>();
    claims.Add(Claim.CreateNameClaim(samlCC.ClientCertificate.Certificate.Subject));
    ClaimSet claimset = new DefaultClaimSet(claims);
    samlCC.Claims = claimset;
    
    // set new credentials
    client.ChannelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
    client.ChannelFactory.Endpoint.Behaviors.Add(samlCC);
    

サービスでは、呼び出し元に関連付けられている要求が表示されます。 サンプルを実行すると、操作要求と応答がクライアント コンソール ウィンドウに表示されます。 クライアント ウィンドウで Enter キーを押して、クライアントをシャットダウンします。

Batch ファイルのセットアップ

このサンプルに含まれる Setup.bat バッチ ファイルを使用すると、サーバー証明書ベースのセキュリティを必要とするセルフホステッド アプリケーションを実行するように、関連する証明書を使用してサーバーを構成できます。 このバッチ ファイルは、コンピューター間で動作するように、またはホストされていないケースで動作するように変更する必要があります。

次に、適切な構成で実行するように変更できるように、バッチ ファイルのさまざまなセクションの概要を示します。

  • サーバー証明書の作成:

    Setup.bat バッチ ファイルの次の行は、使用するサーバー証明書を作成します。 %SERVER_NAME%変数は、サーバー名を指定します。 この変数を変更して、独自のサーバー名を指定します。 このバッチ ファイルの既定値は localhost です。

    証明書は、ローカルマシン ストアの場所にあるマイ (個人用) ストアに格納されます。

    echo ************
    echo Server cert setup starting
    echo %SERVER_NAME%
    echo ************
    echo making server cert
    echo ************
    makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
    
  • クライアントの信頼された証明書ストアへのサーバー証明書のインストール:

    Setup.bat バッチ ファイル内の次の行は、クライアントの信頼できるユーザー ストアにサーバー証明書をコピーします。 Makecert.exe によって生成された証明書はクライアント システムによって暗黙的に信頼されないため、この手順が必要です。 クライアントの信頼されたルート証明書 (Microsoft が発行した証明書など) にルート化された証明書が既にある場合、クライアント証明書ストアにサーバー証明書を設定するこの手順は必要ありません。

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r LocalMachine -s TrustedPeople
    
  • 発行者証明書の作成。

    Setup.bat バッチ ファイルの次の行は、使用する発行者証明書を作成します。 %USER_NAME%変数は発行者名を指定します。 この変数を変更して、独自の発行者名を指定します。 このバッチ ファイルの既定値は Alice です。

    証明書は、CurrentUser ストアの場所の下のマイ ストアに格納されます。

    echo ************
    echo Server cert setup starting
    echo %SERVER_NAME%
    echo ************
    echo making server cert
    echo ************
    makecert.exe -sr CurrentUser -ss My -a sha1 -n CN=%USER_NAME% -sky exchange -pe
    
  • 発行者証明書をサーバーの信頼された証明書ストアにインストールする。

    Setup.bat バッチ ファイル内の次の行は、クライアントの信頼できるユーザー ストアにサーバー証明書をコピーします。 Makecert.exe によって生成された証明書はクライアント システムによって暗黙的に信頼されないため、この手順が必要です。 クライアントの信頼されたルート証明書 (Microsoft が発行した証明書など) にルート化された証明書が既にある場合、この手順では、サーバー証明書ストアに発行者証明書を設定する必要はありません。

    certmgr.exe -add -r CurrentUser -s My -c -n %USER_NAME% -r LocalMachine -s TrustedPeople
    

サンプルを設定してビルドするには

  1. Windows Communication Foundation サンプル One-Time セットアップ手順を実行していることを確認します。

  2. ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。

Svcutil.exe を使用してこのサンプルの構成を再生成する場合は、クライアント構成のエンドポイント名をクライアント コードと一致するように変更してください。

同じコンピューターでサンプルを実行するには

  1. Visual Studio コマンド プロンプト内のサンプル インストール フォルダーから Setup.bat を実行し、管理者特権で実行します。 これにより、サンプルの実行に必要なすべての証明書がインストールされます。

    Setup.bat バッチ ファイルは、Visual Studio コマンド プロンプトから実行するように設計されています。 Visual Studio コマンド プロンプト内で設定された PATH 環境変数は、Setup.bat スクリプトに必要な実行可能ファイルを含むディレクトリを指します。

  2. service\bin から Service.exe を起動します。

  3. \client\bin から Client.exe を起動します。 クライアント アクティビティがクライアント コンソール アプリケーションに表示されます。

  4. クライアントとサービスが通信できない場合は、「WCF サンプルのトラブルシューティングのヒント」を参照してください。

複数のコンピューターでサンプルを実行するには

  1. サービス バイナリのサービス コンピューター上にディレクトリを作成します。

  2. サービス プログラム ファイルをサービス コンピューター上のサービス ディレクトリにコピーします。 また、Setup.bat ファイルと Cleanup.bat ファイルをサービス コンピューターにコピーします。

  3. コンピューターの完全修飾ドメイン名を含むサブジェクト名を持つサーバー証明書が必要です。 Service.exe.config ファイルは、この新しい証明書名を反映するように更新する必要があります。 Setup.bat バッチ ファイルを変更することで、サーバー証明書を作成できます。 setup.bat ファイルは、管理者特権で開かれた Visual Studio の開発者コマンド プロンプト ウィンドウで実行する必要があることに注意してください。 %SERVER_NAME%変数は、サービスのホストに使用されるコンピューターの完全修飾ホスト名に設定する必要があります。

  4. サーバー証明書をクライアントの CurrentUser-TrustedPeople ストアにコピーします。 この手順は、サーバー証明書がクライアントの信頼された発行者によって発行される場合は必要ありません。

  5. サービス コンピューター上の Service.exe.config ファイルで、localhost ではなく完全修飾コンピューター名を指定するようにベース アドレスの値を変更します。

  6. サービス コンピューターで、コマンド プロンプトから Service.exe を実行します。

  7. クライアント プログラム ファイルを、言語固有のフォルダーの下にある \client\bin\ フォルダーからクライアント コンピューターにコピーします。

  8. クライアント コンピューター上の Client.exe.config ファイルで、サービスの新しいアドレスと一致するようにエンドポイントのアドレス値を変更します。

  9. クライアント コンピューターで、コマンド プロンプト ウィンドウから Client.exe を起動します。

  10. クライアントとサービスが通信できない場合は、「WCF サンプルのトラブルシューティングのヒント」を参照してください。

サンプルの実行後にクリーンアップするには

  1. サンプルの実行が完了したら、samples フォルダーで Cleanup.bat を実行します。