Matt Powell
Microsoft Corporation
December 2002
対象 :
MicrosoftR ASP.NET
Microsoft .NET Framework
Microsoft Visual StudioR プロジェクト
SOAP
Web Services Enhancements 1.0 for Microsoft .NET
WS-Security 仕様
概要 : Web Services Enhancements 1.0 for Microsoft .NET を使用して、Web サービスのデータの認証と署名に WS-Security 仕様を利用する方法について説明します。
Web Services Enhancements 1.0 for Microsoft .NET (英語) をダウンロードする。
目次
はじめに
WSE 環境
基本 UsernameToken 認証
WS-Security トークンとしての X.509 証明書の送信
デジタル署名
まとめ
参考資料
はじめに
マイクロソフトは、Web Services Enhancements 1.0 for Microsoft .NET (WSE) の導入により、SOAP メッセージ内部にセキュリティを実装するための最初のツールセットを提供することになります。 Web サービスが、基になるトランスポートのセキュリティ機能を使用することに厳密に結び付けられることはなくなりました。 SOAP メッセージを単独で認証でき、SOAP メッセージの完全性が確認されるようになったので、WS-Security 仕様で定義されているメカニズムを使用して、SOAP エンベロープ内のすべての SOAP メッセージを暗号化することもできるようになりました。 この資料では、WSE を使用して、Web サービスのデータの認証と署名に WS-Security を利用する方法について見ていきます。
WSE 環境
WSE は、Web サービスを作成して使用するための .NET Framework サポートの上位に位置付けられます。 WSE サポートの中心は、着信する SOAP メッセージの WS-Security ヘッダーやその他のヘッダーを調査したり、発信する SOAP メッセージに WS-Security ヘッダーやその他のヘッダーを追加するためのインターフェイスを提供する Microsoft.Web.Services.SoapContext クラスです。 Web サービスを使用するためのコードを記述する開発者向けに、SOAP 要求と応答に SoapContext を追加して、Framework 機能を拡張するラッパー クラスが作成されました。 サーバーでは、着信する SOAP メッセージを検証して、WebMethod 内からアクセスできる要求と応答の SoapContext を作成する Microsoft ASP.NET SOAP 拡張機能が作成されました。
基本的な WSE 環境を構成するには、WSE SOAP 拡張機能を使用するように ASP.NET アプリケーションを構成する必要があります。 Machine.config にエントリを追加することにより、コンピュータ単位でこの構成を行うことができますが、WSE サポートを必要としない場合もあります。 ここでは、WSE サポートを特定の仮想ディレクトリに追加します。 この操作を行うには、仮想ディレクトリの Web.config に /configuration/system.web/webServices/soapExtensionTypes/Add 要素を追加します。 追加した要素は、以下のようなエントリになります。ただし、type 属性はすべて 1 行に記述する必要があります (ここでは読みやすさを考慮して、コードを改行しています)。
<webServices>
<soapExtensionTypes>
<add type="Microsoft.Web.Services.WebServicesExtension,
Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" priority="1" group="0" />
</soapExtensionTypes>
</webServices>
WSE については、これで準備が整いました。 WS-Security のある側面に関しては追加の構成設定を行う必要がありますが、これについては後ほど説明します。 ここで必要な操作は、Visual Studio .NET プロジェクトで Microsoft.Web.Services.dll への参照を追加することです。この操作が完了すると、準備が整います。
基本 UsernameToken 認証
SOAP メッセージにデジタル署名を行うことができるようになるには、まず、署名を行っているユーザーを把握する能力を身に付ける必要があります。 Bob からメッセージが来ることを期待しているときに、そのメッセージが悪意のある第三者から来た場合、メッセージの完全性の検証は役に立ちません。 したがって、UsernameToken の概念および WSE による UsernameToken の検証方法を調べることから、WS-Security の調査を開始します。
UsernameToken 要素は、基本的なユーザー名とパスワードの検証を行う手段を提供するように、WS-Security で定義されています。 HTTP を使用した経験があれば、基本的なユーザー名とパスワードの検証が基本認証と似ていることがわかります。 WS-Security では、3 つの形式の UsernameToken 要素によっていくつかの異なる形式が提供されます。 それぞれの形式を以下に示します。
<!-- No Password -->
<UsernameToken>
<Username>Bob</Username>
</UsernameToken>
<!-- Clear Text Password -->
<UsernameToken>
<Username>Bob</Username>
<Password Type="wsse:PasswordText">Opensezme</Password>
</UsernameToken>
<!-- Digest: SHA1 hash of base64-encoded Password -->
<UsernameToken>
<Username>Bob</Username>
<Password Type="wsse:PasswordDigest">
QSMAKo67+vzYnU9TcMSqOFXy14U=
</Password>
</UsernameToken>
最初の形式にはパスワードが含まれていません。そのため、この形式は別のメカニズムで個人を認証したり、ユーザー名のトークンを識別のみに使用する場合の選択肢と考えることができます。 2 番目の形式には、クリア テキスト パスワードが含まれています。 予想どおり、相手側の認証プロセスでは、ある種のデータベースで有効なユーザー名とパスワードを調べ、それが一致するかどうかを確認することが必要となります。 3 番目の形式では、クリア テキスト パスワードではなく、パスワードのダイジェストが送信されます。 このアプローチの利点は、パスワードがネットワーク上に送信されないことです。したがって、悪意のある仲介者がパスワードを解明することはできません。 このアプローチの欠点は、悪意のある仲介者がハッシュされたパスワードを送信すると、その仲介者が元の送信者として認証される可能性があることです。
この問題を回避するために、Web Services Security 補遺で新たな保護手段が追加されました。 この補遺では、パスワードのハッシュのみを送信するのではなく、パスワードのダイジェストを送信する必要があることが明記されています。 このダイジェストには、パスワード、Nonce (機能的にこの要求を識別する一意の文字列)、および作成時刻の組み合わせであるハッシュが含まれています。 そのため、2 つのパスワードのハッシュが同じになることはありません。 改訂した UsernameToken を以下に示します。
<!-- Revised UsernameToken -->
<wsse:UsernameToken
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility"
wsu:Id="SecurityToken-58564463-5bdc-4a6b-a7fb-94a0d7357a20">
<wsse:Username>Joe</wsse:Username>
<wsse:Password Type="wsse:PasswordDigest">
gpBDXjx79eutcXdtlULIlcrSiRs=
</wsse:Password>
<wsse:Nonce>
h52sI9pKV0BVRPUolQC7Cg==
</wsse:Nonce>
<wsu:Created>2002-11-04T19:16:50Z</wsu:Created>
</wsse:UsernameToken>
すべての正当な要求には異なるハッシュが含まれますが、他人の正当な要求から UsernameToken 全体を取り出し、それを独自の正当ではない要求に追加する悪意のあるユーザーに注意する必要があります。 タイムスタンプの有効期限 (WSE の別の機能) を十分短い期間に設定し、サーバー側で有効期限を論理的に設定することによって、リスクを最小限に抑えることができます。 たとえば、メッセージの有効期限が 30 秒後に切れるように指定できます。その結果、メッセージが遅れて伝達されることはなく、サーバー側では Created 要素に入力した時間から 30 秒後以降に UsernameToken が受け取られることはありません。 コンピュータ間でのクロックの同期問題が原因で、有効な要求が拒否される可能性があることに注意してください。また、状況によっては、ある時間帯で古い要求が許可される可能性もあります。 そのため、作成時刻を使用しても、完全な解決策にはなりません。 Web サービスでは、さらにリスクを回避するために、最近受け取った UsernameToken からの Nonce 値のテーブルを管理できますが、以前に Nonce 値が使用されていた場合は要求を許可できません。 テーブルでは、Nonce エントリが有効期限に達するまで、そのエントリが保持されることだけが必要です。 同じ Nonce 値を含む複数の要求を受け取った場合、最初に正当ではない要求を受け取る可能性があるので、要求を両方とも破棄することを検討する必要があります。 さらに、Nonce 値の確認を行った場合でも、悪意のある仲介者によって受信メッセージが目的地に到達することが妨げられ、その受信メッセージが元のメッセージの UsernameToken を使用して、悪意のある仲介者自身のメッセージと置き換えられるというシナリオに対しては、保護できないことに注意してください。 メッセージにデジタル署名を追加して、この種の攻撃から保護する必要があります。 デジタル署名については、この資料の後半で説明します。
当然のことながら、この分野のすべてのハッシュによる保護によって、送信者と受信者の両方がユーザーのパスワードに関する知識を持っている必要があるという事実がなくなる訳ではありません。 クライアント側では、ユーザーにパスワードを問い合わせることが必要となることを予測できます。 また、サーバー側では、有効なユーザー名とパスワードの組み合わせを参照できるある種のテーブルが必要になります。 WSE でこの機能を有効にするには、パスワード プロバイダと呼ばれる拡張メカニズムを使用します。
パスワード プロバイダ
WSE では Microsoft.Web.Services.Security.IPasswordProvider インターフェイスが定義されています。これは、パスワード プロバイダとして登録するためにクラスで実装できるインターフェイスです。 このインターフェイスには、GetPassword という関数があり、この関数は、入力として Microsoft.Web.Services.Security.UsernameToken を受け取ります。 GetPassword 関数は、指定したユーザーのパスワードを返します。 この考え方は、有効なユーザー名とパスワードの組み合わせを格納するどのようなメカニズムでも使用でき、IPasswordProvider インターフェイスを実装するクラスを提供して WSE が特定のパスワード格納メカニズムにアクセスできるようにするというものです。 また、認証インフラストラクチャをさらに制御するために、UsernameToken で独自のダイジェストとハッシュの組み合わせを実行するという選択肢もあります。この場合、共有シークレットを使用する可能性があります。
WSE に特定のパスワード プロバイダを通知するには、適切な WSE 構成設定を行う必要があります。 この操作では、アプリケーション構成ファイルの構成要素内部に microsoft.web.services 要素を追加する必要があります。 また、この特定の構成情報を理解できる WSE クラスを指定することも必要です。 次の configSections は、コンピュータ上の Machine.config または個別の Web.config に追加できます。 type 属性は 1 行で記述する必要がありますが、ここでも、読みやすさを考慮して改行しています。 このノードは、.config ファイルの /configuration/configSections にあります。
<configSections>
<section name="microsoft.web.services"
type="Microsoft.Web.Services.Configuration.WebServicesConfiguration,
Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
</configSections>
ASP.NET アプリケーションの場合、WSE に特定のパスワード プロバイダを通知するために、次のエントリを Web.config 内の親構成要素の子要素として追加できます。
<microsoft.web.services>
<security>
<!-- This is the class which will provide the password hashes
for the UsernameToken signatures. -->
<passwordProvider
type="WSE_Security.PasswordProvider, WSE-Security" />
</security>
</microsoft.web.services>
passwordProvider 要素の type 属性は、IPasswordProvider インターフェイスを実装するするために記述したクラスを示します。 この場合、クラスは PasswordProvider と呼ばれます。このクラスは、WSE_Security 名前空間で定義され、WSE-Security.dll アセンブリに存在します。 説明を簡単にするために、このクラスはすべてのユーザー名に対して "opensezme" というパスワードを返します。 大半のシナリオでは、このクラスが、SQL データベースまたはその他の格納場所から適切なパスワードを読み取るロジックを実装する場所になります。 一般的なクラスのコードを以下に示します。
namespace WSE_Security
{
public class PasswordProvider : IPasswordProvider
{
public string GetPassword(UsernameToken token)
{
return "opensezme";
}
}
}
WS-Security を使用する WebMethod の作成
ここまでで、Web サービスを作成して呼び出す準備が整いました。 作成する Web サービスは、単純な "Hello World" 形式の Web サービスです。ただし、応答をカスタマイズできるようにクラスの SoapContext にアクセスする点を除きます。 WebMethod のコードを以下に示します。
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using Microsoft.Web.Services.Security;
using Microsoft.Web.Services;
namespace WSE_Security
{
[WebService]
public class Hello : System.Web.Services.WebService
{
[WebMethod]
public string PersonalHello()
{
string response = "";
// SOAP 要求のみを受け取ります。
SoapContext requestContext = HttpSoapContext.RequestContext;
if (requestContext == null)
{
throw new ApplicationException("Non-SOAP request.");
}
// UsernameToken に関して Tokens コレクション内の
// すべてのトークンを調べます。
foreach (SecurityToken tok in requestContext.Security.Tokens)
{
if (tok is UsernameToken)
{
response += "Hello " + ((UsernameToken)tok).Username;
}
}
return response;
}
}
}
WebMethod で WSE により何らかの操作を行う前に、プロジェクト内で Microsoft.Web.Services.dll への参照を追加する必要がありました。 その後、Microsoft.Web.Services と Microsoft.Web.Services.Security のコードに 2 つの using 句を追加しました。 これにより、PersonalHello WebMethod を作成できるようになりました。
最初に、PersonalHello 関数によって、静的メンバ HttpSoapContext.RequestContext を使用して、要求の SoapContext オブジェクトがフェッチされます。 これで、WS-Security ヘッダーのすべての情報にアクセスできるようになります。 コンテキストが存在しない場合は、Web メソッドからエラーが返されます。コンテキストが存在する場合は、要求と共に送信されたすべてのセキュリティ トークンを列挙して、そのいずれかが UsernameToken であるかどうかを確認します。 UsernameToken を検出した場合は、ユーザー名に基づいた挨拶を含む応答文字列を構築します。
WS-Security を使用するクライアントの作成
クライアント側で WSE での WS-Security の使用方法を確認するために、ボタンをクリックすると Web サービスを呼び出す単純な Windows フォーム アプリケーションを作成しました。 Web サービス クラスでこの操作を行った際、Microsoft.Web.Services.dll への参照を追加して、サーバー コードで追加したのと同じ using ステートメントを含めました。
クライアント側では、WSE によって System.Web.Services.Protocols.SoapHttpClientProtocol クラスから継承する Microsoft.Web.Services.WebServicesClientProtocol クラスが提供されます。 Visual Studio .NET 内から [Web 参照の追加] オプションを選択するとき、または WSDL.exe ユーティリティを使用して WSDL に基づくクライアント コードを作成するときに、SoapHttpClientProtocol クラスが使用されます。 Visual Studio .NET の [Web 参照の追加] オプション、または WSDL.exe ユーティリティを使用して、クライアントのプロキシ クラスを生成できます。その後、生成したプロキシ クラスを SoapHttpClientProtocol から継承するクラスから WebServicesClientProtocol から継承するクラスに変更できます。 これで、プロキシ クラスに、送受信する WS-Security ヘッダーへのアクセスに使用できる RequestSoapContext および ResponseSoapContext のプロパティが作成されます。 [Web 参照の追加] オプションを使用する場合は、Web 参照ディレクトリに、生成したプロキシ クラスのコードがあります。 C# プロジェクトでは、ホスト名に一致する名前の WSDL が存在するディレクトリに、Reference.cs という名前のファイルがあります。 MicrosoftR Visual BasicR .NET プロジェクトでは、そのファイル名は Reference.vb です。次のクラス宣言を変更しました。
public class Service1 :
System.Web.Services.Protocols.SoapHttpClientProtocol {
変更後のクラス宣言は、以下のとおりです。
public class Service1 :
Microsoft.Web.Services.WebServicesClientProtocol {
Visual Studio .NET の [Web 参照の追加] オプションを使用する場合、生成したコードが配置されるファイルに加えた変更に注意する必要があります。 [Web 参照の更新] オプションを選択すると、Visual Studio .NET でコードが再生成され、変更が上書きされます。
要求に UsernameToken を作成するために、クライアント コードを次のようにしました。
localhost.Hello proxy = new localhost.Hello();
proxy.Url = endpointInput.Text;
UsernameToken TextToken
= new UsernameToken(usernameInput.Text,
passwordInput.Text,
PasswordOption.SendHashed);
proxy.RequestSoapContext.Security.Tokens.Add(TextToken);
string result;
try
{
result = proxy.PersonalHello();
}
catch (Exception ex)
{
result = ex.Message;
}
MessageBox.Show(result);
このコードと通常の Web サービスを呼び出すコードの唯一の相違点は、UsernameToken オブジェクトを作成して、そのオブジェクトを要求の Tokens コレクションに追加したことです。 UsernameToken オブジェクトのコンストラクタは、ユーザー名、パスワード、および PasswordOption という 3 つのパラメータを受け取ります。 この例では、ハッシュされたパスワードを送信しています。 このコードから生成された簡略化された SOAP 要求は、以下のとおりです。 Security ヘッダーが UsernameToken 子要素を含む要求に含まれていることに気付きます。この子要素には、ユーザー名、ハッシュされたパスワード、この特定の要求を識別するために使用できる任意の Nonce、および作成時刻が含まれています。 完全なメッセージは、この資料の最後の「参考資料」セクションに記載しています。
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
...
<wsse:Security
soap:mustUnderstand="1"
xmlns:wsse="https://schemas.xmlsoap.org/ws/2002/07/secext">
<wsse:UsernameToken
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility"
wsu:Id="SecurityToken-58564463-5bdc-4a6b-a7fb-94a0d7357a20">
<wsse:Username>Joe</wsse:Username>
<wsse:Password Type="wsse:PasswordDigest">
gpBDXjx79eutcXdtlULIlcrSiRs=
</wsse:Password>
<wsse:Nonce>
h52sI9pKV0BVRPUolQC7Cg==
</wsse:Nonce>
<wsu:Created>2002-11-04T19:16:50Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<PersonalHello xmlns="http://tempuri.org/" />
</soap:Body>
</soap:Envelope>
これで、WSE SOAP 拡張機能によって一般的な形式の要求が検証される Web サービスにメッセージを送信できるようになり、パスワード プロバイダから取得したパスワードに対して、パスワードのハッシュが確認されます。すべての処理が正常に行われると、要求により、Web メソッドにハッシュが作成されます。 Web メソッドは、設定された SoapContext で受信要求を受け取り、Security ヘッダーの UsernameToken を検索し、指定した名前に基づいて応答文字列を構築します。 応答は、通常の WebMethod 応答として戻り、クライアント アプリケーションは返された文字列を表示します。 最初の WS-Security アプリケーションが正常に実行されました!
サーバーでの UsernameToken の検証
WSE では、Security ヘッダーの構文が検証され、パスワード プロバイダから取得したパスワードに対してパスワードのハッシュが確認されますが、要求で新たな検証を実行する必要がある場合があります。 たとえば、複数の UsernameToken 要素が要求に含まれていることがあります。 WS-Security には、さまざまな目的で使用する要求に多数のトークンを含めるためのサポートが用意されています。 クライアント コードによって UsernameToken が Tokens "コレクション" に追加されたという事実は、複数のトークンが 1 つのメッセージに含まれる可能性があるという明確な警告になります。 実際に、クライアント コードを変更して 2 つ目の UsernameToken を作成し、それをコレクションに追加すると、現在のコードから次のような文字列が返されるようになります。
Hello Bob.Hello Alice.
UsernameToken にハッシュされたパスワードが含まれていることを検証して、1 つの UsernameToken を含む受信要求だけを受け取るように、Web メソッドのコードを変更しました。 変更後のコードは、次のようになります。
[WebMethod]
public string PersonalHello()
{
SoapContext requestContext = HttpSoapContext.RequestContext;
if (requestContext == null)
{
throw new ApplicationException("Non-SOAP request.");
}
// 1 つのセキュリティ トークンを含む要求のみを許可します。
if (requestContext.Security.Tokens.Count == 1)
{
foreach (SecurityToken tok
in requestContext.Security.Tokens)
{
// UsernameToken のみを受け取ります。
if (tok is UsernameToken)
{
UsernameToken UserToken = (UsernameToken)tok;
// ハッシュされたパスワードを含む
// UsernameToken のみを受け取ります。
if (UserToken.PasswordOption
== PasswordOption.SendHashed)
{
return "Hello " + UserToken.Username;
}
else
{
throw new SoapException(
"Invalid UsernameToken password type.",
SoapException.ClientFaultCode);
}
}
else
{
throw new SoapException(
"UsernameToken security token required.",
SoapException.ClientFaultCode);
}
}
}
else
{
throw new SoapException(
"Request must have exactly one security token.",
SoapException.ClientFaultCode);
}
return null;
}
他人の UsernameToken 要素を使用する悪意のあるユーザーの問題については既に触れました。 この問題は、一種のリプレイ攻撃であり、この問題から身を守るためには、ここで示すコードよりかなり多くのコードが必要になります。 ここでは、よりセキュリティで保護された別の選択肢を見てみましょう。
WS-Security トークンとしての X.509 証明書の送信
WS-Security には UsernameTokens だけでなく、独自の専用の形式を持ついくつかの種類の既知のトークンを保持するために、BinarySecurityToken 要素も定義されています。 仕様では、X.509 v3 証明書および Kerberos v5 チケットの BinarySecurityToken 型が定義されています。 WSE では、X.509 証明書がサポートされているので、これらの証明書は多くの場合 UsernameToken と同じように処理されることがわかります。
証明書ストア
証明書を要求に含める前に、証明書がどこにあるかを把握する必要があります。 従来の MicrosoftR Windows ソフトウェア開発では、証明書が証明書ストアと呼ばれる集中管理された場所に保持されていました。 各ユーザーは、電子メール メッセージにデジタル署名を行ったり、SSL 経由で Web サーバーでの認証を行ったりするために使用する証明書のプライベート ストアを持っています。 証明書自体は、デジタル署名によって公開キーを配布する手段なので、秘密ではありません。 重要なことは、証明書の公開キーに対応する秘密キーが、使用する証明書の所有者のセキュリティで保護されたキー ストアにあることです。 これにより、ユーザーは、秘密キーを使用してエンティティにデジタル署名を行うことができるようになるので、誰でも証明書の公開キーを使用して、そのユーザーの署名を検証できます。
.NET Framework の最初の製品版には、X.509 証明書を処理するためのクラスがありましたが、Windows の証明書ストアにアクセスするためのクラスはありませんでした。 ただし、WSE には、Windows の証明書ストアを開き、容易に管理できるこの場所から証明書を使用できるようにするクラスが用意されています。 次のコードでは、Microsoft.Web.Services.Security.X509 クラスを使用して、ユーザーの個人的な証明書ストアのすべての証明書に共通する名前が listBox に設定されます。
...
using Microsoft.Web.Services;
using Microsoft.Web.Services.Security;
using Microsoft.Web.Services.Security.X509;
...
private X509CertificateStore store;
...
private void Form1_Load(object sender, System.EventArgs e)
{
store = X509CertificateStore.CurrentUserStore(
X509CertificateStore.MyStore);
store.OpenRead();
foreach(X509Certificate cert in store.Certificates)
{
listBox1.Items.Add(cert.GetName());
}
}
証明書ストアから X.509 証明書を選択するための完全な機能を持つダイアログの例については、WSE サンプル コード ディレクトリの X509CertificateStoreDialog.cs を参照してください。
SOAP メッセージへの X.509 証明書トークンの追加
個人の証明書を検出できるようになったので、その証明書を SOAP 要求に追加することは、以前使用した UsernameToken の追加によく似ています。 次のコードでは、先に設定した listBox の選択内容に基づいて、既に開かれた証明書ストアから証明書が取得され、証明書から作成されたバイナリ トークンが Tokens コレクションに追加されます。
X509Certificate cert =
(X509Certificate)store.Certificates[listBox1.SelectedIndex];
proxy.RequestSoapContext.Security.Tokens.Add(
new X509SecurityToken(cert));
サーバー側の証明書へのアクセスも、UsernameToken へのアクセスによく似ています。 前述の Web メソッドのロジックの修正版を以下に示します。このロジックでは、証明書の共通名を使用してカスタマイズした応答が構築されます。
[WebMethod]
public string PersonalHello()
{
// SOAP 要求のみを受け取ります。
SoapContext requestContext = HttpSoapContext.RequestContext;
if (requestContext == null)
{
throw new ApplicationException("Non-SOAP request.");
}
// 1 つのセキュリティ トークンを含む要求のみを許可します。
if (requestContext.Security.Tokens.Count == 1)
{
foreach (SecurityToken tok in requestContext.Security.Tokens)
{
// X.509 証明書のみを受け取ります。
if (tok is X509SecurityToken)
{
X509SecurityToken certToken = (X509SecurityToken)tok;
return "Hello " + certToken.Certificate.GetName();
}
else
{
throw new SoapException(
"X.509 security token required.",
SoapException.ClientFaultCode);
}
}
}
else
{
throw new SoapException(
"Request must have exactly one security token.",
SoapException.ClientFaultCode);
}
return null;
}
デジタル署名
単に、要求と共に X.509 証明書を送信することは、何を認証するにしてもあまり適切な方法ではありません。 証明書は公開された情報とみなされるので、誰でも他人の証明書を要求に含めることができます。 認証に証明書を使用するためのメカニズムは、証明書の公開キーに対応する秘密キーを使用して、一部のエンティティに署名するという前述の考え方に基づいています。 SOAP メッセージを送信する場合、Bob が秘密キーを使用して SOAP 本文の要素のデジタル署名を作成すると、対応する証明書を署名と一緒に要求のヘッダーに含めることができます。その結果、メッセージのすべての受信者が、要求が Bob から来たこと、およびその要求が Bob の署名後に変更されていないことを検証できるようになります。 証明書を要求に含めることは必須ではありませんが、署名を検証しようとする人にとっては便利です。
WSE では、デジタル署名の作成が非常に簡単な方法でサポートされています。 Tokens コレクションだけでなく、Signature 要素などのさまざまな WS-Security 要素を追加できるようにする SoapContext.Security.Elements コレクションがあります。 ここでは、デジタル証明書を含んでいた以前のクライアント コードからビルドするので、要求に署名するために同じ証明書を使用することになります。 この処理を行うコードを以下に示します。
X509Certificate cert =
(X509Certificate)store.Certificates[listBox1.SelectedIndex];
X509SecurityToken certToken = new X509SecurityToken(cert);
proxy.RequestSoapContext.Security.Tokens.Add(certToken);
proxy.RequestSoapContext.Security.Elements.Add(
new Signature(certToken));
以前行ったように、以前開いた証明書ストアから証明書を取り出して、その証明書を Tokens コレクションに追加します。 その後、Signature コンストラクタのパラメータとして X509SecurityToken を使用する新しい Signature オブジェクトを作成し、その Signature オブジェクトを Elements コレクションに追加します。 このコードから生成した簡略化された SOAP メッセージは、以下のとおりです。 完全な SOAP メッセージは、この資料の最後の「参考資料」セクションに記載しています。
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<wsrp:path
soap:actor="https://schemas.xmlsoap.org/soap/actor/next"
soap:mustUnderstand="1"
xmlns:wsrp="https://schemas.xmlsoap.org/rp">
<wsrp:action
wsu:Id="Id-b856ae70-7a1b-4895-a05c-5f6596ca4429"
...
</wsrp:path>
...
<wsse:Security
soap:mustUnderstand="1"
xmlns:wsse="https://schemas.xmlsoap.org/ws/2002/07/secext">
<wsse:BinarySecurityToken
ValueType="wsse:X509v3"
EncodingType="wsse:Base64Binary"
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility"
wsu:Id="SecurityToken-f6f96b4b-23c5-421e-92ff-f1050d531e82">
MIIGkzCCBXugAwIBAgIK . . . 39Vmjd20Lw==
</wsse:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#Id-24cc3660-6f1a-41fe-a949-71d7ed9fc636">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>
/8iL3OP9mfzuixI/ilkhHMbatV0=
</DigestValue>
</Reference>
<Reference URI="#Id-b856ae70-7a1b-4895-a05c-5f6596ca4429">
<Transforms>
...
</SignedInfo>
<SignatureValue>
ZY4MhHzBYz+CBdAz1LhAFjy6QxQoKJoA7l2eG45QV0hDIJrmXwLEGrPnpX+uPan5+MS6hm+oL
/sGTbKJ/DJMp/t5ZyqY1qvngGQLcYXRy538zemwFfeGN5R2wmOoUSeCBUqprQVUbnkz+qlVp/
5f7t7VGW2Ee55Q3ol+ApVoFQE=
</SignatureValue>
<KeyInfo>
<wsse:SecurityTokenReference>
<wsse:Reference
URI="#SecurityToken-f6f96b4b-23c5-421e-92ff-f1050d531e82" />
</wsse:SecurityTokenReference>
</KeyInfo>
</Signature>
</wsse:Security>
</soap:Header>
<soap:Body
wsu:Id="Id-24cc3660-6f1a-41fe-a949-71d7ed9fc636"
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility">
<PersonalHello xmlns="http://tempuri.org/" />
</soap:Body>
</soap:Envelope>
この要求に含まれるさまざまなヘッダー要素を完全に理解するには、WS-Routing 仕様や Web Services Security 補遺仕様などの他の一部の仕様を参照してください。 この資料では、要求に署名するために、WSE で WS-Security がどのように使用されるかについて詳しく見ていきます。
このメッセージに関して最初に注意することは、Security ヘッダー内部に BinarySecurityToken 要素があることです。 この要素の属性は、X.509 v3 証明書が Base 64 エンコードされていることを示します。 これは、すぐ前の 2 つの例で、Tokens コレクションに追加した X509SecurityToken です。 さらに、この要素は、メッセージに含める必要はありませんが、署名の確認を容易にします。
次に、Security ヘッダーに Signature 要素が存在するようになったことに注意します。 Signature には、3 つの子要素、SignedInfo 要素、SignatureValue 要素、および KeyInfo 要素があります。 SignedInfo 要素では、この要求に署名されている正確なデータ、このデータを正規化する方法、および署名を計算するために使用するアルゴリズムを定義します。 理解する必要がある重要な項目は、このメッセージ内の実際に署名が行われるものを定義する参照要素の一覧です。 説明を簡単にするために多くの参照要素を削除しましたが、記載した 2 つの参照要素では、それぞれの要素に関連する URI 属性があることに気付きます。 その URI 属性は、署名に含まれる要素の ID 属性に対応しています。 たとえば、最初の参照は "#Id-24cc3660-" で始まる URI を示します。 メッセージの中を検索すると、この URI がメッセージ内の Body 要素の ID に対応していることがわかります。 したがって、Body はこの要求の署名された情報の一部です。 2 番目の参照は、WS-Routing 仕様の一部である Path ヘッダーの action 要素を参照します。 WSE には、Path ヘッダーと Timestamp ヘッダーが自動的に含められ、これらのヘッダーに追加された多くの情報がデジタル署名に含まれます。 WSE によって SignedInfo ヘッダーで参照される要素の一覧を以下に示します。この一覧は、デジタル署名に含まれます。
/soap:Envelope/soap:Header/wsrp:path/wsrp:action
/soap:Envelope/soap:Header/wsrp:path/wsrp:to
/soap:Envelope/soap:Header/wsrp:path/wsrp:from
/soap:Envelope/soap:Header/wsrp:path/wsrp:id
/soap:Envelope/soap:Header/wsu:Timestamp/wsu:Created
/soap:Envelope/soap:Header/wsu:Timestamp/wsu:Expires
/soap:Envelope/soap:Body
path および Timestamp 要素全体ではなく、path および Timestamp ヘッダーのサブ要素に署名が行われます。これは、WS-Routing シナリオでは、メッセージが SOAP レベルのルーターを経由して伝達される場合に、仲介者が path ヘッダーおよび Timestamp ヘッダー内部に要素を追加することが予想されるためです。 そのため、これらのヘッダー内で何かを変更すると、デジタル署名が無効になります。
この要求の Signature 要素に関して最後に注意することは、KeyInfo が証明書を含む BinarySecurityToken を逆参照していることです。 これは、この要求の署名に使用したキーの由来を示しています。 当然のことながら、証明書の場合、デジタル署名を作成するために使用した証明書の公開キーに対応しているのが秘密キーです。 そのため、公開キーを使用して、対応する秘密キーを知っている人のみからデータが送信される可能性があることを検証できます。
デジタル署名の検証
WSE SOAP 拡張機能では、デジタル署名の構文が検証されますが、有効な署名がメッセージに存在することを知っているだけでは、メッセージが特定の個人から送信されたものであることを認識するには十分ではありません。 WS-Security および XML Digital Signature 仕様 ((Extensible Markup Language) XML-Signature Syntax and Processing (英語)) は、XML 内部に署名を含める方法において非常に柔軟性があります。 たとえば、メッセージが Alice から送信されたものであると予想し、メッセージには Alice の署名があると考えるとします。ただし、その署名では、path ヘッダーのルーティング情報に署名されることしか考えられません。 独自の検証を行わない限り、最も関心がある可能性がある項目 (通常、メッセージの本文) が、Alice から送信されたものであるということを保証できません。
次のコードでは、要求を含む Signature の存在が調べられ、SignatureOptions プロパティを使用してメッセージの本文に署名が行われているかどうかが検証されます。その後、メッセージの送信者とそのメッセージがそのままの状態で到着したことがはっきりとわかると、要求の署名に使用する証明書に基づいて、個別の挨拶が構成されます。
[WebMethod]
public string PersonalHello()
{
// SOAP 要求のみを受け取ります。
SoapContext requestContext = HttpSoapContext.RequestContext;
if (requestContext == null)
{
throw new ApplicationException("Non-SOAP request.");
}
// Elements コレクションの署名を調べます。
foreach (Object elem in requestContext.Security.Elements)
{
if (elem is Signature)
{
Signature sign = (Signature)elem;
// 署名が要求の本文に行われていることを検証します。
if (sign != null
&& (sign.SignatureOptions &
SignatureOptions.IncludeSoapBody) != 0)
{
// 署名に使用するトークンの
// 種類を判断します。
if (sign.SecurityToken is UsernameToken)
{
return "Hello " +
((UsernameToken)sign.SecurityToken).Username;
}
else if (sign.SecurityToken is X509SecurityToken)
{
return "Hello " +
((X509SecurityToken)sign.SecurityToken)
.Certificate.GetName();
}
}
}
}
// 適切な署名が見つかりません。
throw new SoapException("No valid signature found",
SoapException.ClientFaultCode);
}
メッセージの一部への署名
上記の例では、SOAP メッセージに WSE の既定の署名を作成しました。 その署名には、SOAP 本文、およびほとんどの場合に意味がある SOAP ヘッダーのさまざまな部分が含まれています。 既定のメッセージ部分または SOAP 本文全体に署名を行わない可能性があるシナリオがあります。 たとえば、仲介者が、署名を無効にする SOAP 本文の部分を変更することは意味があります。 したがって、署名された部分と署名されていない部分を厳密に制御する場合は、この操作が適切です。
デジタル署名がカバーしているメッセージ部分を判断するためのオプションが 2 つあります。 上記の例では、Signature クラスの SignatureOptions プロパティを使用して、署名に SOAP 本文が含まれているかどうかを判断しました。 また、SignatureOptions プロパティを使用して、署名がカバーしている SOAP メッセージの部分を指定することもできます。 以下のフラグを任意に組み合わせて使用できます。これらのフラグは、SignatureOptions 列挙子で定義されています。
IncludePath
IncludePathAction
IncludePathFrom
IncludePathId
IncludePathTo
IncludeSoapBody
IncludeTimestamp
IncludeTimestampCreated
IncludeTimestampExpires
SignatureOptions に設定できる値がもう一つあります。それは IncludeNone です。 この値は、上記のどのオプションも署名に含める必要がないことを示します。 それでは、IncludeNone SignatureOption を指定する予定の場合、わざわざメッセージに署名を行うのはなぜでしょう? この答えは、送信するメッセージ部分を判断するための他のメカニズムを使用して解明します。
WS-Security には、署名されている部分と署名されていない部分を判断するための選択肢が用意されています。 その選択肢の 1 つは、メッセージでの変換を指定し、結果に基づいて署名を作成することです。 ただし、WSE では、この選択肢がサポートされていません。 他の可能性のある選択肢は、署名する要素の Id 属性を指定して、その要素への参照を Signature 要素の SignedInfo 部分に含めることです。 この選択肢は、WSE によって、SignatureOptions 列挙子の一部であるさまざまなオプションで署名が作成される方法です。 ただし、WSE では、Id 属性を作成して、署名の SignedInfo 部分への参照を手動で追加することもできます。 これにより、署名を行う本文の任意の特定部分だけでなく、カスタム SOAP ヘッダーに署名を行うこともできるようになります。 必要な操作は、Id 属性が設定されていて、その属性の href が参照に含まれていることを確認することだけです。 SOAP 本文の部分のみに署名を行う例について見ていきます。
ASP.NET Web サービスでは、.NET Framework の XmlSerializer を使用して、サービスが送受信する SOAP メッセージが作成および解析されます。 XmlSerializer の動作方法を制御する属性を使用して、コンテンツにフラグを設定することで、シリアル化が行われる方法に関する多くの詳細を正確に制御できます。 そのため、Id 属性を XML 部分に追加し、その属性の値を設定して、その値を署名の参照に追加することは困難な操作ではありません。
この操作の実行方法を説明するために、発注書を含む複合型を返す ASP.NET WebMethod を作成しました。 クライアントで、発注書が私から送信されたものであることを正しく認識されるように、XML の実際の発注書要素のみにデジタル署名を行います。まず、WebMethod から返される複雑なクラスを作成しました。 このクラスでは、発注書要素は実際の発注書番号ですが、属性としてシリアル化される ID プロパティも存在します。 このプロパティは、署名で参照される ID になります。 WS-Security 仕様の名前空間と一致するこの属性の正しい名前空間を含めることは重要なので、XmlAttribute 属性の Namespace パラメータを使用して、そのパラメータを http://schemas.xmlsoap.org.ws/2002/07/utility に設定します。
public class PONum
{
[XmlAttribute("Id",
Namespace="https://schemas.xmlsoap.org/ws/2002/07/utility")]
public string ID;
[XmlText]
public string PONumber;
}
WebMethod では、多くの処理が行われる必要があります。 まず、一意 ID として使用する GUID を生成する必要があります。WebMethod では、上記のクラスのインスタンスが作成され、ID プロパティが設定されます。その結果、WebMethod は、GUID 値を含む適切な形式になります。 また、WebMethod では、実際の発注書番号の値が設定されます。この特定の例では、実際の発注書番号は常に同じです。 次に、WebMethod では、発注書番号に署名するために使用する X.509 証明書に基づいてセキュリティ トークンが作成されます。
ASP.NET からの証明書を使用するには、WebMethod が、ASP.NET の作業プロセス内からアクセスできる証明書ストアに格納されている必要があります。 同様に、証明書の公開キーに対応する秘密キーは、ASP.NET からアクセスできるキー ストアに格納されている必要があります。この例では、コンピュータの証明書ストアに格納されている証明書を使用しました。 この処理を機能させるために、コンピュータの物理的なキー ストアに対するアクセス許可を "ASPNET ユーザー" (ASP.NET ワーカー プロセスが動作するのに基づくアカウント) に与えます。 これにより、コンピュータのキー ストアに対する他のアプリケーションのアクセスが許可される可能性があるので、このように変更する際は、十分注意する必要があります。 コンピュータ上の証明書とキーの格納およびアクセスに関する問題の詳細については、「Managing X.509 Certificates」(英語) というタイトルの WSE ドキュメントに記載されている情報を参照してください。
セキュリティ トークンが作成されると、コンストラクタにトークンを渡す Signature のインスタンスを作成できるようになります。 すぐに、SignatureOptions プロパティを SignatureOptions.IncludeNone に設定して、通常含まれているメッセージのすべての既定部分が含まれないようにします。 これで、Signature オブジェクトの AddReference メソッドを呼び出すことができるようになりました。このメソッドでは、ID プロパティに以前設定したのと同じ文字列を指定します。ただし、ここでは、ローカル参照であることを示すために、'#' 記号を先頭に追加しました。 Web メソッドのコードを以下に示します。
[WebMethod]
public PONum GetPONumber()
{
SoapContext responseContext
= HttpSoapContext.ResponseContext;
PONum PO = new PONum();
Guid referenceID = Guid.NewGuid();
PO.ID = "Id:" + referenceID.ToString();
PO.PONumber = "PO10025";
X509CertificateStore store =
X509CertificateStore.LocalMachineStore(
X509CertificateStore.MyStore);
store.OpenRead();
X509Certificate cert
= store.FindCertificateBySubjectName(
"CN = mattpo.redmond.corp.microsoft.com")[0];
X509SecurityToken token = new X509SecurityToken(cert);
responseContext.Security.Tokens.Add(token);
Signature sig = new Signature(token);
sig.SignatureOptions = SignatureOptions.IncludeNone;
sig.AddReference(new Reference("#" + PO.ID));
responseContext.Security.Elements.Add(sig);
return PO;
}
最後の手順として、クライアントで、返された発注書の署名が適切であるかどうかを確認します。 この操作を行うには、返された ID が署名の参照に存在し、その署名に使用された証明書が期待している証明書であることを確認します。 Web サービスを呼び出し、使用されている証明書を検証するためのコードを以下に示します。
localhost.Service1Wse proxy = new localhost.Service1Wse();
localhost.PONum po = proxy.GetPONumber();
foreach (Object element in proxy.ResponseSoapContext.Security.Elements)
{
if (element is Signature)
{
Signature sig = (Signature)element;
foreach (Reference reference in sig.SignedInfo.References)
{
if (reference.Uri == "#" + po.Id)
{
X509Certificate signatureCert
= ((X509SecurityToken)
sig.SecurityToken).Certificate;
if (signatureCert.Equals(poCert))
MessageBox.Show("It's signed!");
}
}
}
}
まとめ
この資料では、WSE を使用して UsernameToken 認証を行う方法を調べ、X.509 認証を使用して SOAP メッセージの部分にデジタル署名を行うことで、Web Services Enhancements for Microsoft .NET で WS-Security 仕様の機能を垣間見ることができる方法について説明しました。 WSE により、SOAP メッセージに含まれている種類の WS-Security サポートを調査および確認できます。 この資料では説明していませんが、暗号化のさまざまな形式を使用する機能など、WSE には他の WS-Security 機能が数多く備わっています。 WS-Security によって、Web サービスの機能がどのように拡張されるかについての感触をつかむために、WSE のインストール、一部のコードの記述、および作成されたメッセージと「参考資料」セクションに記載されているメッセージの調査を行うことをお勧めします。
参考資料
ハッシュされた UsernameToken を含む SOAP メッセージ
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<wsrp:path
soap:actor="https://schemas.xmlsoap.org/soap/actor/next"
soap:mustUnderstand="1"
xmlns:wsrp="https://schemas.xmlsoap.org/rp">
<wsrp:action>
http://tempuri.org/PersonalHello
</wsrp:action>
<wsrp:to>
https://localhost:8080/wsdk-security/verifiedhello.asmx
</wsrp:to>
<wsrp:id>
uuid:14f61c50-586c-42ec-8286-c5c9fa8bfce1
</wsrp:id>
</wsrp:path>
<wsu:Timestamp
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility">
<wsu:Created>2002-11-04T19:16:50Z</wsu:Created>
<wsu:Expires>2002-11-04T19:21:50Z</wsu:Expires>
</wsu:Timestamp>
<wsse:Security
soap:mustUnderstand="1"
xmlns:wsse="https://schemas.xmlsoap.org/ws/2002/07/secext">
<wsse:UsernameToken
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility"
wsu:Id="SecurityToken-58564463-5bdc-4a6b-a7fb-94a0d7357a20">
<wsse:Username>Joe</wsse:Username>
<wsse:Password Type="wsse:PasswordDigest">
gpBDXjx79eutcXdtlULIlcrSiRs=
</wsse:Password>
<wsse:Nonce>
h52sI9pKV0BVRPUolQC7Cg==
</wsse:Nonce>
<wsu:Created>2002-11-04T19:16:50Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<PersonalHello xmlns="http://tempuri.org/" />
</soap:Body>
</soap:Envelope>
メッセージにデジタル署名を行うために使用する X.509 証明書トークンを含む SOAP メッセージ
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<wsrp:path
soap:actor="https://schemas.xmlsoap.org/soap/actor/next"
soap:mustUnderstand="1"
xmlns:wsrp="https://schemas.xmlsoap.org/rp">
<wsrp:action
wsu:Id="Id-b856ae70-7a1b-4895-a05c-5f6596ca4429"
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility">
http://tempuri.org/PersonalHello
</wsrp:action>
<wsrp:to
wsu:Id="Id-e3fa8752-df7d-4a16-a883-f98800bf24f7"
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility">
https://localhost:8080/wsdk-security/signedhello.asmx
</wsrp:to>
<wsrp:id
wsu:Id="Id-64bdd986-1d54-4176-bbc3-c38255fcfedf"
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility">
uuid:15810d11-91d9-44cb-b3c8-016ff9d93b78
</wsrp:id>
</wsrp:path>
<wsu:Timestamp
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility">
<wsu:Created
wsu:Id="Id-134651fa-1791-4679-abf7-cbbc100fbeb9">
2002-11-05T23:35:59Z
</wsu:Created>
<wsu:Expires
wsu:Id="Id-9c106440-3955-4f62-903d-c30c6c9e8d27">
2002-11-05T23:40:59Z
</wsu:Expires>
</wsu:Timestamp>
<wsse:Security
soap:mustUnderstand="1"
xmlns:wsse="https://schemas.xmlsoap.org/ws/2002/07/secext">
<wsse:BinarySecurityToken
ValueType="wsse:X509v3"
EncodingType="wsse:Base64Binary"
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility"
wsu:Id="SecurityToken-f6f96b4b-23c5-421e-92ff-f1050d531e82">
MIIGkzCCBXugAwIBAgIK . . . 39Vmjd20Lw==
</wsse:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#Id-24cc3660-6f1a-41fe-a949-71d7ed9fc636">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>
/8iL3OP9mfzuixI/ilkhHMbatV0=
</DigestValue>
</Reference>
<Reference URI="#Id-b856ae70-7a1b-4895-a05c-5f6596ca4429">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>
rH3m6W9zglaAMMzP7sD9yvwzEdA=
</DigestValue>
</Reference>
<Reference URI="#Id-e3fa8752-df7d-4a16-a883-f98800bf24f7">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>
tzgO0kc7HSomAxAokgHw3/hkL+E=
</DigestValue>
</Reference>
<Reference URI="#Id-64bdd986-1d54-4176-bbc3-c38255fcfedf">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>
Ls05OCaMfqqaIFj2vV8a/aHQQBo=
</DigestValue>
</Reference>
<Reference URI="#Id-134651fa-1791-4679-abf7-cbbc100fbeb9">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>
VqOIop+9EeJXGIwB3TqAqXgiKUI=
</DigestValue>
</Reference>
<Reference URI="#Id-9c106440-3955-4f62-903d-c30c6c9e8d27">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>
3d9WMNwuLIrRfSyaWCsl63d+wDA=
</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
ZY4MhHzBYz+CBdAz1LhAFjy6QxQoKJoA7l2eG45QV0hDIJrmXwLEGrPnpX+uPan5+MS6hm+oL/sGTbKJ/DJMp/t5ZyqY1qvngGQLcYXRy538zemwFfeGN5R2wmOoUSeCBUqprQVUbnkz+qlVp/5f7t7VGW2Ee55Q3ol+ApVoFQE=
</SignatureValue>
<KeyInfo>
<wsse:SecurityTokenReference>
<wsse:Reference
URI="#SecurityToken-f6f96b4b-23c5-421e-92ff-f1050d531e82" />
</wsse:SecurityTokenReference>
</KeyInfo>
</Signature>
</wsse:Security>
</soap:Header>
<soap:Body
wsu:Id="Id-24cc3660-6f1a-41fe-a949-71d7ed9fc636"
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility">
<PersonalHello xmlns="http://tempuri.org/" />
</soap:Body>
</soap:Envelope>
メッセージ部分にデジタル署名を行うために使用する X.509 証明書トークンを含む SOAP メッセージ
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<wsu:Timestamp
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility">
<wsu:Created>2002-11-07T21:51:51Z</wsu:Created>
<wsu:Expires>2002-11-07T21:56:51Z</wsu:Expires>
</wsu:Timestamp>
<wsse:Security
soap:mustUnderstand="1"
xmlns:wsse="https://schemas.xmlsoap.org/ws/2002/07/secext">
<wsse:BinarySecurityToken
ValueType="wsse:X509v3"
EncodingType="wsse:Base64Binary"
xmlns:wsu="https://schemas.xmlsoap.org/ws/2002/07/utility"
wsu:Id="SecurityToken-547309ee-532f-40ce-a370-a64be85e977e">
MIIHRjC ... HVUjaoy
</wsse:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#Id:ce249a29-aa9a-427a-b0c4-830cdc7f481a">
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>7yhtDGpxNtkFGT9+1vWHI7sQL1c=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
tNj18ILnxAyc/3AoNCRb+ZBcYcIp5KCKTFLCTNhAzuokk5m1S8FOBvFYTUdy1qCU2i655/KCcIzZ7lzLSqY57iaoWgdpQBAWvEEhxkSNuGGl/qoknNhc4B2SN24t1AniB4UwNFvo2u6rHiBr3nSfAv0rSPuGa32c3Ri8LRcqZ5M=
</SignatureValue>
<KeyInfo>
<wsse:SecurityTokenReference>
<wsse:Reference
URI=
"#SecurityToken-547309ee-532f-40ce-a370-a64be85e977e" />
</wsse:SecurityTokenReference>
</KeyInfo>
</Signature>
</wsse:Security>
</soap:Header>
<soap:Body>
<GetPONumberResponse xmlns="http://tempuri.org/">
<GetPONumberResult
d4p1:Id="Id:ce249a29-aa9a-427a-b0c4-830cdc7f481a"
xmlns:d4p1="https://schemas.xmlsoap.org/ws/2002/07/utility">
PO10025
</GetPONumberResult>
</GetPONumberResponse>
</soap:Body>
</soap:Envelope>