Token niestandardowy
W tym przykładzie pokazano, jak dodać niestandardową implementację tokenu do aplikacji Windows Communication Foundation (WCF). W tym przykładzie użyto metody , CreditCardToken
aby bezpiecznie przekazać informacje o kartach kredytowych klienta do usługi. Token jest przekazywany w nagłówku komunikatu WS-Security i jest podpisany i szyfrowany przy użyciu symetrycznego elementu powiązania zabezpieczeń wraz z treścią komunikatu i innymi nagłówkami komunikatów. Jest to przydatne w przypadkach, gdy wbudowane tokeny nie są wystarczające. W tym przykładzie pokazano, jak udostępnić niestandardowy token zabezpieczający usłudze zamiast używać jednego z wbudowanych tokenów. Usługa implementuje kontrakt, który definiuje wzorzec komunikacji typu żądanie-odpowiedź.
Uwaga
Procedura instalacji i instrukcje kompilacji dla tego przykładu znajdują się na końcu tego tematu.
Podsumowując, w tym przykładzie przedstawiono następujące kwestie:
Jak klient może przekazać niestandardowy token zabezpieczający do usługi.
Jak usługa może korzystać z niestandardowego tokenu zabezpieczającego i weryfikować go.
Jak kod usługi WCF może uzyskać informacje o odebranych tokenach zabezpieczających, w tym niestandardowy token zabezpieczający.
Sposób użycia certyfikatu X.509 serwera do ochrony klucza symetrycznego używanego do szyfrowania i podpisu komunikatów.
Uwierzytelnianie klienta przy użyciu niestandardowego tokenu zabezpieczającego
Usługa uwidacznia pojedynczy punkt końcowy, który jest programowo tworzony przy użyciu BindingHelper
klas i EchoServiceHost
. Punkt końcowy składa się z adresu, powiązania i kontraktu. Powiązanie jest konfigurowane przy użyciu powiązania niestandardowego przy użyciu metod SymmetricSecurityBindingElement
i HttpTransportBindingElement
. W tym przykładzie ustawiono SymmetricSecurityBindingElement
certyfikat X.509 usługi w celu ochrony klucza symetrycznego podczas transmisji i przekazania niestandardowego CreditCardToken
nagłówka komunikatu WS-Security jako podpisanego i zaszyfrowanego tokenu zabezpieczającego. Zachowanie określa poświadczenia usługi, które mają być używane do uwierzytelniania klienta, a także informacje o certyfikacie X.509 usługi.
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);
}
}
Aby korzystać z tokenu karty kredytowej w komunikacie, w przykładzie użyto poświadczeń usługi niestandardowej w celu zapewnienia tej funkcji. Klasa poświadczeń usługi znajduje się w CreditCardServiceCredentials
klasie i jest dodawana do kolekcji zachowań hosta usługi w metodzie 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();
}
}
Punkt końcowy klienta jest konfigurowany w podobny sposób jak punkt końcowy usługi. Klient używa tej samej BindingHelper
klasy do utworzenia powiązania. Pozostała część konfiguracji znajduje się w Client
klasie . Klient ustawia również informacje, które mają być zawarte w CreditCardToken
certyfikacie X.509 usługi i w kodzie konfiguracji, dodając CreditCardClientCredentials
wystąpienie z odpowiednimi danymi do kolekcji zachowań punktów końcowych klienta. W przykładzie użyto certyfikatu X.509 z nazwą podmiotu ustawioną jako CN=localhost
certyfikat usługi.
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();
Niestandardowa implementacja tokenu zabezpieczającego
Aby włączyć niestandardowy token zabezpieczający w programie WCF, utwórz reprezentację obiektu niestandardowego tokenu zabezpieczającego. Przykład zawiera tę reprezentację CreditCardToken
w klasie . Reprezentacja obiektu jest odpowiedzialna za przechowywanie wszystkich odpowiednich informacji o tokenie zabezpieczającym i podanie listy kluczy zabezpieczeń zawartych w tokenie zabezpieczającym. W takim przypadku token zabezpieczający karty kredytowej nie zawiera żadnego klucza zabezpieczeń.
W następnej sekcji opisano, co należy zrobić, aby umożliwić przesyłanie tokenu niestandardowego za pośrednictwem przewodu i używane przez punkt końcowy programu 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; } }
}
Pobieranie niestandardowego tokenu karty kredytowej do i z komunikatu
Serializatory tokenów zabezpieczających w programie WCF są odpowiedzialne za tworzenie reprezentacji obiektu tokenów zabezpieczających na podstawie kodu XML w komunikacie i tworzenie formularza XML tokenów zabezpieczających. Są one również odpowiedzialne za inne funkcje, takie jak odczytywanie i zapisywanie identyfikatorów kluczy wskazujących tokeny zabezpieczające, ale w tym przykładzie są używane tylko funkcje związane z tokenami zabezpieczającymi. Aby włączyć token niestandardowy, należy zaimplementować własny serializator tokenu zabezpieczającego. W tym przykładzie użyto CreditCardSecurityTokenSerializer
klasy w tym celu.
W usłudze niestandardowy serializator odczytuje formularz XML tokenu niestandardowego i tworzy na jego podstawie reprezentację niestandardowego obiektu tokenu.
Na kliencie CreditCardSecurityTokenSerializer
klasa zapisuje informacje zawarte w reprezentacji obiektu tokenu zabezpieczającego w składniku zapisywania 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);
}
}
}
Jak są tworzone klasy dostawcy tokenów i wystawcy uwierzytelniania tokenów
Poświadczenia klienta i usługi są odpowiedzialne za dostarczanie wystąpienia menedżera tokenów zabezpieczających. Wystąpienie menedżera tokenów zabezpieczających służy do pobierania dostawców tokenów, wystawców uwierzytelnień tokenów i serializatorów tokenów.
Dostawca tokenu tworzy reprezentację obiektu tokenu na podstawie informacji zawartych w poświadczeniach klienta lub usługi. Reprezentacja obiektu tokenu jest następnie zapisywana w komunikacie przy użyciu serializatora tokenu (omówionego w poprzedniej sekcji).
Wystawca uwierzytelniania tokenów weryfikuje tokeny, które docierają do komunikatu. Reprezentacja obiektu tokenu przychodzącego jest tworzona przez serializator tokenu. Ta reprezentacja obiektu jest następnie przekazywana do wystawcy uwierzytelniającego token w celu weryfikacji. Po pomyślnym zweryfikowaniu tokenu wystawca uwierzytelnienia tokenu IAuthorizationPolicy
zwraca kolekcję obiektów reprezentujących informacje zawarte w tokenie. Te informacje są używane później podczas przetwarzania komunikatów w celu podejmowania decyzji dotyczących autoryzacji i dostarczania oświadczeń dla aplikacji. W tym przykładzie wystawca uwierzytelniający token karty kredytowej używa CreditCardTokenAuthorizationPolicy
do tego celu.
Serializator tokenu jest odpowiedzialny za uzyskanie reprezentacji obiektu tokenu do i z przewodu. Omówiono to w poprzedniej sekcji.
W tym przykładzie używamy dostawcy tokenów tylko na kliencie i wystawcy uwierzytelniającego token tylko w usłudze, ponieważ chcemy przesyłać token karty kredytowej tylko w kierunku klient-usługa.
Funkcjonalność klienta znajduje się w klasach CreditCardClientCredentials
i CreditCardClientCredentialsSecurityTokenManager
CreditCardTokenProvider
.
W usłudze funkcje znajdują się w klasach CreditCardServiceCredentials
, CreditCardServiceCredentialsSecurityTokenManager
CreditCardTokenAuthenticator
i 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;
}
}
Wyświetlanie informacji o obiektach wywołujących
Aby wyświetlić informacje elementu wywołującego, użyj elementu ServiceSecurityContext.Current.AuthorizationContext.ClaimSets
, jak pokazano w poniższym przykładowym kodzie. Zawiera ServiceSecurityContext.Current.AuthorizationContext.ClaimSets
oświadczenia autoryzacji skojarzone z bieżącym obiektem wywołującym. Oświadczenia są dostarczane przez klasę CreditCardToken
w swojej AuthorizationPolicies
kolekcji.
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";
}
Po uruchomieniu przykładu żądania operacji i odpowiedzi są wyświetlane w oknie konsoli klienta. Naciśnij klawisz ENTER w oknie klienta, aby zamknąć klienta.
Konfigurowanie pliku wsadowego
Plik wsadowy Setup.bat dołączony do tego przykładu umożliwia skonfigurowanie serwera z odpowiednimi certyfikatami w celu uruchomienia aplikacji hostowanej przez usługi IIS, która wymaga zabezpieczeń opartych na certyfikatach serwera. Ten plik wsadowy należy zmodyfikować tak, aby działał na komputerach lub działać w przypadku innym niż hostowany.
Poniżej przedstawiono krótkie omówienie różnych sekcji plików wsadowych, dzięki czemu można je zmodyfikować w celu uruchomienia w odpowiedniej konfiguracji.
Tworzenie certyfikatu serwera:
Następujące wiersze z pliku wsadowego
Setup.bat
tworzą certyfikat serwera do użycia. Zmienna%SERVER_NAME%
określa nazwę serwera. Zmień tę zmienną, aby określić własną nazwę serwera. Wartość domyślna w tym pliku wsadowym to localhost. Jeśli zmienisz zmienną%SERVER_NAME%
, musisz przejść przez pliki Client.cs i Service.cs i zastąpić wszystkie wystąpienia hosta lokalnego nazwą serwera używaną w skrypcie Setup.bat.Certyfikat jest przechowywany w magazynie Moje (osobiste) w
LocalMachine
lokalizacji magazynu. Certyfikat jest przechowywany w magazynie LocalMachine dla usług hostowanych przez usługi IIS. W przypadku usług hostowanych samodzielnie należy zmodyfikować plik wsadowy w celu przechowywania certyfikatu klienta w lokalizacji magazynu CurrentUser, zastępując ciąg LocalMachine bieżącymużytkownikiem.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
Instalowanie certyfikatu serwera w magazynie zaufanych certyfikatów klienta:
Następujące wiersze w pliku wsadowym Setup.bat skopiuj certyfikat serwera do magazynu zaufanych osób klienta. Ten krok jest wymagany, ponieważ certyfikaty generowane przez Makecert.exe nie są niejawnie zaufane przez system kliencki. Jeśli masz już certyfikat, który jest root w zaufanym certyfikacie głównym klienta — na przykład certyfikat wystawiony przez firmę Microsoft — ten krok wypełniania magazynu certyfikatów klienta przy użyciu certyfikatu serwera nie jest wymagany.
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
Aby umożliwić dostęp do klucza prywatnego certyfikatu z usługi hostowanej przez usługi IIS, konto użytkownika, w ramach którego działa proces hostowany przez usługi IIS, musi mieć odpowiednie uprawnienia dla klucza prywatnego. Jest to realizowane przez ostatnie kroki skryptu 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
Uwaga
Plik wsadowy Setup.bat jest przeznaczony do uruchamiania z poziomu wiersza polecenia programu Visual Studio. Zmienna środowiskowa PATH ustawiona w wierszu polecenia programu Visual Studio wskazuje katalog zawierający pliki wykonywalne wymagane przez skrypt Setup.bat.
Aby skonfigurować i skompilować przykład
Upewnij się, że wykonano procedurę instalacji jednorazowej dla przykładów programu Windows Communication Foundation.
Aby skompilować rozwiązanie, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).
Aby uruchomić przykład na tym samym komputerze
- Otwórz okno wiersza polecenia programu Visual Studio z uprawnieniami administratora i uruchom Setup.bat z przykładowego folderu instalacji. Spowoduje to zainstalowanie wszystkich certyfikatów wymaganych do uruchomienia przykładu. Upewnij się, że ścieżka zawiera folder, w którym znajduje się Makecert.exe.
Uwaga
Pamiętaj, aby usunąć certyfikaty, uruchamiając Cleanup.bat po zakończeniu pracy z przykładem. Inne przykłady zabezpieczeń używają tych samych certyfikatów.
Uruchom Client.exe z katalogu client\bin. Działanie klienta jest wyświetlane w aplikacji konsolowej klienta.
Jeśli klient i usługa nie mogą się komunikować, zobacz Rozwiązywanie problemów Wskazówki dla przykładów programu WCF.
Aby uruchomić przykład na komputerze
Utwórz katalog na komputerze usługi dla plików binarnych usługi.
Skopiuj pliki programu usługi do katalogu usługi na komputerze usługi. Nie zapomnij skopiować CreditCardFile.txt; w przeciwnym razie wystawca karty kredytowej nie może zweryfikować informacji o karcie kredytowej wysłanej od klienta. Skopiuj również pliki Setup.bat i Cleanup.bat na komputer usługi.
Musisz mieć certyfikat serwera o nazwie podmiotu, który zawiera w pełni kwalifikowaną nazwę domeny komputera. Można go utworzyć przy użyciu Setup.bat, jeśli zmienisz zmienną
%SERVER_NAME%
na w pełni kwalifikowaną nazwę komputera, na którym jest hostowana usługa. Należy pamiętać, że plik Setup.bat musi być uruchamiany w wierszu polecenia dewelopera dla programu Visual Studio otwartym z uprawnieniami administratora.Skopiuj certyfikat serwera do magazynu CurrentUser-Trusted Osoby na kliencie. Należy to zrobić tylko wtedy, gdy certyfikat serwera nie został wystawiony przez zaufanego wystawcę.
W pliku EchoServiceHost.cs zmień wartość nazwy podmiotu certyfikatu, aby określić w pełni kwalifikowaną nazwę komputera zamiast hosta lokalnego.
Skopiuj pliki programu klienckiego z folderu \client\bin\ w folderze specyficznym dla języka na komputer kliencki.
W pliku Client.cs zmień wartość adresu punktu końcowego, aby odpowiadała nowemu adresowi usługi.
W pliku Client.cs zmień nazwę podmiotu certyfikatu X.509 usługi, aby był zgodny z w pełni kwalifikowaną nazwą komputera hosta zdalnego zamiast hosta lokalnego.
Na komputerze klienckim uruchom Client.exe w oknie wiersza polecenia.
Jeśli klient i usługa nie mogą się komunikować, zobacz Rozwiązywanie problemów Wskazówki dla przykładów programu WCF.
Aby wyczyścić po próbce
- Uruchom Cleanup.bat w folderze samples po zakończeniu uruchamiania przykładu.