- PR -

WCFの認証にについての質問です

1
投稿者投稿内容
ゆうじ001
会議室デビュー日: 2005/07/30
投稿数: 13
投稿日時: 2008-02-07 00:14
《開発環境》
 Windows VISTA Ultima + VS2008 + IIS + WCF

 WCFを利用して、ユーザ認証をプログラムしているのですが、
 認証の際の必須条件なのか、設定がおかしいいのかわからないので、
 質問させていただきます。

 やりたいことは、
 WCFを利用して、「カスタム ユーザー名およびパスワード検証」です。
 http://msdn2.microsoft.com/ja-jp/library/aa702565(vs.80).aspx

 サンプル通りに作成して実行すると、
 「サービス証明書が指定されていません。ServiceCredentials で
  サービス証明書を指定してください。」
 のエラーが発生します。

 そこで、証明書が必要らしいことから、
 こちらのサンプルをみながら、
 http://msdn2.microsoft.com/ja-jp/library/aa354513(VS.85).aspx
 証明書の設定を行いました。
 動作するのは確認できたのですが、正常に動作するのは、ASP.NET開発サーバーのみ
 で、IISに移行したら、
 「System.Security.Cryptography.CryptographicException: キー セットが
  ありません。」
 というエラーが発生します。

 実際わからないところが、
 WCFのユーザ認証を行う際には、必ずX.509 証明書が必要ということなのでしょうか?
 また、ユーザ認証を、ASP.NETのメンバシップを利用する場合にも証明書が必要という
 ことなのでしょうか?

 以上、わかる方がいらっしゃいましたら、ご教授をお願いいたします。
ゆうじ001
会議室デビュー日: 2005/07/30
投稿数: 13
投稿日時: 2008-02-07 08:38
とりあえず自己解決しました。

やりたかったのは、WCFを使って、カスタムなユーザIDとパスワードを、
サーバー側で独自に判断し、認証されなかった場合は、OperationContract
を実行させないというのをしたかったのです。

実際の配置環境は、インターネット上にサーバを立てて、IISでWCFをホスト
させます。誰でもサービスを呼び出せないように認証を行い、さらに
データは、wsHttpBindingで暗号化させて、クライアントとの通信を行います。

以下の方法で、ローカルでは実現できました。実際の環境では、どうなのか
まだ不明な点もあります。ここはおかしいという点がありましたら、ご指摘を
お願いいたします。

-------------------------------------------------------------------------
1.WCFプロジェクトの作成
-------------------------------------------------------------------------
[ファイル]→[新規作成]→[プロジェクト]→[WEB]→[WCFサービスアプリケーション]で作成。

-------------------------------------------------------------------------
2.Service1.svc.cs のコードに、「CustomUserNameValidator」を追加。
  また、参照の追加で、「System.IdentityModel」を追加
-------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Principal;
using System.ServiceModel;

namespace WcfService1
{
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}

public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}

public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}

// ここで、DBなどからの情報を元に、任意に認証が可能
if (!(userName == "test1" && password == "1tset") && !(userName == "test2" && password == "2tset"))
{
throw new SecurityTokenException("Unknown Username or Incorrect Password");
}
}
}
}

-------------------------------------------------------------------------
3.Web.configの変更
-------------------------------------------------------------------------
<system.serviceModel>
<services>
<service name="WcfService1.Service1" behaviorConfiguration="WcfService1.Service1Behavior">
<!-- Service Endpoints -->
<endpoint address=""
binding="wsHttpBinding"
contract="WcfService1.IService1"
bindingConfiguration="Binding1">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<!--
メッセージ セキュリティを指定し、資格情報の種類を UserName
に指定するバインディングを構成
-->
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="WcfService1.Service1Behavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>

<!-- ここでカスタム認証、証明書を指定 -->
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="WcfService1.CustomUserNameValidator, WcfService1"/>

<serviceCertificate findValue="localhost"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>


-------------------------------------------------------------------------
3.証明書を作成するためのバッチファイルを作成

以下の makecert.exe や、certmgr.exeなどは、パスを通してください。
  また、FindPrivateKey.exe は

