Dela via


Anpassad token

Det här exemplet visar hur du lägger till en anpassad tokenimplementering i ett WCF-program (Windows Communication Foundation). I exemplet används en för att på ett CreditCardToken säkert sätt skicka information om klientkreditkort till tjänsten. Token skickas i meddelanderubriken WS-Security och signeras och krypteras med hjälp av det symmetriska säkerhetsbindningselementet tillsammans med meddelandetexten och andra meddelandehuvuden. Detta är användbart i fall där de inbyggda token inte räcker till. Det här exemplet visar hur du tillhandahåller en anpassad säkerhetstoken till en tjänst i stället för att använda en av de inbyggda token. Tjänsten implementerar ett kontrakt som definierar ett kommunikationsmönster för begäran-svar.

Kommentar

Installationsproceduren och bygginstruktionerna för det här exemplet finns i slutet av det här avsnittet.

Sammanfattningsvis visar det här exemplet följande:

  • Hur en klient kan skicka en anpassad säkerhetstoken till en tjänst.

  • Hur tjänsten kan använda och verifiera en anpassad säkerhetstoken.

  • Hur WCF-tjänstkoden kan hämta information om mottagna säkerhetstoken, inklusive den anpassade säkerhetstoken.

  • Hur serverns X.509-certifikat används för att skydda den symmetriska nyckel som används för meddelandekryptering och signatur.

Klientautentisering med en anpassad säkerhetstoken

Tjänsten exponerar en enskild slutpunkt som skapas programmatiskt med hjälp av BindingHelper och EchoServiceHost klasser. Slutpunkten består av en adress, en bindning och ett kontrakt. Bindningen konfigureras med en anpassad bindning med hjälp av SymmetricSecurityBindingElement och HttpTransportBindingElement. Det här exemplet anger SymmetricSecurityBindingElement att du ska använda en tjänsts X.509-certifikat för att skydda den symmetriska nyckeln under överföringen och skicka en anpassad CreditCardToken i ett WS-Security-meddelandehuvud som en signerad och krypterad säkerhetstoken. Beteendet anger de tjänstautentiseringsuppgifter som ska användas för klientautentisering och även information om X.509-tjänstcertifikatet.

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);
    }
}

Om du vill använda en kreditkortstoken i meddelandet använder exemplet autentiseringsuppgifter för anpassad tjänst för att tillhandahålla den här funktionen. Klassen för tjänstautentiseringsuppgifter finns i CreditCardServiceCredentials klassen och läggs till i beteendesamlingarna för tjänstvärden i EchoServiceHost.InitializeRuntime -metoden.

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();
    }
}

Klientslutpunkten konfigureras på ett liknande sätt som tjänstslutpunkten. Klienten använder samma BindingHelper klass för att skapa en bindning. Resten av installationen finns i Client klassen . Klienten anger också information som ska finnas i CreditCardToken och information om X.509-tjänstcertifikatet i installationskoden genom att lägga till en CreditCardClientCredentials instans med rätt data i samlingen med klientslutpunktsbeteenden. Exemplet använder X.509-certifikat med ämnesnamnet inställt på CN=localhost som tjänstcertifikat.

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();

Implementering av anpassad säkerhetstoken

Om du vill aktivera en anpassad säkerhetstoken i WCF skapar du en objektrepresentation av den anpassade säkerhetstoken. Exemplet har den här representationen CreditCardToken i klassen. Objektrepresentationen ansvarar för att lagra all relevant säkerhetstokeninformation och ange en lista över säkerhetsnycklar som finns i säkerhetstoken. I det här fallet innehåller kreditkortssäkerhetstoken ingen säkerhetsnyckel.

I nästa avsnitt beskrivs vad som måste göras för att en anpassad token ska kunna överföras via kabeln och användas av en WCF-slutpunkt.

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; } }
}

Hämta den anpassade kreditkortstoken till och från meddelandet

Serialiserare för säkerhetstoken i WCF ansvarar för att skapa en objektrepresentation av säkerhetstoken från XML i meddelandet och skapa en XML-form av säkerhetstoken. De ansvarar också för andra funktioner som att läsa och skriva nyckelidentifierare som pekar på säkerhetstoken, men i det här exemplet används endast funktioner relaterade till säkerhetstoken. Om du vill aktivera en anpassad token måste du implementera din egen säkerhetstokens serialiserare. Det här exemplet använder CreditCardSecurityTokenSerializer klassen för det här ändamålet.

