忘れないように、メモ。
と、言っても、かなり長くなると思うし、日誌としてのものよりも、単に自分があとで困らないように、なので。
あと、C#でWebサービスプログラミングの話題なので、わからない人は回れ右です。
今日の日誌はそういうもの。
やりたいこと。
WSE3.0を利用して、SAMLによる認証機構を使ったWebサービス。
実際問題、認証や権限を云々するには、x509証明書を使ったサービスにするのが望ましいと思う。しかし、ユーザー名、パスワードという、「あまりセキュアとは言えない」認証システムの方が、ユーザーには取っつきやすいという感は否めない。そのため、SAMLによって、認証を行い、システムによって権限を承認を行う。つまり、「本人性」と、「システムへのアクセス権」を分離したい。というのが、基本。
SAMLを選択したのは、上記のような理由と共に、x509署名によるシステムへの移行をスムーズにするためでもある。
現時点、Webブラウザアプリケーションの多くは「その選択肢がベター」だからという理由によって作られているものは少ない。(そして顧客のオーダーから、ベストでもない場合が多い)
これは配布の容易さや、ユーザーが「言葉の響き」に騙されているというのもある。が、こちらとしても、売れないものを作っても仕方がないので、これらもふまえつつ、Webサービス+ClickOnceで、CSシステムのようなアプリケーションを設計する。
はじめに、はここまで。
で、こっから先は、C#の中級者くらい?で、それなりにx509の知識があって、Webサービスプログラミングができる事を前提とする。
C#中級者がどのレベルかは、知らない。
開発環境は、Windows XP SP2で、VisualStudio2005。SQL Server2005(Developer Ed)。SQL Serverは外部サーバに。開発時はVisualStudio2005のパーソナルWebサーバを利用する。(開発の容易さと、企業のセキュリティ問題を考慮するため)
実行環境としては、Windows XP SP2、またはWindows 2000 Proをクライアントに、Windows 2003 ServerのIIS6.0でWebサービスを公開する。SQL Serverは2005。DMZにWebサーバがおかれるだろうから、SQL Serverは裏に隠れることを前提とする。ただし、今回は2003Serverを1台しか用意できなかったので(さすがにそんなに使えるレベルのPCがないので)、WebサーバとDBサーバは同一のものを使っている。
まずは、前提知識として、以下のURIにあるMSDNのステップアップを学習し、Webサービス+Windowsクライアントアプリケーションを作れること。
http://www.microsoft.com/japan/msdn/thisweek/step7/#vs2005_DA
Visual Studio 2005 による分散アプリケーション開発編 (全 6 回)
このハンズオンを学習する事によって、分散アプリケーションの基礎が学べると同時に、VisualStudio2005の操作、及び進化したTableAdapterの利用方法が学べる。(DataSetというべきか)
実務レベルに引き落とすには、アーキテクトに任せるので、そこまでは触れない。ただし、ここに書かれているやり方で、大抵の業務アプリケーション開発におけるコーディング量は、3割程度は減少すると思う。
このハンズオンでは、SQL Serverは2005のExpressだが、外部サーバでDevでも問題はない。(実際そうなので)
総合Windows認証と、SQL Server認証におけるセキュリティ問題その他は、ここでは触れないので、実際の運用時には注意すること。
第2回の、「データ層の開発 - その 2」では、接続文字列がapp.configに書き込まれる事を確認しておくこと。
このapp.configの接続文字列は、実際にこれを利用する側で再編集できる。
いきなり触れておくが、以下のように利用するアプリケーション側で設定できる。(Webサービス側の話になるので、これはWeb.configに書かれる事になる)
<connectionStrings>
<remove name="EventDA.Properties.Settings.Event2005ConnectionString" />
<!-- 一応、これで上書きしておこう -->
<add name="EventDA.Properties.Settings.Event2005ConnectionString"
connectionString="Data Source=DBServer;Initial Catalog=Event2005DB;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
「第 3 回 ビジネス層の開発 - その 2」で、Webサービスを実装している。
ここでは、ローカルIISを利用しているが、セキュリティポリシーの関係から、ファイルシステムを利用する事にする。注意すべきは、「Step2」でWebサービスプロジェクトを作る時に、「場所」を(ファイルダイアログの下の方)HTTPではなく、ファイルシステムにすること。
これで、VisualStudioが提供する、ファイルシステム上のパーソナルWebサーバを利用できるようになる。
ただし、このパーソナルWebサーバは、ポートが動的に変更されるので、これを固定した方がよい。(クライアントからのWebサービスの呼び出し時に面倒な事になるので)
固定の方法は、ソリューションエクスプローラーで、Webサイトのプロパティを表示する。
「動的ポートの使用」というプロパティがあるので、これをfalseにする。(デフォルトはtrue)
その後、ポートを選ぶ。この時のポートは、他のプログラムで利用されていないポートである事。
「第 3 回 ビジネス層の開発 - その 3」で、テストアプリケーション開発時にうまくWSDLがもってこられない時は、Webサイトをリビルドして実行して見ると大抵の場合はうまくいく。
「第 4 回 プレゼンテーション層の開発 - その 1」は飛ばしてもよい。
今回はWindowsクライアントアプリを作るので、Webプレゼンテーション層はいらない。
ただし、ここに書いてある事がすらすらと読んで理解できるレベルでないなら、やっておくべき。
「第 5 回 分散アプリケーションの開発」は特に必要はない。(認証方式が違うので)
ただし、SQL Server2005上のデータベースに、Windows2003Server上のNetworkServiceロールのメンバをアクセス可能にする方法については確認しておくこと。SQL Server2005はセキュリティが2000に比べて格段にきつくなっているので、許可しない限りは、同一サーバであってもアクセスできないので注意が必要になる。(開発時はWindows認証を利用していて、それがAdministratorであれば繋がるので気にならないが、IISを実行するユーザはあくまで、NetworkServiceのメンバである。これは今後も重要になる)
以上で、ファイルシステムWebサーバを利用したVisualStudio2005での簡単な開発方法は理解できる。
(なお、サンプルのコードは全部VBだが、すべてC#で書き直した)
これで最も簡単なWebサービス+Windowsクライアントアプリケーションができたので、次にWSE3.0と、SAMLを利用したWebサービスに書き換える。
WSE3.0はともかく、SAMLを自力で実装するのは骨が折れるので、ここでは、SAML Quickstartを利用する。
2つのランタイム、及びコードは以下。
http://msdn.microsoft.com/webservices/webservices/building/wse/default.aspx
Web Services Enhancements (WSE)
もの凄くナチュラルに英語なので、注意。
と、言うより、3.0の日本語はないので。(多分WCFを先に出すと思うので、3.0が翻訳されるかすら怪しい)
ダウンロードとインストールはおなじみの通り。
ランタイムも入ってる。(はず)
サンプルコードがVBとC#とがあるので、場合によっては片方だけにしてもよし。たいしたサイズではないですが。
サンプルはインストールディレクトリの下、Samplesフォルダにあるので、見ておくとよいかも知れません。ただし、当然全部英語です。
基本的にはconfigで設定して使うものがほとんどなので、configの設定方法さえ覚えてしまえば問題はないかもしれません。カスタムのSoapフィルタを作るとかいうのであれば、それなりの構造を理解する必要はあります。あとで触れます。
SAML Quickstartは、以下のアドレスにあります。
http://www.gotdotnet.com/codegallery/codegallery.aspx?id=8da852b9-2c0d-4eb7-a2de-77222a4075f6
SAML STS for WSE 3.0 QuickStart
こちらも英語。
Downloadにはメンバー登録が必要なようです。昔はなかったんだけどなー。
ライセンスはLicenseを確認してください。商用の利用も可能ですが、copyrightを含める必要があります。コードの変更も許可されています。
コードの変更ですが、このライブラリ(必要なのは、WseSamlプロジェクトだけですが)にはバグがあります。
このまま使用すると、ユーザー名やRoleがSAMLから取得できません。このバグの問題により、以下のコードは期待した動作をしません。
[WebMethod]
public EventDA.SessionEntryDataSet GetEntry(int eventID)
{
EventDA.SessionEntryDataSet ds = new EventDA.SessionEntryDataSet();
EventBZ.SessionEntry se = new EventBZ.SessionEntry();
//プロファイルの取得
int profileID = 0;
//SAMLの権限はここに入ってる
// しかもこれ、ライブラリがバグってやがって、直したよ!ちくしょぅめ!
if (RequestSoapContext.Current.Envelope.Context.IdentityToken.Principal.IsInRole("Administrator"))
{
profileID = 18;
}
//セッションリスト取得
ds = se.GetEntry(eventID, profileID);
return ds;
}
このバグをfixするには、SamlPolicyAssertion.csファイル内にある、ServiceInputFilterクラスのValidateMessageSecurityメソッドを修正する必要があります。
//The signing token must be one of the configured trusted issuers
foreach (X509SecurityToken trustedToken in _trustedTokens)
{
if(trustedToken.Equals(samlToken.SigningToken))
{
trusted = true;
break;
}
}
if (!trusted)
{
throw new InvalidOperationException(Resources.UntrustedSTS);
}
envelope.Context.OperationState.Set(samlToken);
envelope.Context.IdentityToken = samlToken;
上記のコードを、以下のように修正します。
//The signing token must be one of the configured trusted issuers
foreach (X509SecurityToken trustedToken in _trustedTokens)
{
if(trustedToken.Equals(samlToken.SigningToken))
{
trusted = true;
break;
}
}
if (!trusted)
{
throw new InvalidOperationException(Resources.UntrustedSTS);
}
envelope.Context.Credentials.UltimateReceiver.SetClientToken<SamlToken>(samlToken); //Added this line
envelope.Context.OperationState.Set(samlToken);
envelope.Context.IdentityToken = samlToken;
これで期待する動作になります。
(現在のリリースでは修正されているかも知れませんが、わかりません)
SAML STS for WSE 3.0 QuickStartにはサンプルコードも含まれているので、動作に関してはその辺りも確認するといいです。
インストール方法等は、zipファイル内のpdfファイルを確認してください。サンプルを動かすようにするのも一苦労かとは思いますが。
さて、これでツールは揃いましたので、動作の原理について考えます。
WSE3.0は、ASP.net Webサービスで、通信部分をラップするようなイメージです。
開発者はその内部を理解する必要はないように設計されていますし、気にするべきではありません。通常のWebサービスを実装するのと同じように、開発者はメソッドにWebMethodアトリビュートをつけ、そのクラスにMicrosoft.Web.Services3.Policy属性をつけるだけです。
すでに作成した上記のハンズオンをWSE3.0を利用して、SAMLでポリシーを記述する場合のサービス部分のコードは以下のようになります。
using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using Microsoft.Web.Services3;
/// <summary>
/// SessionEntry の概要の説明です
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[Microsoft.Web.Services3.Policy("Sign-Saml")]
public class SessionEntry : System.Web.Services.WebService
{
[WebMethod]
public EventDA.SessionEntryDataSet GetEntry(int eventID)
{
EventDA.SessionEntryDataSet ds = new EventDA.SessionEntryDataSet();
EventBZ.SessionEntry se = new EventBZ.SessionEntry();
//プロファイルの取得
int profileID = 0;
//SAMLの権限はここに入ってる
// しかもこれ、ライブラリがバグってやがって、直したよ!ちくしょぅめ!
if (RequestSoapContext.Current.Envelope.Context.IdentityToken.Principal.IsInRole("Administrator"))
{
profileID = 18;
}
//セッションリスト取得
ds = se.GetEntry(eventID, profileID);
return ds;
}
[WebMethod]
public int UpdateEntry(EventDA.SessionEntryDataSet ds)
{
EventBZ.SessionEntry se = new EventBZ.SessionEntry();
return se.UpdateEntry(ds);
}
}
僕は開発者に新しい苦労をさせるべきではないと思いますし、するべきではないと思います。
属性こそ、初めて見るようなものがつけられてはいますが、開発者は多くの新しい知識を必要とせず、WSEを利用した、SAMLによる認証機構をスムーズに作れます。
それでは、実際の動きを考えます。
動きは簡単で、クライアントは「SAMLをもらい、Webサービスにアクセスする」という構造です。
まずは、SAMLを発行するサーバサイドのWebサービスを実装します。
これはSAML STSのサンプルにあるSamlSecurityTokenServiceプロジェクトをベースにします。
このWebサービスプロジェクトには、asmxファイルはありません。全ての設定はWeb.configと、samlPolicy.configに記述されます。ちなみに呼び出されるのは、<System.Web>Elementの<httpHandlers>Elementに書かれています。
このサービスは、ユーザーの認証と権限の取得に関して、ASP.net2.0の新機能である、membershipを利用しています。設定は、%lt;membership%gt;Elementを参照してください。
メンバーシップAPIの実装、及び動作に関する知識は、下記のURIを参照してください。
http://www.microsoft.com/japan/msdn/thisweek/step7/aspnet/MembershipAndRole/memberandrole1.aspx
ステップ 7 ハンズオン : ASP.NET 2.0 メンバーシップとロールの管理 その 1
この例ではMDBを利用していますが、SQL Server等でもできます。
この例の中では、machine.configを出していますが、このファイルを変更するべきできありません。
(もっといい奴がMSDNにあったような気がするんだけど、どこにいってしまったのだろう…)
今回はSQL Serverを利用しました。
ユーザー名とパスワード、そして権限(ロール)を利用しています。メンバーシップAPIの部分のconfigだけを抜き出すと、以下のようになります。
<!--
メンバーシップAPIの設定です。
defaultProvider:省略可能なString型の属性で、規定のメンバシッププロバイダの名前です。
-->
<membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
<providers>
<clear /><!--これで、規定のメンバシップは利用されなくなる-->
<!--
新しい規定のメンバシップです。
場合によってはapplicationNameをユーザ毎に切り替える必要があるかもしれないです。
name:プロバイダ名です。
connectionStringName:connectionStrings要素の1つを設定します。
applicationName:一意のアプリケーション名を指定します。
enablePasswordReset:ユーザにパスワードのリセットが許可されているか。
enablePasswordRetrieval:ユーザにパスワードの呼び出しが許可されているか。
requiresQuestionAndAnswer:秘密の質問に答えるべきであること。
requiresUniqueEmail:登録されるe-mailがユニークであること。
maxInvalidPasswordAttempts:メンバシッププロバイダがパスワードをロックするまでの試行回数。
passwordAttemptWindow:パスワード間違いを保持する時間の分。この時間内にmaxInvalidPasswordAttempts回間違うと、パスワードはロックされる。
passwordFormat:パスワードを格納する時の処理。ここでは一方向ハッシュ。SQLサーバでないとダメだけど、salt値でハッシュされる。
-->
<add name="SqlProvider"
connectionStringName="MembershipDatabase"
applicationName="SAML"
enablePasswordReset="true"
enablePasswordRetrieval="false"
requiresQuestionAndAnswer="false"
requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5"
passwordAttemptWindow="10"
passwordFormat="Hashed"
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</membership>
<roleManager defaultProvider="SqlProvider" enabled="true" cacheRolesInCookie="true" cookieName=".ASPROLES" cookieTimeout="30" cookiePath="/" cookieRequireSSL="false" cookieSlidingExpiration="true" cookieProtection="All">
<providers>
<add name="SqlProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="MembershipDatabase" applicationName="SAML" />
</providers>
</roleManager>
実際の接続文字列は、connectionStringName属性に書かれているものを見ます。これはconfigに別に記述します。
何故このような仕様になっているのかというと、ハンズオンでのデータベースの接続文字列の部分を確認してください。ASP.net 1.xから比べて、データベースアプリケーションの作成が2.0では容易になっているだけでなく、セキュリティを意識して、この接続文字列を暗号化する事も可能です。(DPAPIで暗号化をするためのものを自力で実装する必要はありません)
この方法については、以下のURIを参照してください。
http://www.microsoft.com/japan/msdn/enterprise/pag/securityguidance/paght000005.aspx
How To: DPAPI を使用して ASP.NET 2.0 内の構成セクションを暗号化する方法
実際の実装は、以下のようになります。
<!--
接続文字列の定義はここで設定できるようになりました。
接続文字列の設定です。納品するサーバでは、暗号化するべきです。
nameが使う時の、一意になる名前です。(上書きされるです)
addでいくつも定義できるようになっています。
Integrated Security=SSPI;を使うには、Windows認証で、サーバにこのユーザが繋げないとためです。
これは暗号化できるです。暗号化については、下をみるです。
-->
<connectionStrings>
<add name="MembershipDatabase" connectionString="Server=DBServer;Uid=aspnetdb;pwd=aspnetdb" />
</connectionStrings>
<!--
暗号化のサポートのためのものです。
これはユーザストアを利用した、DPAPIによる暗号化です。
aspnet_regiis -pef "connectionStrings" "F:\OSIGOTO\Test\FormsAuthSQL" -prov "MyUserDataProtectionConfigurationProvider"
で、connectionStringsのセクションが、ユーザストアで暗号化されるです。最後の引数は、任意のnameにできるです。
ただ、DPAPIである以上、キーはOSが管理するユーザーに対して1つのものなので、複数並べ立てても意味はないです。
復号化するには
aspnet_regiis -pdf "connectionStrings" "X:\OSIGOTO\Test\FormsAuthSQL"
です。
-->
<configProtectedData>
<providers>
<add useMachineProtection="false"
keyEntropy=""
name="MyUserDataProtectionConfigurationProvider"
type="System.Configuration.DpapiProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</configProtectedData>
実際のサーバ上では、セキュリティのために暗号化するべきです。
このSAMLを発行するサービスと、クライアントアプリケーションは、WSEを利用して、セキュリティを考慮します。
そのため、<System.Web>Elementには、<webService>Elementをつけて、拡張する必要があります。
以下のコードが、<System.Web>Element内に必要です。
<webServices>
<soapExtensionImporterTypes>
<add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</soapExtensionImporterTypes>
</webServices>
これらはSAML STSのサンプルコードに記載されているものなので、サンプルコードをまずは動かせるようにするべきです。
そして、必要に応じて、拡張していくと理解できます。
なお、実際の納品サーバにおけるメンバーシップの追加等をIIS上から行う方法等については、以下のURIにあるサンプルを参照するとよいと思います。(僕はまだざっとしか確認してないですが)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/ASP2memroleman.asp
Microsoft ASP.NET 2.0 Member/Role Management with IIS, Part 2: Implementation
英語ですが。
さて、実際の細かい設定は、<configSections>で追加される新たなElementによります。ここでは、<microsoft.web.services3>と<WseSaml>です。
コメントをつけた、完全なconfigは以下です。
<!--
WSE3.0用の設定
ここでWSE3.0を設定する。この設定は多岐にわたるのと、複雑なので、ちょっと覚悟するように。
-->
<microsoft.web.services3>
<!--
デバッグとかのためのトレース情報を格納するか。また、それのファイル名。
デバッグのために行き来するデータを表示したり、内部のログを収集するには、trueにする。
-->
<diagnostics>
<trace enabled="true" input="samlAsserionServer_InputTrace.webinfo" output="samlAsserionServer_OutputTrace.webinfo" />
</diagnostics>
<!--
トークン(ユーザー認証データ)の発行に対する設定。
statefulSecurityContextTokenというのは、セキュリティに関するトークンを保持するかどうか
-->
<tokenIssuer>
<statefulSecurityContextToken enabled="true" />
</tokenIssuer>
<!--
セキュリティに関する設定はいろいろと細かいです。
keyIdentifierMappingは定義です。
securityTokenManagerはトークンを解析するためのクラスです。
-->
<security>
<keyIdentifierMapping>
<add valueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID"
tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" />
</keyIdentifierMapping>
<securityTokenManager>
<!-- AssertionにSAMLを使用する。その使用はCustomSamlTokenManagerを用いる。 -->
<add localName="Assertion" namespace="urn:oasis:names:tc:SAML:1.0:assertion"
type="BCL.Web.Services.WSE3.SAML.CustomSamlTokenManagers.CustomSamlTokenManager, BCL.Web.Services.WSE3.SAML.CustomSamlTokenManagers"/>
<!-- UserNameTokenを使用するクラスです。実際はこの中で処理されて、いろいろいじくられます。 -->
<add localName="UsernameToken" namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
type="BCL.Web.Services.WSE3.SAML.CustomSamlTokenManagers.CustomUsernameTokenManager, BCL.Web.Services.WSE3.SAML.CustomSamlTokenManagers" />
</securityTokenManager>
<!-- キーアルゴリズムのハッシュに利用されるもの。 -->
<binarySecurityTokenManager>
<add valueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
<keyAlgorithm name="RSA15" />
</add>
</binarySecurityTokenManager>
<!--
x509署名に関する設定。
<x509 allowTestRoot="true" verifyTrust="false" />
上記はテスト用。最も強力にするには、下記。
<x509 allowTestRoot="false" revocationMode="Online" verifyTrust="true" />
これは、Test署名のルートを許可しない。Onlineで署名確認を行う。署名が正しくなければならない。というきつい条件。
納品サーバではそうするべきかもしれないが、署名の配布問題によっては、一考すべきもの。
-->
<x509 allowTestRoot="true" verifyTrust="false" />
<!--
xmlの署名には時間が含まれるのだけれど、その時間とサーバが確認する時間との誤差をどれだけ認めるか。
単位は秒。最低0。即時切れを起こす(と思う)から、86400(24時間)まで。
ただしこれはセキュリティに関する問題なので、あまり長い時間にするべきではない。
とりあえず5分くらいが無難。
-->
<timeToleranceInSeconds value="300" />
</security>
<!-- SAMLに関してのポリシーは別ファイル -->
<policy fileName="samlPolicy.config" />
</microsoft.web.services3>
<!--
SAML拡張を使うために宣言されたものを受け取る先
-->
<WseSaml>
<!--
トークン発行者としての設定
ttlInSecondsはtimeToleranceInSecondsと同じ
-->
<samlTokenIssuer allowCachingToken="true" ttlInSeconds="300">
<!--
このコンフィグはSAMLのトークン発行の署名に関してです。
-->
<serviceTokens>
<!-- SAML Authority certificate -->
<add uri="http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue/SAML" storeLocation="LocalMachine" storeName="My" findValue="CN=SAML Authority" findType="FindBySubjectDistinguishedName" />
<!-- Sample service -->
<add uri="http://localhost:8080/SampleService/Service.asmx" storeLocation="LocalMachine" storeName="My" findValue="CN=SampleService" findType="FindBySubjectDistinguishedName" />
<add uri="http://localhost:8080/EventSV/SessionEntry.asmx" storeLocation="LocalMachine" storeName="My" findValue="CN=SampleService" findType="FindBySubjectDistinguishedName" />
</serviceTokens>
<policy name="issuerPolicy" />
</samlTokenIssuer>
</WseSaml>
<securityTokenManager>では、独自の実装をしています。
これは、SAML STSのサンプルで言うところの、CostomTokenManagerを拡張したものと考えてください。サンプルではここで初めて、SQL Serverを使ったメンバーシップを利用しています。このCostomされたTokenManagerは、それをさらに拡張したものです。
実際、業務レベルで利用するには、独自に拡張したAssertionや、UsernameTokenを必要とするはずです。
<WseSaml>Element内の、<samlTokenIssuer>Element(SAMLの表明発行者)では、このSAMLが通用する先と、そのx509署名を設定します。
このx509署名は、このサービスが実装させるサーバ側に秘密キーをもった証明書である必要があります。サンプルでは1台のPC上でサーバとクライアントが同居するので切り分けがわかりませんが、これらは、サーバサイドが秘密キーを持つ証明書です。(ちゃんと確認はしてないのですけど)
クライアントサイドには、これらの公開キーを含んだ証明書が必要です。
証明書に関してよりも先に、samlPolicy.configを見ます。
<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
<policy name="issuerPolicy">
<usernameForCertificateSecurity establishSecurityContext="false" renewExpiredSecurityContext="false" requireSignatureConfirmation="false" messageProtectionOrder="SignBeforeEncryptAndEncryptSignature" requireDerivedKeys="true">
<!--
サービスをしてよい対象
-->
<serviceToken>
<x509 storeLocation="LocalMachine" storeName="My"
findValue="E=webmaster@studio-odyssey.net, CN=studio Odyssey CodeCr, OU=PLANET, O=studio Odyssey, L=side, S=7"
findType="FindBySubjectDistinguishedName" />
</serviceToken>
<!-- データ保護の設定 -->
<protection>
<request signatureOptions="IncludeSoapBody" encryptBody="true" />
<response signatureOptions="IncludeSoapBody" encryptBody="true" />
<fault signatureOptions="IncludeSoapBody" encryptBody="false" />
</protection>
</usernameForCertificateSecurity>
</policy>
</policies>
<serviceToken>で、見慣れないx509証明書があります。すでにこのsubjectから想像可能なように、この証明書は自宅で作ったものです。
x509証明書は自分がルートになりさえすれば、自宅からも発行できます。
そのルートを誰が信頼するか、という問題こそありますが、企業の「アプリケーションを保守する」または「サービスの利用を保証する」というレベルで証明書を発行するのはよいのではないかと思います。(お客さんに信頼してもらうというだけの話です。それはアプリケーションだけの問題ではないでしょう)
証明書サーバは、Windows2000Server、またはWindows2003Serverであれば、自分で作る事が可能です。
Microsoft証明書サーバで検索すれば、ある程度の情報は揃うでしょう。まずはIISのSSL通信ができるように設定してみてください。
- Microsoft証明書サーバは、Windowsアプリケーションの追加と削除で追加できます。
- 証明書サーバをインストールすると、管理ツールで出ます。
- 証明書サーバは、Windows2003Serverの場合、セキュリティ的に動かない事があります。
- 可能であれば、IISの設定を見直してください。
- 証明書サーバがインストールされた後にIISにマッピングする事も可能です。ディレクトリはcertsrv。(規定ではSystem32下)
- ブラウザから発行要求をした場合は、証明書サービス側で発行をします。(規定の動作)
- クライアントとSSL通信をするためには、ルート証明書を貰う必要があります。(CertSrvにアクセスするなりなんなりで)
実際に格納される証明書を確認するには、IE等のブラウザから、[ツール]-[インターネットオプション]-[コンテンツ]-[証明書]で確認できますが、より詳細に複雑な事をする必要が出てきます。
ここでは基本的に、Microsoft Management Consoleを利用します。
[ファイル名を指定して実行](Windowsキー+R)で、mmcとタイプして、OKします。すると、コンソールが起動します。[ファイル]-[スナップインの追加と削除]で、表示されるダイアログの中から、証明書を選択します。
すると、証明書ストアの何処を選ぶかを選択する必要ができます。証明書ストアには、ユーザとマシンがあります。言葉から想像できるように、ユーザ証明書ストアは、実行ユーザがアクセスできるもので、マシンストアはマシン内の各ユーザがアクセスすることがあるものです。
例えば、すでに想像できるように、サーバサイドでIISが証明書にアクセスするためには、マシンストアにあり、かつ、IISの実行ユーザであるNetworkService(2000Serverでは、ASPNETユーザ)がその証明書にアクセスできなければなりません。
少なくとも、以下の条件が、サービスサーバとクライアント側で満たされている必要があります。
- Webサービス側の証明書には秘密キーが含まれていて、IISの実行アカウントが秘密キーにアクセスできなければならない。
- LocationはLOCAL_MACNIE(ローカルコンピュータ)で、ストアはMY(個人)でなければならない。
- クライアントはサーバが持つ証明書の公開キーを持つ証明書をもっていなければならない。
- クライアントが必要とする証明書のうち、クライアントのsamlPolicy.configで設定する証明書以外のものはLOCAL_MACHINEのMYストアになければならない。
1台のPC上で実行するのではない場合は(大抵そうでしょうが)、このようになっていなければなりません。(ただし、3つめの条件は例外もあります)
証明書の発行、及びインポート、アクセスの許可その他に関しては、winhttpcertcfgというツールを利用します。
以下のURIを参照してください。
http://support.microsoft.com/default.aspx?scid=kb%3Bja%3B901183
ASP.NET Web アプリケーションで認証用のクライアント証明書を使用して Web サービスを呼び出す方法
最低限覚えなければいけない構文は以下です。
- WinHttpCertCfg.exe -g -c LOCAL_MACHINE\MY -s "IssuedToName " -a "NetworkService "
- Winhttpcertcfg.exe -i PFXFile -c LOCAL_MACHINE\My -a "AccountName"
なお、このツールは規定では、Program Files\Windows Resource Kits\Toolsにインストールされます。
実際に証明書ストアにアクセスできるかどうかは、以下のコードでチェックできます。
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography.X509Certificates;
namespace StoreConsole
{
class Program
{
static void Main(string[] args)
{
//Create new X509 store called teststore from the local certificate store.
X509Store store = new X509Store("My", StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection storecollection = (X509Certificate2Collection)store.Certificates;
Console.WriteLine("Store name: {0}", store.Name);
foreach (X509Certificate2 x509 in storecollection)
{
Console.WriteLine("certificate name: {0}", x509.Subject);
Console.WriteLine(" HasPrivateKey: {0}", x509.HasPrivateKey ? "true" : "false");
try
{
if (x509.HasPrivateKey)
Console.WriteLine(" HasPrivateKey-Size: {0}", x509.PrivateKey.KeyExchangeAlgorithm);
}
catch (Exception)
{
Console.WriteLine(" HasPrivateKey-Size: Error");
}
}
//Close the store.
store.Close();
#if DEBUG
Console.Read();
#endif
}
}
}
Webサイトで確認する場合には、上記のコードをPage_Loadで実行し、ページに書き込めばいいです。(divでも作って、innerHTMLにでも)
以上の事を知っていれば、あとはSampleを拡張して行くことによって、WSE3.0と、SAML STSを利用したWebサービスを実装できるようになります。
なお、カスタムされたSoapFilterを実装するには、WseSamlプロジェクトの、SamlPolicyAssertionクラスを継承して拡張すればよいはずだが、これについては別の機会にする。
ご意見、ご質問、アドバイス(特に熱望)に関しては、みんなのケイジバンによろしくお願いします。
これが正しいのかは、わからないしぃ。