このサンプルでは、Windows Communication Foundation (WCF) アプリケーションにカスタム トークン実装を追加する方法を示します。 この例では、 CreditCardToken
を使用して、クライアント クレジット カードに関する情報をサービスに安全に渡します。 トークンは WS-Security メッセージ ヘッダーで渡され、対称セキュリティ バインド要素と共にメッセージ本文やその他のメッセージ ヘッダーを使用して署名および暗号化されます。 これは、組み込みのトークンが十分でない場合に便利です。 このサンプルでは、組み込みのトークンのいずれかを使用するのではなく、カスタム セキュリティ トークンをサービスに提供する方法を示します。 このサービスは、要求/応答通信パターンを定義するコントラクトを実装します。
注
このサンプルのセットアップ手順とビルド手順は、このトピックの最後にあります。
要約すると、このサンプルでは次の例を示します。
クライアントがカスタム セキュリティ トークンをサービスに渡す方法。
サービスがカスタム セキュリティ トークンを使用して検証する方法。
WCF サービス コードが、カスタム セキュリティ トークンを含む受信したセキュリティ トークンに関する情報を取得する方法。
サーバーの X.509 証明書を使用して、メッセージの暗号化と署名に使用される対称キーを保護する方法。
カスタム セキュリティ トークンを使用したクライアント認証
サービスは、 BindingHelper
クラスと EchoServiceHost
クラスを使用してプログラムによって作成された単一のエンドポイントを公開します。 エンドポイントは、アドレス、バインディング、およびコントラクトで構成されます。 バインドは、 SymmetricSecurityBindingElement
と HttpTransportBindingElement
を使用してカスタム バインドで構成されます。 このサンプルでは、サービスの X.509 証明書を使用して転送中に対称キーを保護し、署名された暗号化されたセキュリティ トークンとして WS-Security メッセージ ヘッダーにカスタム CreditCardToken
を渡すSymmetricSecurityBindingElement
を設定します。 この動作では、クライアント認証に使用するサービス資格情報と、サービス X.509 証明書に関する情報も指定します。
public static class BindingHelper
{
public static Binding CreateCreditCardBinding()
{
var httpTransport = new HttpTransportBindingElement();
// The message security binding element will be configured to require a credit card.
// The token that is encrypted with the service's certificate.
var messageSecurity = new SymmetricSecurityBindingElement();
messageSecurity.EndpointSupportingTokenParameters.SignedEncrypted.Add(new CreditCardTokenParameters());
X509SecurityTokenParameters x509ProtectionParameters = new X509SecurityTokenParameters();
x509ProtectionParameters.InclusionMode = SecurityTokenInclusionMode.Never;
messageSecurity.ProtectionTokenParameters = x509ProtectionParameters;
return new CustomBinding(messageSecurity, httpTransport);
}
}
メッセージでクレジット カード トークンを使用するために、サンプルではカスタム サービス資格情報を使用してこの機能を提供します。 サービス資格情報クラスは、 CreditCardServiceCredentials
クラスにあり、 EchoServiceHost.InitializeRuntime
メソッドのサービス ホストの動作コレクションに追加されます。
class EchoServiceHost : ServiceHost
{
string creditCardFile;
public EchoServiceHost(parameters Uri[] addresses)
: base(typeof(EchoService), addresses)
{
creditCardFile = ConfigurationManager.AppSettings["creditCardFile"];
if (string.IsNullOrEmpty(creditCardFile))
{
throw new ConfigurationErrorsException("creditCardFile not specified in service config");
}
creditCardFile = String.Format("{0}\\{1}", System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath, creditCardFile);
}
override protected void InitializeRuntime()
{
// Create a credit card service credentials and add it to the behaviors.
CreditCardServiceCredentials serviceCredentials = new CreditCardServiceCredentials(this.creditCardFile);
serviceCredentials.ServiceCertificate.SetCertificate("CN=localhost", StoreLocation.LocalMachine, StoreName.My);
this.Description.Behaviors.Remove((typeof(ServiceCredentials)));
this.Description.Behaviors.Add(serviceCredentials);
// Register a credit card binding for the endpoint.
Binding creditCardBinding = BindingHelper.CreateCreditCardBinding();
this.AddServiceEndpoint(typeof(IEchoService), creditCardBinding, string.Empty);
base.InitializeRuntime();
}
}
クライアント エンドポイントは、サービス エンドポイントと同様の方法で構成されます。 クライアントは、同じ BindingHelper
クラスを使用してバインディングを作成します。 セットアップの残りの部分は、 Client
クラスにあります。 また、クライアントは、適切なデータを持つ CreditCardClientCredentials
インスタンスをクライアント エンドポイント動作コレクションに追加することによって、CreditCardToken
に含まれる情報とセットアップ コードのサービス X.509 証明書に関する情報を設定します。 このサンプルでは、サービス証明書としてサブジェクト名が CN=localhost
に設定された X.509 証明書を使用します。
Binding creditCardBinding = BindingHelper.CreateCreditCardBinding();
var serviceAddress = new EndpointAddress("http://localhost/servicemodelsamples/service.svc");
// Create a client with given client endpoint configuration.
channelFactory = new ChannelFactory<IEchoService>(creditCardBinding, serviceAddress);
// Configure the credit card credentials on the channel factory.
var credentials =
new CreditCardClientCredentials(
new CreditCardInfo(creditCardNumber, issuer, expirationTime));
// Configure the service certificate on the credentials.
credentials.ServiceCertificate.SetDefaultCertificate(
"CN=localhost", StoreLocation.LocalMachine, StoreName.My);
// Replace ClientCredentials with CreditCardClientCredentials.
channelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
channelFactory.Endpoint.Behaviors.Add(credentials);
client = channelFactory.CreateChannel();
Console.WriteLine($"Echo service returned: {client.Echo()}");
((IChannel)client).Close();
channelFactory.Close();
カスタム セキュリティ トークンの実装
WCF でカスタム セキュリティ トークンを有効にするには、カスタム セキュリティ トークンのオブジェクト表現を作成します。 このサンプルは、 CreditCardToken
クラスでこの表現を持っています。 オブジェクト表現は、関連するすべてのセキュリティ トークン情報を保持し、セキュリティ トークンに含まれるセキュリティ キーの一覧を提供します。 この場合、クレジット カードのセキュリティ トークンにはセキュリティ キーは含まれません。
次のセクションでは、カスタム トークンをネットワーク経由で送信し、WCF エンドポイントで使用できるようにするために実行する必要がある処理について説明します。
class CreditCardToken : SecurityToken
{
CreditCardInfo cardInfo;
DateTime effectiveTime = DateTime.UtcNow;
string id;
ReadOnlyCollection<SecurityKey> securityKeys;
public CreditCardToken(CreditCardInfo cardInfo) : this(cardInfo, Guid.NewGuid().ToString()) { }
public CreditCardToken(CreditCardInfo cardInfo, string id)
{
if (cardInfo == null)
throw new ArgumentNullException(nameof(cardInfo));
if (id == null)
throw new ArgumentNullException(nameof(id));
this.cardInfo = cardInfo;
this.id = id;
// The credit card token is not capable of any cryptography.
this.securityKeys = new ReadOnlyCollection<SecurityKey>(new List<SecurityKey>());
}
public CreditCardInfo CardInfo { get { return this.cardInfo; } }
public override ReadOnlyCollection<SecurityKey> SecurityKeys { get { return this.securityKeys; } }
public override DateTime ValidFrom { get { return this.effectiveTime; } }
public override DateTime ValidTo { get { return this.cardInfo.ExpirationDate; } }
public override string Id { get { return this.id; } }
}
メッセージとの間でカスタム クレジット カード トークンを取得する
WCF のセキュリティ トークン シリアライザーは、メッセージ内の XML からセキュリティ トークンのオブジェクト表現を作成し、セキュリティ トークンの XML 形式を作成する役割を担います。 また、セキュリティ トークンを指すキー識別子の読み取りと書き込みなどの他の機能も担当しますが、この例ではセキュリティ トークン関連の機能のみを使用します。 カスタム トークンを有効にするには、独自のセキュリティ トークン シリアライザーを実装する必要があります。 このサンプルでは、この目的のために CreditCardSecurityTokenSerializer
クラスを使用します。
サービスでは、カスタム シリアライザーはカスタム トークンの XML 形式を読み取り、そこからカスタム トークン オブジェクト表現を作成します。
クライアントでは、 CreditCardSecurityTokenSerializer
クラスは、セキュリティ トークン オブジェクト表現に含まれる情報を XML ライターに書き込みます。
public class CreditCardSecurityTokenSerializer : WSSecurityTokenSerializer
{
public CreditCardSecurityTokenSerializer(SecurityTokenVersion version) : base() { }
protected override bool CanReadTokenCore(XmlReader reader)
{
XmlDictionaryReader localReader = XmlDictionaryReader.CreateDictionaryReader(reader);
if (reader == null)
throw new ArgumentNullException(nameof(reader));
if (reader.IsStartElement(Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace))
return true;
return base.CanReadTokenCore(reader);
}
protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));
if (reader.IsStartElement(Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace))
{
string id = reader.GetAttribute(Constants.Id, Constants.WsUtilityNamespace);
reader.ReadStartElement();
// Read the credit card number.
string creditCardNumber = reader.ReadElementString(Constants.CreditCardNumberElementName, Constants.CreditCardTokenNamespace);
// Read the expiration date.
string expirationTimeString = reader.ReadElementString(Constants.CreditCardExpirationElementName, Constants.CreditCardTokenNamespace);
DateTime expirationTime = XmlConvert.ToDateTime(expirationTimeString, XmlDateTimeSerializationMode.Utc);
// Read the issuer of the credit card.
string creditCardIssuer = reader.ReadElementString(Constants.CreditCardIssuerElementName, Constants.CreditCardTokenNamespace);
reader.ReadEndElement();
var cardInfo = new CreditCardInfo(creditCardNumber, creditCardIssuer, expirationTime);
return new CreditCardToken(cardInfo, id);
}
else
{
return WSSecurityTokenSerializer.DefaultInstance.ReadToken(reader, tokenResolver);
}
}
protected override bool CanWriteTokenCore(SecurityToken token)
{
if (token is CreditCardToken)
return true;
return base.CanWriteTokenCore(token);
}
protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
{
if (writer == null)
throw new ArgumentNullException(nameof(writer));
if (token == null)
throw new ArgumentNullException(nameof(token));
CreditCardToken c = token as CreditCardToken;
if (c != null)
{
writer.WriteStartElement(Constants.CreditCardTokenPrefix, Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace);
writer.WriteAttributeString(Constants.WsUtilityPrefix, Constants.Id, Constants.WsUtilityNamespace, token.Id);
writer.WriteElementString(Constants.CreditCardNumberElementName, Constants.CreditCardTokenNamespace, c.CardInfo.CardNumber);
writer.WriteElementString(Constants.CreditCardExpirationElementName, Constants.CreditCardTokenNamespace, XmlConvert.ToString(c.CardInfo.ExpirationDate, XmlDateTimeSerializationMode.Utc));
writer.WriteElementString(Constants.CreditCardIssuerElementName, Constants.CreditCardTokenNamespace, c.CardInfo.CardIssuer);
writer.WriteEndElement();
writer.Flush();
}
else
{
base.WriteTokenCore(writer, token);
}
}
}
トークン プロバイダーとトークン認証子クラスの作成方法
クライアントとサービスの資格情報は、セキュリティ トークン マネージャー インスタンスを提供する役割を担います。 セキュリティ トークン マネージャー インスタンスは、トークン プロバイダー、トークン認証子、およびトークン シリアライザーを取得するために使用されます。
トークン プロバイダーは、クライアントまたはサービスの資格情報に含まれる情報に基づいて、トークンのオブジェクト表現を作成します。 トークン オブジェクト表現は、(前のセクションで説明した) トークン シリアライザーを使用してメッセージに書き込まれます。
トークン認証子は、メッセージに到着したトークンを検証します。 受信トークン オブジェクト表現は、トークン シリアライザーによって作成されます。 このオブジェクト表現は、検証のためにトークン認証子に渡されます。 トークンが正常に検証されると、トークン認証子は、トークンに含まれる情報を表す IAuthorizationPolicy
オブジェクトのコレクションを返します。 この情報は、承認の決定を実行し、アプリケーションの要求を提供するために、メッセージ処理中に後で使用されます。 この例では、クレジット カード トークン認証システムは、この目的のために CreditCardTokenAuthorizationPolicy
を使用します。
トークン シリアライザーは、ワイヤとの間でトークンのオブジェクト表現を取得する役割を担います。 これについては、前のセクションで説明します。
このサンプルでは、クライアント側でのみトークン プロバイダーを使用し、サービス上でのみトークン認証子を使用します。これは、クライアントからサービスへの方向でのみクレジット カード トークンを送信するためです。
クライアントの機能は、 CreditCardClientCredentials
、 CreditCardClientCredentialsSecurityTokenManager
、および CreditCardTokenProvider
クラスにあります。
サービスでは、この機能は、 CreditCardServiceCredentials
、 CreditCardServiceCredentialsSecurityTokenManager
、 CreditCardTokenAuthenticator
、および CreditCardTokenAuthorizationPolicy
クラスに存在します。
public class CreditCardClientCredentials : ClientCredentials
{
CreditCardInfo creditCardInfo;
public CreditCardClientCredentials(CreditCardInfo creditCardInfo)
: base()
{
if (creditCardInfo == null)
throw new ArgumentNullException(nameof(creditCardInfo));
this.creditCardInfo = creditCardInfo;
}
public CreditCardInfo CreditCardInfo
{
get { return this.creditCardInfo; }
}
protected override ClientCredentials CloneCore()
{
return new CreditCardClientCredentials(this.creditCardInfo);
}
public override SecurityTokenManager CreateSecurityTokenManager()
{
return new CreditCardClientCredentialsSecurityTokenManager(this);
}
}
public class CreditCardClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager
{
CreditCardClientCredentials creditCardClientCredentials;
public CreditCardClientCredentialsSecurityTokenManager(CreditCardClientCredentials creditCardClientCredentials)
: base (creditCardClientCredentials)
{
this.creditCardClientCredentials = creditCardClientCredentials;
}
public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
{
// Handle this token for Custom.
if (tokenRequirement.TokenType == Constants.CreditCardTokenType)
return new CreditCardTokenProvider(this.creditCardClientCredentials.CreditCardInfo);
// Return server cert.
else if (tokenRequirement is InitiatorServiceModelSecurityTokenRequirement)
{
if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
{
return new X509SecurityTokenProvider(creditCardClientCredentials.ServiceCertificate.DefaultCertificate);
}
}
return base.CreateSecurityTokenProvider(tokenRequirement);
}
public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
{
return new CreditCardSecurityTokenSerializer(version);
}
}
class CreditCardTokenProvider : SecurityTokenProvider
{
CreditCardInfo creditCardInfo;
public CreditCardTokenProvider(CreditCardInfo creditCardInfo) : base()
{
if (creditCardInfo == null)
throw new ArgumentNullException(nameof(creditCardInfo));
this.creditCardInfo = creditCardInfo;
}
protected override SecurityToken GetTokenCore(TimeSpan timeout)
{
SecurityToken result = new CreditCardToken(this.creditCardInfo);
return result;
}
}
public class CreditCardServiceCredentials : ServiceCredentials
{
string creditCardFile;
public CreditCardServiceCredentials(string creditCardFile)
: base()
{
if (creditCardFile == null)
throw new ArgumentNullException(nameof(creditCardFile));
this.creditCardFile = creditCardFile;
}
public string CreditCardDataFile
{
get { return this.creditCardFile; }
}
protected override ServiceCredentials CloneCore()
{
return new CreditCardServiceCredentials(this.creditCardFile);
}
public override SecurityTokenManager CreateSecurityTokenManager()
{
return new CreditCardServiceCredentialsSecurityTokenManager(this);
}
}
public class CreditCardServiceCredentialsSecurityTokenManager : ServiceCredentialsSecurityTokenManager
{
CreditCardServiceCredentials creditCardServiceCredentials;
public CreditCardServiceCredentialsSecurityTokenManager(CreditCardServiceCredentials creditCardServiceCredentials)
: base(creditCardServiceCredentials)
{
this.creditCardServiceCredentials = creditCardServiceCredentials;
}
public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
{
if (tokenRequirement.TokenType == Constants.CreditCardTokenType)
{
outOfBandTokenResolver = null;
return new CreditCardTokenAuthenticator(creditCardServiceCredentials.CreditCardDataFile);
}
return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
}
public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
{
return new CreditCardSecurityTokenSerializer(version);
}
}
class CreditCardTokenAuthenticator : SecurityTokenAuthenticator
{
string creditCardsFile;
public CreditCardTokenAuthenticator(string creditCardsFile)
{
this.creditCardsFile = creditCardsFile;
}
protected override bool CanValidateTokenCore(SecurityToken token)
{
return (token is CreditCardToken);
}
protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)
{
CreditCardToken creditCardToken = token as CreditCardToken;
if (creditCardToken.CardInfo.ExpirationDate < DateTime.UtcNow)
throw new SecurityTokenValidationException("The credit card has expired.");
if (!IsCardNumberAndExpirationValid(creditCardToken.CardInfo))
throw new SecurityTokenValidationException("Unknown or invalid credit card.");
// the credit card token has only 1 claim - the card number. The issuer for the claim is the
// credit card issuer
var cardIssuerClaimSet = new DefaultClaimSet(new Claim(ClaimTypes.Name, creditCardToken.CardInfo.CardIssuer, Rights.PossessProperty));
var cardClaimSet = new DefaultClaimSet(cardIssuerClaimSet, new Claim(Constants.CreditCardNumberClaim, creditCardToken.CardInfo.CardNumber, Rights.PossessProperty));
var policies = new List<IAuthorizationPolicy>(1);
policies.Add(new CreditCardTokenAuthorizationPolicy(cardClaimSet));
return policies.AsReadOnly();
}
/// <summary>
/// Helper method to check if a given credit card entry is present in the User DB
/// </summary>
private bool IsCardNumberAndExpirationValid(CreditCardInfo cardInfo)
{
try
{
using (var myStreamReader = new StreamReader(this.creditCardsFile))
{
string line = "";
while ((line = myStreamReader.ReadLine()) != null)
{
string[] splitEntry = line.Split('#');
if (splitEntry[0] == cardInfo.CardNumber)
{
string expirationDateString = splitEntry[1].Trim();
DateTime expirationDateOnFile = DateTime.Parse(expirationDateString, System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.AdjustToUniversal);
if (cardInfo.ExpirationDate == expirationDateOnFile)
{
string issuer = splitEntry[2];
return issuer.Equals(cardInfo.CardIssuer, StringComparison.InvariantCultureIgnoreCase);
}
else
{
return false;
}
}
}
return false;
}
}
catch (Exception e)
{
throw new Exception("BookStoreService: Error while retrieving credit card information from User DB " + e.ToString());
}
}
}
public class CreditCardTokenAuthorizationPolicy : IAuthorizationPolicy
{
string id;
ClaimSet issuer;
IEnumerable<ClaimSet> issuedClaimSets;
public CreditCardTokenAuthorizationPolicy(ClaimSet issuedClaims)
{
if (issuedClaims == null)
throw new ArgumentNullException(nameof(issuedClaims));
this.issuer = issuedClaims.Issuer;
this.issuedClaimSets = new ClaimSet[] { issuedClaims };
this.id = Guid.NewGuid().ToString();
}
public ClaimSet Issuer { get { return this.issuer; } }
public string Id { get { return this.id; } }
public bool Evaluate(EvaluationContext context, ref object state)
{
foreach (ClaimSet issuance in this.issuedClaimSets)
{
context.AddClaimSet(this, issuance);
}
return true;
}
}
呼び出し元の情報の表示
呼び出し元の情報を表示するには、次のサンプル コードに示すように ServiceSecurityContext.Current.AuthorizationContext.ClaimSets
を使用します。
ServiceSecurityContext.Current.AuthorizationContext.ClaimSets
には、現在の呼び出し元に関連付けられている承認要求が含まれています。 要求は、AuthorizationPolicies
コレクション内のCreditCardToken
クラスによって提供されます。
bool TryGetStringClaimValue(ClaimSet claimSet, string claimType, out string claimValue)
{
claimValue = null;
IEnumerable<Claim> matchingClaims = claimSet.FindClaims(claimType, Rights.PossessProperty);
if (matchingClaims == null)
return false;
IEnumerator<Claim> enumerator = matchingClaims.GetEnumerator();
enumerator.MoveNext();
claimValue = (enumerator.Current.Resource == null) ? null :
enumerator.Current.Resource.ToString();
return true;
}
string GetCallerCreditCardNumber()
{
foreach (ClaimSet claimSet in
ServiceSecurityContext.Current.AuthorizationContext.ClaimSets)
{
string creditCardNumber = null;
if (TryGetStringClaimValue(claimSet,
Constants.CreditCardNumberClaim, out creditCardNumber))
{
string issuer;
if (!TryGetStringClaimValue(claimSet.Issuer,
ClaimTypes.Name, out issuer))
{
issuer = "Unknown";
}
return $"Credit card '{creditCardNumber}' issued by '{issuer}'";
}
}
return "Credit card is not known";
}
サンプルを実行すると、操作要求と応答がクライアント コンソール ウィンドウに表示されます。 クライアント ウィンドウで Enter キーを押して、クライアントをシャットダウンします。
Batch ファイルのセットアップ
このサンプルに含まれる Setup.bat バッチ ファイルを使用すると、サーバー証明書ベースのセキュリティを必要とする IIS でホストされるアプリケーションを実行するように、関連する証明書を使用してサーバーを構成できます。 このバッチ ファイルは、コンピューター間で動作するように、またはホストされていないケースで動作するように変更する必要があります。
次に、適切な構成で実行するように変更できるように、バッチ ファイルのさまざまなセクションの概要を示します。
サーバー証明書の作成:
Setup.bat
バッチ ファイルの次の行は、使用するサーバー証明書を作成します。%SERVER_NAME%
変数は、サーバー名を指定します。 この変数を変更して、独自のサーバー名を指定します。 このバッチ ファイルの既定値は localhost です。%SERVER_NAME%
変数を変更する場合は、Client.csファイルとService.cs ファイルを確認し、localhost のすべてのインスタンスを、Setup.bat スクリプトで使用するサーバー名に置き換える必要があります。証明書は、
LocalMachine
ストアの場所の My (Personal) ストアに保存されます。 証明書は、IIS でホストされるサービスの LocalMachine ストアに格納されます。 セルフホステッド サービスの場合は、LocalMachine という文字列を CurrentUser に置き換えて、クライアント証明書を CurrentUser ストアの場所に格納するようにバッチ ファイルを変更する必要があります。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 が発行した証明書など) にルート化された証明書が既にある場合、クライアント証明書ストアにサーバー証明書を設定するこの手順は必要ありません。
echo ************ echo copying server cert to client's TrustedPeople store echo ************ certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
IIS でホストされるサービスから証明書の秘密キーへのアクセスを有効にするには、IIS でホストされるプロセスが実行されているユーザー アカウントに秘密キーに対する適切なアクセス許可が付与されている必要があります。 これは、Setup.bat スクリプトの最後の手順で行います。
echo ************ echo setting privileges on server certificates echo ************ for /F "delims=" %%i in ('"%ProgramFiles%\ServiceModelSampleTools\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
注
Setup.bat バッチ ファイルは、Visual Studio コマンド プロンプトから実行するように設計されています。 Visual Studio コマンド プロンプト内で設定された PATH 環境変数は、Setup.bat スクリプトに必要な実行可能ファイルを含むディレクトリを指します。
サンプルを設定してビルドするには
Windows Communication Foundation サンプル のOne-Time セットアップ手順を実行していることを確認します。
ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。
同じコンピューターでサンプルを実行するには
- 管理者特権で Visual Studio コマンド プロンプト ウィンドウを開き、サンプルのインストール フォルダーから Setup.bat 実行します。 これにより、サンプルの実行に必要なすべての証明書がインストールされます。パスに、Makecert.exe が配置されているフォルダーが含まれていることを確認します。
注
サンプルが完了したら、Cleanup.bat を実行して証明書を削除してください。 他のセキュリティ サンプルでは、同じ証明書が使用されます。
client\bin ディレクトリから Client.exe を起動します。 クライアント アクティビティがクライアント コンソール アプリケーションに表示されます。
クライアントとサービスが通信できない場合は、「WCF サンプルのトラブルシューティングのヒント」を参照してください。
コンピューター間でサンプルを実行するには
サービス バイナリのサービス コンピューター上にディレクトリを作成します。
サービス プログラム ファイルをサービス コンピューター上のサービス ディレクトリにコピーします。 CreditCardFile.txtをコピーすることを忘れないでください。それ以外の場合、クレジット カード認証システムは、クライアントから送信されたクレジット カード情報を検証できません。 また、Setup.bat ファイルと Cleanup.bat ファイルをサービス コンピューターにコピーします。
コンピューターの完全修飾ドメイン名を含むサブジェクト名を持つサーバー証明書が必要です。
%SERVER_NAME%
変数をサービスがホストされているコンピューターの完全修飾名に変更する場合は、Setup.bat を使用して作成できます。 Setup.bat ファイルは、管理者特権で開かれた Visual Studio の開発者コマンド プロンプトで実行する必要があることに注意してください。サーバー証明書をクライアント上の CurrentUser-TrustedPeople ストアにコピーします。 これは、サーバー証明書が信頼された発行者によって発行されていない場合にのみ行う必要があります。
EchoServiceHost.cs ファイルで、証明書のサブジェクト名の値を変更して、localhost ではなく完全修飾コンピューター名を指定します。
クライアント プログラム ファイルを、言語固有のフォルダーの下にある \client\bin\ フォルダーからクライアント コンピューターにコピーします。
Client.cs ファイルで、サービスの新しいアドレスと一致するようにエンドポイントのアドレス値を変更します。
Client.cs ファイルで、サービス X.509 証明書のサブジェクト名を、localhost ではなくリモート ホストの完全修飾コンピューター名と一致するように変更します。
クライアント コンピューターで、コマンド プロンプト ウィンドウから Client.exe を起動します。
クライアントとサービスが通信できない場合は、「WCF サンプルのトラブルシューティングのヒント」を参照してください。
サンプルの実行後にクリーンアップするには
- サンプルの実行が完了したら、samples フォルダーで Cleanup.bat を実行します。