Bagikan melalui


Token Kustom

Sampel ini menunjukkan cara menambahkan implementasi token kustom ke dalam aplikasi Windows Communication Foundation (WCF). Contohnya menggunakan CreditCardToken untuk meneruskan informasi dengan aman tentang kartu kredit klien ke layanan. Token diteruskan di header pesan WS-Security dan ditandatangani dan dienkripsi menggunakan elemen pengikatan keamanan simetris bersama dengan isi pesan dan header pesan lainnya. Ini berguna dalam kasus di mana token bawaan tidak cukup. Sampel ini menunjukkan cara memberikan token keamanan kustom ke layanan alih-alih menggunakan salah satu token bawaan. Layanan tersebut mengimplementasikan kontrak yang mendefinisikan pola komunikasi balasan permintaan.

Catatan

Prosedur penyiapan dan petunjuk pembuatan untuk sampel ini terdapat di akhir topik ini.

Untuk meringkas, sampel ini menunjukkan hal berikut:

  • Bagaimana klien dapat meneruskan token keamanan kustom ke layanan.

  • Bagaimana layanan dapat menggunakan dan memvalidasi token keamanan kustom.

  • Bagaimana kode layanan WCF dapat memperoleh informasi tentang token keamanan yang diterima termasuk token keamanan kustom.

  • Bagaimana sertifikat X.509 server digunakan untuk melindungi kunci simetris yang digunakan untuk enkripsi pesan dan tanda tangan.

Autentikasi Klien Menggunakan Token Keamanan Kustom

Layanan ini memaparkan satu titik akhir yang dibuat secara terprogram menggunakan kelas BindingHelper dan EchoServiceHost. Titik akhir terdiri dari alamat, pengikatan, dan kontrak. Pengikatan dikonfigurasi dengan pengikatan kustom menggunakan SymmetricSecurityBindingElement dan HttpTransportBindingElement. Sampel ini mengatur SymmetricSecurityBindingElement untuk menggunakan sertifikat X.509 layanan untuk melindungi kunci simetris selama transmisi dan meneruskan kustom CreditCardToken di header pesan WS-Security sebagai token keamanan yang ditandatangani dan dienkripsi. Perilaku menentukan mandat layanan yang akan digunakan untuk autentikasi klien dan juga informasi tentang sertifikat X.509 layanan.

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

Untuk menggunakan token kartu kredit dalam pesan, sampel menggunakan mandat layanan kustom untuk menyediakan fungsionalitas ini. Kelas mandat layanan terletak di kelas CreditCardServiceCredentials dan ditambahkan ke kumpulan perilaku host layanan dalam metode 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();
    }
}

Titik akhir klien dikonfigurasi dengan cara yang sama seperti titik akhir layanan. Klien menggunakan kelas BindingHelper yang sama untuk membuat pengikatan. Sisa pengaturan terletak di kelas Client. Klien juga mengatur informasi yang akan dimuat dalam CreditCardToken dan informasi tentang sertifikat X.509 layanan dalam kode pengaturan dengan menambahkan instans CreditCardClientCredentials dengan data yang tepat ke kumpulan perilaku titik akhir klien. Sampel menggunakan sertifikat X.509 dengan nama subjek diatur ke CN=localhost sebagai sertifikat layanan.

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

Implementasi Token Keamanan Kustom

Untuk mengaktifkan token keamanan kustom di WCF, buat representasi objek dari token keamanan kustom. Sampel memiliki representasi ini di kelas CreditCardToken. Representasi objek bertanggung jawab untuk menyimpan semua informasi token keamanan yang relevan dan untuk memberikan daftar kunci keamanan yang terkandung dalam token keamanan. Dalam hal ini, token keamanan kartu kredit tidak berisi kunci keamanan apa pun.

Bagian berikutnya menjelaskan apa yang harus dilakukan untuk mengaktifkan token kustom yang akan ditransmisikan melalui kawat dan digunakan oleh titik akhir 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; } }
}

Mendapatkan Token Kartu Kredit Kustom ke dan dari Pesan

Pembuat serialisasi token keamanan di WCF bertanggung jawab untuk membuat representasi objek token keamanan dari XML dalam pesan dan membuat bentuk XML token keamanan. Mereka juga bertanggung jawab atas fungsionalitas lain seperti membaca dan menulis pengidentifikasi kunci yang menunjuk ke token keamanan, tetapi contoh ini hanya menggunakan fungsionalitas terkait token keamanan. Untuk mengaktifkan token kustom, Anda harus menerapkan pembuat serialisasi token keamanan Anda sendiri. Sampel ini menggunakan kelas CreditCardSecurityTokenSerializer untuk tujuan ini.

