メッセージの保護を証明書を使って行う、構成を実現します。証明書を使用することで、サービスの認証とメッセージの保護が実現できます。今回はIIS上でホストして見ます。今回は自己ホストではなく、WCF Service Applicationというプロジェクトテンプレートで作成したプロジェクトをIISでホストします。

動作確認環境

  • 同一OS(クライアント:コンソール,ホスト:IIS7.0)
  • 開発環境:Visual Studio 2008 Professional (英語版)
  • .NET 3.5

1 サービスの証明書の作成

1.1 証明書の作成

Administrator 権限でVisual Studio2008 Command Promptを表示します(ショートカットを右クリック→Run as Administrator選択)。以下のコマンドを入力し、LocalMachineのPersonalストアに証明書を作成しインポートします。証明書のCN(Common Name)はTestWCFServiceCertとしました。本来ならdns名に一致したほうがよいと思いますがテストなのでこのようにしています。

>makecert -sr LocalMachine -ss My -n CN=TestWCFServiceCert -sky exchange

IISでホストする場合はASP.NETを実行するサービス(IIS6の場合NetworkService,IIS6の場合ASPNET)に証明書へのアクセス権を付与する必要があります。
FindPrivateKeyツールで証明書ファイルの場所を調べ、caclsコマンドで読み取りのアクセス権を付与します。

>FindPrivateKey My LocalMachine -n CN=TestWCFServiceCert -a

>cacls "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\7b90a71bfc56f2582e916a51aed6df9a_50e910eb-f32d-4b23-8322-efd58c8de43e" /E /G NETWORKSERVICE:R

FindPrivateKeyがない場合
FindPrivateKeyがない場合(他の環境がわからないのでよくわかっていませんが私がそうでした)があります。そのときは、コンパイルしてexeを作成できます。Microsoft Document Explorerを表示→[.NET Development]→[.NET Framework]→[Samples]→[Windows Communication Foundation Samples]→[Technorogy Samples]→[Sample Tools]→[FindPrivateKey]とツリーを展開してページを表示します。ページ内のDownload Sampleからソリューションをダウンロードしてソリューションをコンパイルすれば作成できます。

MSDNをインストールしていない場合(いないと思いますが)は以下のページからダウロードできます。
Windows SDK .NET Framework 3.0 Samples
http://www.microsoft.com/downloads/details.aspx?FamilyID=22b58b6c-8f98-40d0-880d-c3339c5da01e&DisplayLang=en

IISでホストする場合はiisresetで再起動しておきます。

>iisreset

1.2 証明書のクライアントへのインポート

サービスの証明書をクライアントの証明書ストアへのインポートするために、公開鍵を格納したファイルをエクスポートします

>certmgr -put -c -n TestWCFServiceCert -r LocalMachine -s My WCFServiceCert.cer

証明書をCurrentUserのTrustedPeopleストアへインポートします。

2 ソリューションの作成

Visual Studioを起動し、空のソリューションを作成します。ソリューション名はWCFSample023とします。

3 WCFサービスの作成

ソリューションエクスプローラからソリューションを右クリック→[Add]→[New Project]をクリック。表示されるAdd New ProjectダイアログからProject typesにWeb,TemplateにWCF Service Applicationを選択しHelloServiceという名前でWCF Service Applicationを作成します。既定で作成されるIService1.cs,IService1.svc,Service1.svc.csを削除します。

3.1 プログラムの編集

プロジェクトを右クリック→[Add]→[New Item]でTemplateにWCF Serviceを選択、ファイル名をHelloService.svcとしてファイルを作成します。
ファイルの編集します。

IHelloService.csを以下のように編集。(usingの部分は省略)

namespace HelloService
{
    [ServiceContract]
    public interface IHelloService
    {
        [OperationContract]
        string HelloWorld(string name);
    }
}

HelloService.svc.csを以下のように編集。(usingの部分は省略)