http://www.microsoft.com/downloads/details.aspx?FamilyId=2611A6FF-FD2D-4F5B-A672-C002F1C09CCD&displaylang=en
のサンプルをダウンロードして、展開すると
WCF_WF_CardSpace_Samples\WCF\Tools\FindPrivateKey にプロジェクトが
  あるので、これをビルドして利用します。

-------------------------------------------------------------------------
set SERVER_NAME=localhost
set COMPUTERNAME=ここにコンピュータ名

makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe

echo ************
echo 証明書の秘密キーに関する権限の付与
echo ************
certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -s TrustedPeople

echo ************
echo 証明書の秘密キーに関する権限の付与
echo ************
for /F "delims=" %%i in ('"FindPrivateKey.exe" My LocalMachine -n CN^=%SERVER_NAME% -a') do set PRIVATE_KEY_FILE=%%i
set WP_ACCOUNT=NT AUTHORITY\NETWORK SERVICE
(ver | findstr /C:"5.1") && set WP_ACCOUNT=%COMPUTERNAME%\ASPNET
echo Y|cacls.exe "%PRIVATE_KEY_FILE%" /E /G "%WP_ACCOUNT%":R

iisreset
pause

-------------------------------------------------------------------------
以上でサーバー側の設定がおわりました。
次は、クライアントの設定です。
-------------------------------------------------------------------------


ゆうじ001
会議室デビュー日: 2005/07/30
投稿数: 13
投稿日時: 2008-02-07 08:39
次にクライアント側です。

-------------------------------------------------------------------------
1.Windowsフォームプロジェクトの作成
-------------------------------------------------------------------------
[ファイル]→[新規作成]→[プロジェクト]
→[Windows]→[Windowsフォームアプリケーション]で作成。

-------------------------------------------------------------------------
2.WCFサービスの追加
-------------------------------------------------------------------------
[サービス参照の追加]から、WCFホストしているIISのアドレスを移動
サービス名がでてきたら、名前空間をWEBに変更。

-------------------------------------------------------------------------
3.フォームの編集
-------------------------------------------------------------------------
フォーム上にボタンをひとつ追加して、以下のコードを追加

private void button1_Click(object sender, EventArgs e)
{
WEB.Service1Client host = new WindowsFormsApplication1.WEB.Service1Client();
try
{
host.ClientCredentials.UserName.UserName = "test1";
host.ClientCredentials.UserName.Password = "1tset";
MessageBox.Show(host.GetData(123));
host.Close();
}

catch (System.Exception ex)
  {
host.Abort();
MessageBox.Show(ex.Message);
}

finally
{
}
}

-------------------------------------------------------------------------
4.App.Configの編集
-------------------------------------------------------------------------
<system.serviceModel>内に
<behaviors>
<endpointBehaviors>
<behavior name="ClientCredentialsBehavior">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="None" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
 を追加。

 また、既存の <client>を以下のように編集する
<client>
<endpoint address="http://**実際のURL**/Service1.svc" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IService1" contract="WEB.IService1" behaviorConfiguration="ClientCredentialsBehavior"
name="WSHttpBinding_IService1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>


-------------------------------------------------------------------------
以上で、WCFを利用したカスタム認証が可能になります。
-------------------------------------------------------------------------
ゆうじ001
会議室デビュー日: 2005/07/30
投稿数: 13
投稿日時: 2008-02-07 08:46
追記。

先のエラー内容について、
 「サービス証明書が指定されていません。ServiceCredentials で
  サービス証明書を指定してください。」
 のエラーが発生

 →やはり、証明書が必要なので、設定が必要であった。


 「System.Security.Cryptography.CryptographicException: キー セットが
  ありません。」
 のエラーが発生
 
 →証明書の秘密キーに関する権限の付与がなかったために発生。
  LocalMachine ストアに保存されたサーバー証明書を ASP.NET ワーカー プロセス   アカウントでアクセスできるようにしなければならなかった。


1

スキルアップ/キャリアアップ(JOB@IT)