Pada layanan, pembuat serialisasi kustom membaca bentuk XML dari token kustom dan membuat representasi objek token kustom darinya.

Pada klien, kelas CreditCardSecurityTokenSerializer menulis informasi yang terkandung dalam representasi objek token keamanan ke dalam penulis 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);
        }
    }
}

Cara Membuat Penyedia Token dan Kelas Pengautentikasi Token

Mandat klien dan layanan bertanggung jawab untuk menyediakan instans manajer token keamanan. Instans manajer token keamanan digunakan untuk mendapatkan penyedia token, pengautentikasi token, dan pembuat serialisasi token.

Penyedia token membuat representasi objek token berdasarkan informasi yang terkandung dalam mandat klien atau layanan. Representasi objek token kemudian ditulis ke pesan menggunakan pembuat serialisasi token (dibahas di bagian sebelumnya).

Pengautentikasi token memvalidasi token yang tiba dalam pesan. Representasi objek token yang masuk dibuat oleh pembuat serialisasi token. Representasi objek ini kemudian diteruskan ke pengautentikasi token untuk validasi. Setelah token berhasil divalidasi, pengautentikasi token mengembalikan kumpulan objek IAuthorizationPolicy yang mewakili informasi yang terkandung dalam token. Informasi ini digunakan nanti selama pemrosesan pesan untuk melakukan keputusan otorisasi dan untuk memberikan klaim untuk aplikasi. Dalam contoh ini, pengautentikasi token kartu kredit menggunakan CreditCardTokenAuthorizationPolicy untuk tujuan ini.

Pembuat serialisasi token bertanggung jawab untuk mendapatkan representasi objek token ke dan dari kawat. Ini dibahas di bagian sebelumnya.

Dalam sampel ini, kami menggunakan penyedia token hanya pada klien dan pengautentikasi token hanya pada layanan, karena kami ingin mengirimkan token kartu kredit hanya dalam arah klien-ke-layanan.

Fungsionalitas pada klien terletak di kelas CreditCardClientCredentials, CreditCardClientCredentialsSecurityTokenManager dan CreditCardTokenProvider.

Pada layanan, fungsionalitas berada di kelas CreditCardServiceCredentials, CreditCardServiceCredentialsSecurityTokenManager, CreditCardTokenAuthenticator dan 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;
        }
    }

Menampilkan Informasi Pemanggil

Untuk menampilkan informasi pemanggil, gunakan ServiceSecurityContext.Current.AuthorizationContext.ClaimSets seperti yang ditunjukkan dalam kode sampel berikut. ServiceSecurityContext.Current.AuthorizationContext.ClaimSets berisi klaim otorisasi yang terkait dengan pemanggil saat ini. Klaim disediakan oleh kelas CreditCardToken dalam koleksi 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";
}

Saat Anda menjalankan sampel, permintaan dan respons operasi ditampilkan di jendela konsol klien. Tekan ENTER di jendela klien untuk mematikan komputer klien.

Menyiapkan File Batch

File batch Setup.bat yang disertakan dengan sampel ini memungkinkan Anda mengonfigurasi server dengan sertifikat yang relevan untuk menjalankan aplikasi yang dihost IIS yang memerlukan keamanan berbasis sertifikat server. File batch ini harus dimodifikasi untuk bekerja di seluruh komputer atau untuk bekerja dalam kasus yang tidak dihosting.

Berikut ini memberikan gambaran singkat tentang berbagai bagian file batch sehingga dapat dimodifikasi untuk berjalan dalam konfigurasi yang sesuai:

  • Membuat sertifikat server:

    Baris berikut dari file batch Setup.bat membuat sertifikat server yang akan digunakan. Variabel %SERVER_NAME% menentukan nama server. Ubah variabel ini untuk menentukan nama server Anda sendiri. Default dalam file batch ini adalah localhost. Jika Anda mengubah variabel %SERVER_NAME%, Anda harus melalui file Client.cs dan Service.cs dan mengganti semua instans localhost dengan nama server yang Anda gunakan dalam skrip Setup.bat.

    Sertifikat disimpan di penyimpanan Saya (Pribadi) di bawah LocalMachine lokasi penyimpanan. Sertifikat disimpan di penyimpanan LocalMachine untuk layanan yang dihosting IIS. Untuk layanan yang dihost sendiri, Anda harus memodifikasi file batch untuk menyimpan sertifikat klien di lokasi penyimpanan CurrentUser dengan mengganti string LocalMachine dengan 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
    
  • Menginstal sertifikat server ke penyimpanan sertifikat tepercaya klien:

    Baris berikut dalam file batch Setup.bat menyalin sertifikat server ke penyimpanan orang tepercaya klien. Langkah ini diperlukan karena sertifikat yang dihasilkan oleh Makecert.exe tidak dipercaya secara implisit oleh sistem klien. Jika Anda sudah memiliki sertifikat yang berakar pada sertifikat akar tepercaya klien—misalnya, sertifikat yang dikeluarkan Microsoft—langkah mengisi penyimpanan sertifikat klien dengan sertifikat server tidak diperlukan.

    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
    
  • Untuk mengaktifkan akses ke kunci privat sertifikat dari layanan yang di-hosting IIS, akun pengguna tempat proses yang di-hosting IIS berjalan harus diberikan izin yang sesuai untuk kunci privat. Ini dicapai dengan langkah terakhir dalam skrip 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
    