I tjänsten läser den anpassade serialiseraren XML-formen för den anpassade token och skapar den anpassade tokenobjektrepresentationen från den.

På klienten CreditCardSecurityTokenSerializer skriver klassen informationen i objektrepresentationen för säkerhetstoken till XML-skrivaren.

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);
        }
    }
}

Så här skapas tokenprovider- och tokenautentiseringsklasser

Klient- och tjänstautentiseringsuppgifter ansvarar för att tillhandahålla instansen för säkerhetstokenhanteraren. Säkerhetstokenhanterarens instans används för att hämta tokenprovidrar, tokenautentiseringsprogram och token-serialiserare.

Tokenprovidern skapar en objektrepresentation av token baserat på informationen i klient- eller tjänstautentiseringsuppgifterna. Tokenobjektrepresentationen skrivs sedan till meddelandet med hjälp av token-serialiseraren (beskrivs i föregående avsnitt).

Tokenautentiseringen verifierar token som tas emot i meddelandet. Den inkommande tokenobjektrepresentationen skapas av token-serialiseraren. Den här objektrepresentationen skickas sedan till tokenautentiseringsutentiseraren för validering. När token har verifierats returnerar tokenautentiseringen en samling IAuthorizationPolicy objekt som representerar informationen i token. Den här informationen används senare under meddelandebearbetningen för att utföra auktoriseringsbeslut och för att tillhandahålla anspråk för programmet. I det här exemplet använder CreditCardTokenAuthorizationPolicy autentiseringen av kreditkortstoken för detta ändamål.

Token-serialiseraren ansvarar för att hämta objektrepresentationen av token till och från kabeln. Detta beskrivs i föregående avsnitt.

I det här exemplet använder vi bara en tokenprovider på klienten och en tokenautentiseringsutentare endast på tjänsten, eftersom vi endast vill överföra en kreditkortstoken i klient-till-tjänst-riktningen.

Funktionerna på klienten finns i klasserna CreditCardClientCredentials, CreditCardClientCredentialsSecurityTokenManager och CreditCardTokenProvider .

I tjänsten finns funktionerna i klasserna CreditCardServiceCredentials, CreditCardServiceCredentialsSecurityTokenManagerCreditCardTokenAuthenticator och 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;
        }
    }

Visa uppringarens information

Om du vill visa uppringarens information använder du det ServiceSecurityContext.Current.AuthorizationContext.ClaimSets som visas i följande exempelkod. Innehåller ServiceSecurityContext.Current.AuthorizationContext.ClaimSets auktoriseringsanspråk som är associerade med den aktuella anroparen. Anspråken tillhandahålls av CreditCardToken klassen i samlingen AuthorizationPolicies .

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";
}

När du kör exemplet visas åtgärdsbegäranden och svar i klientkonsolfönstret. Tryck på RETUR i klientfönstret för att stänga av klienten.

Konfigurera Batch-fil

Med Setup.bat batchfil som ingår i det här exemplet kan du konfigurera servern med relevanta certifikat för att köra det IIS-värdbaserade programmet som kräver servercertifikatbaserad säkerhet. Den här batchfilen måste ändras för att fungera mellan datorer eller för att fungera i ett fall som inte är värdbaserat.