namespace HelloService
{
    public class HelloService : IHelloService
    {
        public string HelloWorld(string name)
        {
            return string.Format("Hello {0}", name);
        }
    }
}

HelloService.svcを確認し、次のようになっていることを確認。

<%@ ServiceHost Language="C#" Debug="true" Service="HelloService.HelloService" CodeBehind="HelloService.svc.cs" %>

3.2 Web.configの編集

Web.config内のsystem.serviceModelセクションを一度削除します。Service Configuration Editorで編集します。Web.configを右クリック→[Edit WCF Configuration]をクリックします。

左ペインのConfigurationのBindingsを選択し、右ペインのNew Binding Confugrationをクリックして、バインディングの構成を作成します。Create a New Bindingダイアログが表示されるのでwsHttpBindingを選択してOKをクリックします。

バインディングの構成の編集で以下のように編集します。
Bindingタブを選択し、NameをHelloServiceBindingに設定します。
Securityタブを選択し、ModeがMessageであることを確認し、MessageClientCredentialTypeをNoneにセットします。

 

次に左ペインのConfigurationからAdvanced→Service Behaviorsを選択します。Service BehaviorsのNew Service Behavior Configurationをクリックし、ビヘイビアを作成します。

プロパティのNameにHelloServiceBehaviorを入力し、下側のAddボタンをクリックして表示されるダイアログで、Available elements にserviceCredentials, serviceDebug,serviceMetadataを選択し、Addボタンをクリック

左ペインのHelloServiceBehavior→serviceCredentials→serviceCertificateと展開しパラメータ設定します。FindValueをTestWCFServiceCert、X509FindTypeをFindBySubjectNameに設定します。

 HelloServiceBehavior→serviceMetadataを展開し、パラメータを設定します。httpGetEnabledをtrueに設定します。

左ペインのServicesノードをクリック、:右ペインのCreate a New Service選択します。

ウィザードを開始します。ウィザードで以下のように入力し下図のように構成の確認が表示されるので、Finishボタンをクリックして完了します。

  • Service type:HelloService.HelloService
  • Contract : HelloService.IHelloService
  • bindingの選択:Existing binding confugirationを選択し、Hello ServiceBinding.wsHttpBindingをドロップダウンから選択
  • Address:ブランク
  • Finishボタンクリックで完了

[Services]→[HelloService.HelloService]ノードをクリックしBehaviorConfigurationを作成したHelloServiceBehaviorに設定します。

上書き保存して、終了。

Web.configのsystem.serviceModelセクションは以下のように構成されます。

<system.serviceModel>
  <behaviors>
   <serviceBehaviors>
    <behavior name="HelloServiceBehavior">
     <serviceCredentials>
      <serviceCertificate findValue="TestWCFServiceCert" x509FindType="FindBySubjectName" />
     </serviceCredentials>
     <serviceDebug />
     <serviceMetadata httpGetEnabled="true" />
    </behavior>
   </serviceBehaviors>
  </behaviors>
  <bindings>
   <wsHttpBinding>
    <binding name="HelloServiceBinding">
     <security>
      <message clientCredentialType="None" />
     </security>
    </binding>
   </wsHttpBinding>
  </bindings>
  <services>
   <service behaviorConfiguration="HelloServiceBehavior" name="HelloService.HelloService">
    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="HelloServiceBinding"
     contract="HelloService.IHelloService" />
   </service>
  </services>
 </system.serviceModel>

IIS Managerからアプリケーションサイトを登録して動作することを確認します。以下はHelloServiceMessageSecurityという名前で登録したアプリケーションのsvcファイルにアクセスしたときにブラウザに表示される内容。

 

4 WCFクライアントの作成

4.1 プロキシクラスの生成

プロキシクラスの作成

>svcutil http://localhost/HelloServiceMessageSecurity/HelloService.svc /out:Proxy.cs

4.2 WCFクライアントプロジェクトの作成

