Поделиться через


Пользовательский токен

В этом примере демонстрируется, как добавить пользовательскую реализацию токена в приложение Windows Communication Foundation (WCF). В этом примере используется CreditCardToken для безопасной передачи сведений о клиентских кредитных картах в сервис. Маркер передается в заголовке сообщения WS-Security и подписывается и шифруется с помощью элемента привязки симметричного безопасности вместе с текстом сообщения и другими заголовками сообщений. Это полезно в случаях, когда встроенные токены недостаточны. В этом примере показано, как предоставить пользовательский маркер безопасности службе вместо использования одного из встроенных маркеров. Служба реализует контракт, определяющий шаблон связи с запросом и ответом.

Замечание

Процедура установки и инструкции по сборке для этого примера находятся в конце этого раздела.

В этом примере показано следующее:

  • Как клиент может передать в службу пользовательский маркер безопасности.

  • Как служба может использовать и проверять пользовательский маркер безопасности.

  • Как код службы WCF может получить сведения о полученных токенах безопасности, включая пользовательский токен безопасности.

  • Как сертификат X.509 сервера используется для защиты симметричного ключа, используемого для шифрования сообщений и подписи.

Проверка подлинности клиента с помощью пользовательского токена безопасности

Служба предоставляет одну конечную точку, которая создается программно с помощью классов BindingHelper и EchoServiceHost. Конечная точка состоит из адреса, привязки и контракта. Привязка настраивается с помощью пользовательской привязки с использованием SymmetricSecurityBindingElement и HttpTransportBindingElement. В этом примере SymmetricSecurityBindingElement используется для применения сертификата X.509 службы, чтобы защитить симметричный ключ во время передачи, и передачи пользовательского CreditCardToken в заголовке сообщения WS-Security в качестве подписанного и зашифрованного токена безопасности. Поведение определяет учетные данные сервиса, которые должны быть использованы для аутентификации клиента, а также информацию о сертификате 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 классе. Клиент также задает сведения, которые должны содержаться в CreditCardToken, и информацию о сертификате службы X.509 в коде установки, добавив экземпляр CreditCardClientCredentials с соответствующими данными в коллекцию поведения конечных точек клиента. В примере используется сертификат X.509 с именем субъекта, установленным на CN=localhost, в качестве сертификата службы.

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 содержит утверждения о правах доступа, связанные с текущим вызывающим. Утверждения предоставляются классом CreditCardToken в коллекции 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";
}

При запуске примера запросы и ответы операции отображаются в окне консоли клиента. Нажмите клавишу ВВОД в окне клиента, чтобы завершить работу клиента.

Настройка пакетного файла

Пакетный файл Setup.bat, включенный в этот пример, позволяет настроить сервер с соответствующими сертификатами для запуска размещенного в IIS приложения, требующего безопасности на основе сертификатов сервера. Для работы этого пакетного файла на разных компьютерах или без хостинга его необходимо изменить.

Ниже представлены общие сведения о различных разделах пакетных файлов, позволяющие изменять их для выполнения в соответствующей конфигурации.

  • Создание сертификата сервера:

    В следующих строках из Setup.bat пакетного файла создается используемый сертификат сервера. Переменная %SERVER_NAME% задает имя сервера. Измените эту переменную, чтобы указать собственное имя сервера. Значение по умолчанию в этом пакетном файле — localhost. При изменении %SERVER_NAME% переменной необходимо пройти через файлы Client.cs и Service.cs и заменить все упоминания localhost на имя сервера, которое вы используете в скрипте Setup.bat.

    Сертификат хранится в хранилище Мой (Личное) в местоположении LocalMachine хранилища. Сертификат хранится в хранилище LocalMachine для служб, размещенных в IIS. Для услуг с самостоятелной установкой следует изменить пакетный файл, чтобы клиентский сертификат сохранялся в расположении хранилища CurrentUser, заменив строку LocalMachine на 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, не являются неявно доверенными клиентской системой. Если у вас уже есть сертификат, основанный на доверенном корневом сертификате клиента, например, выданный корпорацией Майкрософт, этот шаг, в котором хранилище сертификатов клиента заполняется серверным сертификатом, не требуется.

    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. Переменная среды PATH в командной строке Visual Studio указывает на каталог, содержащий исполняемые файлы, необходимые скрипту Setup.bat.

Для настройки и сборки примера

  1. Убедитесь, что вы выполнили процедуру настройки One-Time для образцов Windows Communication Foundation.

  2. Чтобы создать решение, следуйте инструкциям по созданию примеров Windows Communication Foundation.

Запуск примера на том же компьютере

  1. Откройте окно командной строки Visual Studio с правами администратора и запустите Setup.bat из папки установки примера. При этом устанавливаются все сертификаты, необходимые для выполнения примера. Убедитесь, что путь содержит папку, в которой находится Makecert.exe.

Замечание

Не забудьте удалить сертификаты, выполнив Cleanup.bat после завершения работы с примером. Другие примеры безопасности используют те же сертификаты.

  1. Запустите Client.exe из каталога client\bin. Действие клиента отображается в клиентском консольном приложении.

  2. Если клиент и служба не могут взаимодействовать, см. рекомендации по устранению неисправностей для примеров WCF.

Запуск примера на компьютере

  1. Создайте каталог на компьютере сервиса для файлов двоичного кода сервиса.

  2. Скопируйте файлы программы службы в каталог службы на компьютере службы. Не забудьте скопировать CreditCardFile.txt; в противном случае средство проверки подлинности кредитной карты не может проверить данные кредитной карты, отправленные клиентом. Кроме того, скопируйте файлы Setup.bat и Cleanup.bat на компьютер службы.

  3. У вас должен быть сертификат сервера с именем субъекта, который содержит полное доменное имя компьютера. Вы можете создать его с использованием Setup.bat, если заменить переменную %SERVER_NAME% на полное имя компьютера, на котором размещена служба. Обратите внимание, что файл Setup.bat должен выполняться в командной строке разработчика для Visual Studio, открываемой с правами администратора.

  4. Скопируйте сертификат сервера в хранилище CurrentUser-TrustedPeople на клиенте. Это необходимо сделать, только если сертификат сервера не выдан доверенным издателем.

  5. В файле EchoServiceHost.cs измените значение имени субъекта сертификата, чтобы указать полностью квалифицированное имя компьютера вместо localhost.

  6. Скопируйте файлы клиентской программы из папки \client\bin\ в папку, соответствующую конкретному языку, на клиентский компьютер.

  7. В файле Client.cs измените значение адреса конечной точки, чтобы он соответствовал новому адресу службы.

  8. В файле Client.cs измените имя владельца сертификата службы X.509 так, чтобы оно совпадало с полным квалифицированным именем компьютера удаленного хоста вместо localhost.

  9. На клиентском компьютере запустите Client.exe из окна командной строки.

  10. Если клиент и служба не могут взаимодействовать, см. рекомендации по устранению неисправностей для примеров WCF.

Очистка после образца

  1. Запустите Cleanup.bat в папке примеров после завершения работы примера.