Följande ger en kort översikt över de olika avsnitten i batchfilerna så att de kan ändras så att de körs i lämplig konfiguration.

  • Skapa servercertifikatet:

    Följande rader från Setup.bat batchfilen skapar det servercertifikat som ska användas. Variabeln %SERVER_NAME% anger servernamnet. Ändra den här variabeln för att ange ditt eget servernamn. Standardvärdet i den här batchfilen är localhost. Om du ändrar variabeln %SERVER_NAME% måste du gå igenom Client.cs och Service.cs filer och ersätta alla instanser av localhost med det servernamn som du använder i skriptet Setup.bat.

    Certifikatet lagras i arkivet My (Personal) under lagringsplatsen LocalMachine . Certifikatet lagras i LocalMachine-arkivet för IIS-värdbaserade tjänster. För tjänster med egen värd bör du ändra batchfilen så att klientcertifikatet lagras på lagringsplatsen CurrentUser genom att ersätta strängen LocalMachine med 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
    
  • Installera servercertifikatet i klientens betrodda certifikatarkiv:

    Följande rader i Setup.bat batchfil kopierar servercertifikatet till klientarkivet för betrodda personer. Det här steget krävs eftersom certifikat som genereras av Makecert.exe inte är implicit betrodda av klientsystemet. Om du redan har ett certifikat som är rotat i ett klientbetrott rotcertifikat, till exempel ett Microsoft-utfärdat certifikat, krävs inte det här steget för att fylla i klientcertifikatarkivet med servercertifikatet.

    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
    
  • Om du vill aktivera åtkomst till certifikatets privata nyckel från den IIS-värdbaserade tjänsten måste användarkontot som IIS-värdbaserade processen körs under beviljas lämpliga behörigheter för den privata nyckeln. Detta görs genom de sista stegen i skriptet 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
    

Kommentar

Den Setup.bat batchfilen är utformad för att köras från en Visual Studio-kommandotolk. Variabeln PATH-miljö som angetts i Visual Studio-kommandotolken pekar på katalogen som innehåller körbara filer som krävs av Setup.bat skriptet.

Så här konfigurerar och skapar du exemplet

  1. Kontrollera att du har utfört engångsinstallationsproceduren för Windows Communication Foundation-exempel.

  2. Skapa lösningen genom att följa anvisningarna i Skapa Windows Communication Foundation-exempel.

Så här kör du exemplet på samma dator

  1. Öppna ett Visual Studio-kommandotolkfönster med administratörsbehörighet och kör Setup.bat från exempelinstallationsmappen. Detta installerar alla certifikat som krävs för att köra exemplet. Kontrollera att sökvägen innehåller mappen där Makecert.exe finns.

Kommentar

Se till att ta bort certifikaten genom att köra Cleanup.bat när du är klar med exemplet. Andra säkerhetsexempel använder samma certifikat.

  1. Starta Client.exe från katalogen client\bin. Klientaktiviteten visas i klientkonsolprogrammet.

  2. Om klienten och tjänsten inte kan kommunicera kan du läsa Felsökningstips för WCF-exempel.

Så här kör du exemplet över datorn

  1. Skapa en katalog på tjänstdatorn för tjänstens binärfiler.

  2. Kopiera tjänstprogramfilerna till tjänstkatalogen på tjänstdatorn. Glöm inte att kopiera CreditCardFile.txt; Annars kan kreditkortsautentiseringen inte verifiera kreditkortsinformation som skickas från klienten. Kopiera även Setup.bat- och Cleanup.bat-filerna till tjänstdatorn.

  3. Du måste ha ett servercertifikat med ämnesnamnet som innehåller datorns fullständigt kvalificerade domännamn. Du kan skapa en med hjälp av Setup.bat om du ändrar variabeln %SERVER_NAME% till ett fullständigt kvalificerat namn på den dator där tjänsten finns. Observera att filen Setup.bat måste köras i en kommandotolk för utvecklare för Visual Studio som öppnas med administratörsbehörighet.

  4. Kopiera servercertifikatet till arkivet CurrentUser-Trusted Personer på klienten. Du måste bara göra detta om servercertifikatet inte har utfärdats av en betrodd utfärdare.

  5. I filen EchoServiceHost.cs ändrar du värdet för certifikatmottagarens namn för att ange ett fullständigt kvalificerat datornamn i stället för localhost.

  6. Kopiera klientprogramfilerna från mappen \client\bin\ under den språkspecifika mappen till klientdatorn.

  7. I filen Client.cs ändrar du adressvärdet för slutpunkten så att den matchar den nya adressen för din tjänst.

  8. I den Client.cs filen ändrar du ämnesnamnet för tjänst-X.509-certifikatet så att det matchar fjärrvärdens fullständigt kvalificerade datornamn i stället för localhost.

  9. Starta Client.exe från kommandotolken på klientdatorn.

  10. Om klienten och tjänsten inte kan kommunicera kan du läsa Felsökningstips för WCF-exempel.

Rensa efter exemplet

  1. Kör Cleanup.bat i exempelmappen när du har kört exemplet.