ソリューションを右クリック→[Add]→[New Project]から、コンソールアプリケーションプロジェクトをWCFSample.ConsoleClientという名前で新規作成します。4.1で作成したProxy.csとoutput.configをApp.configにリネームしたファイルをプロジェクトに追加します。

App.configのデフォルト値は削除し、サーバー認証用の構成を追加します。(青色の文字が追加部分です)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_IHelloService">
          <security mode="Message">
            <transport clientCredentialType="Windows" proxyCredentialType="None"
                realm="" />
            <message clientCredentialType="None" negotiateServiceCredential="true"
                algorithmSuite="Default" establishSecurityContext="true" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="ClientBehavior">
          <clientCredentials>
            <serviceCertificate>
              <!-- 追加TrustedPeopleストアに証明書が配置してあるので、certificaValidateionModeをPeerOrChainTrustに設定-->
              <authentication certificateValidationMode="PeerOrChainTrust" />
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <client>
      <endpoint address="http://localhost/HelloServiceMessageSecurity/HelloService.svc"
          binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHelloService"
          contract="IHelloService" name="WSHttpBinding_IHelloService" behaviorConfiguration="ClientBehavior">
        <identity>
          <!-- 
          <dns value="TestWCFServiceCert"/>
          -->
          <certificateReference storeLocation="CurrentUser" storeName="TrustedPeople" x509FindType="FindBySubjectName" findValue="TestWCFServiceCert"/>
          <!-- svcutilで作成されるidentity 
          <certificate encodedValue="AwAAAAEAAAAUAAAA/LCMp73+Q4yRGSvbWlK0Xvp/4lAgAAAAAQAAAMYBAAAwggHCMIIBbKADAgECAhA3pL6bpAh1p0Waph8IbTeIMA0GCSqGSIb3DQEBBAUAMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5MB4XDTA4MDgyNDA2MDQ1NloXDTM5MTIzMTIzNTk1OVowHTEbMBkGA1UEAxMSVGVzdFdDRlNlcnZpY2VDZXJ0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRb11F35QARtlC9ZhTt0NPaJuevosQ5SU/k+nLqhuMUZ3pk27rtTeTSmABQ5torX1h8zlpLcZ1HXvVb0dXlScrvXtpGgBZpSiqoql7ojWMvYNesKpaxTwOVZGnRBKSQHI3/OZpt/ZwqAO54yamjZzZLL/6JLFdQDFn+G31v5t8ZwIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAeEJiQBPzpzocwtfnENyTcqQ7pIMyz08Fzli0R9wPsAl+JqEvqqAyVrg83CVw7o1OeUNPoyS3gQitY50pzzRL3" />
          -->
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>
</configuration>

使用する証明書は信頼されている認証機関から作成されたものではなくmakecertツールを使用して作成したものなので、そのままの構成ではサービスの認証時に例外が発生します。そのため、クライアント用のビヘイビアを作成し、TrustedPeopleストアに保存されていれば、証明書を正しいと判断するようにcertificationValidateionModeをPeerOnTrustに設定しています。製品環境ではこのChainTrustで動作するように信頼された証明機関で発行された証明書を使う必要があります。

endpointのidentityもDNS名と一致しないCNを持つ証明書を使用しているので、certificateReferenceセクションを使用して、サービスを認証するときに使用する証明書の参照先を設定しています。コメントアウトされているdns,certificate(svcutilで自動生成)でもローカルのテスト環境では動作しました。

Program.csのMainを次のように編集します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace WCFSample.ConsoleClient
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Console.ReadLine();
            try
            {
                HelloServiceClient proxy = new HelloServiceClient();
                System.Console.WriteLine(proxy.HelloWorld("Tarou"));
                ((ICommunicationObject)proxy).Close();
                proxy = null;
            }
            catch (Exception e)
            {
                System.Console.WriteLine(e.Message);
            }
            System.Console.ReadLine();
        }
    }
}

クライアントプロジェクトを実行して、動作することを確認します。

説明は以上です。誤り、指摘点とうがありましたら、ご連絡ください。