Catatan

File batch Setup.bat dirancang untuk dijalankan dari Prompt Perintah Visual Studio. Variabel lingkungan PATH yang diatur dalam Perintah Visual Studio menunjuk ke direktori yang berisi executable yang diperlukan oleh skrip Setup.bat.

Untuk menyiapkan dan membangun sampel

  1. Pastikan Anda telah melakukan Prosedur Penyiapan Satu Kali untuk Sampel Windows Communication Foundation.

  2. Untuk membangun solusi, ikuti instruksi dalam Membangun Sampel Windows Communication Foundation.

Untuk menjalankan sampel di komputer yang sama

  1. Buka jendela Visual Studio Command Prompt dengan hak istimewa administrator dan jalankan Setup.bat dari folder pemasangan sampel. Ini menginstal semua sertifikat yang diperlukan untuk menjalankan sampel. Pastikan jalur menyertakan folder tempat Makecert.exe berada.

Catatan

Pastikan untuk menghapus sertifikat dengan menjalankan Cleanup.bat setelah selesai dengan sampel. Sampel keamanan lainnya menggunakan sertifikat yang sama.

  1. Jalankan Client.exe dari direktori \client\bin. Aktivitas klien ditampilkan di aplikasi konsol klien.

  2. Jika klien dan layanan tidak dapat berkomunikasi, lihat Tips Pemecahan Masalah untuk Sampel WCF.

Untuk menjalankan sampel di seluruh komputer

  1. Buat direktori pada komputer layanan untuk biner layanan.

  2. Salin file program layanan ke direktori layanan pada komputer layanan. Jangan lupa untuk menyalin CreditCardFile.txt; jika tidak, pengautentikasi kartu kredit tidak dapat memvalidasi informasi kartu kredit yang dikirim dari klien. Salin juga file Setup.bat dan Cleanup.bat ke komputer layanan.

  3. Anda harus memiliki sertifikat server dengan nama subjek yang berisi nama domain komputer yang sepenuhnya memenuhi syarat. Anda dapat membuatnya menggunakan Setup.bat jika Anda mengubah variabel %SERVER_NAME% menjadi nama komputer yang sepenuhnya memenuhi syarat tempat layanan dihosting. Perhatikan bahwa file Setup.bat harus dijalankan dalam Perintah Pengembang untuk Visual Studio yang dibuka dengan hak istimewa administrator.

  4. Salin sertifikat server ke penyimpanan CurrentUser-TrustedPeople pada klien. Anda harus melakukan ini hanya jika sertifikat server tidak dikeluarkan oleh pengeluar setrifikat tepercaya.

  5. Dalam file EchoServiceHost.cs, ubah nilai nama subjek sertifikat untuk menentukan nama komputer yang sepenuhnya memenuhi syarat alih-alih localhost.

  6. Salin file program klien dari folder \client\bin\, di bawah folder khusus bahasa, ke komputer klien.

  7. Dalam file Client.cs, ubah nilai alamat titik akhir agar sesuai dengan alamat baru layanan Anda.

  8. Dalam file Client.cs ubah nama subjek sertifikat layanan X.509 agar sesuai dengan nama komputer host jarak jauh yang sepenuhnya memenuhi syarat alih-alih localhost.

  9. Di komputer klien, luncurkan Client.exe dari jendela perintah.

  10. Jika klien dan layanan tidak dapat berkomunikasi, lihat Tips Pemecahan Masalah untuk Sampel WCF.

Untuk membersihkan sampel

  1. Jalankan Cleanup.bat di folder sampel setelah Anda selesai menjalankan sampel.