Я думаю, что нашел решение моей проблемы сейчас благодаря ценному вкладу @ ladislav-mrnka в его ответе. Я понял, что необходимо предоставить две конечные точки для настройки различных требований, а также узнал о возможностях поддержки токенов при настройке служб.
Я нашел ссылку о поддерживающих токенах на MSDN, и, следуя этому рецепту, я реализовал конечную точку на сервере со следующим настраиваемым связыванием (я переключился на настройку через код. Не уверен, что это может быть также настроенным в web.config.)
private static Binding CreateMultiFactorAuthenticationBinding()
{
var httpsTransport = new HttpsTransportBindingElement();
// The message security binding element will be configured to require 2 tokens:
// 1) A username-password encrypted with the service token
// 2) A client certificate used to sign the message
// Create symmetric security binding element with encrypted username-password token.
// Symmetric key is encrypted with server certificate.
var messageSecurity = SecurityBindingElement.CreateUserNameForCertificateBindingElement();
messageSecurity.AllowInsecureTransport = false;
// Require client certificate as endorsing supporting token for all requests from client to server
var clientX509SupportingTokenParameters = new X509SecurityTokenParameters
{
InclusionMode =
SecurityTokenInclusionMode.AlwaysToRecipient
};
messageSecurity.EndpointSupportingTokenParameters.Endorsing.Add(clientX509SupportingTokenParameters);
return new CustomBinding(messageSecurity, httpsTransport);
}
Эта привязка создает SymmetricSecurityBindingElement , где симметричный ключ (зашифрованный с помощью сертификата сервера) используется для шифрования токена имени пользователя / пароля в заголовке сообщения и самого тела сообщения.
Кроме того, маркер безопасности X509 добавлен в качестве подтверждающего, поддерживающего токен для привязки. Этот токен настроен на постоянное включение в клиентские запросы к серверу.
Эта пользовательская привязка впоследствии использовалась для настройки новой WCF-службы с конечной точкой, требующей этой привязки. Я использую WcfFacility в Castle Windsor для настройки службы.
Этот код выполняет следующие действия:
- Устанавливает сертификат обслуживания
- Устанавливает режим проверки для сертификатов клиента для цепочки доверия, так что входящие сертификаты клиента должны выдаваться доверенным корневым центром сертификации в хранилище сервера
- Добавляет пользовательские валидаторы для имени пользователя / пароля и сертификата клиента.
//// Registering WCF-services
var returnFaults = new ServiceDebugBehavior {IncludeExceptionDetailInFaults = true};
var metaData = new ServiceMetadataBehavior {HttpsGetEnabled = true};
var serviceCredentials = new ServiceCredentials();
// Configure service sertificate
serviceCredentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"ServerCertificate");
// Configure client certificate authentication mode
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust;
// Add custom username-password validator
serviceCredentials.UserNameAuthentication.UserNamePasswordValidationMode =
UserNamePasswordValidationMode.Custom;
serviceCredentials.UserNameAuthentication.CustomUserNamePasswordValidator =
_container.Resolve<MyServicesUsernameValidator>();
// Add custom certificate validator
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode =
X509CertificateValidationMode.Custom;
serviceCredentials.ClientCertificate.Authentication.CustomCertificateValidator =
_container.Resolve<MyServicesCertificateValidator>();
var serviceModel = new DefaultServiceModel();
serviceModel.AddEndpoints(
WcfEndpoint.ForContract<IMyContract>().BoundTo(CreateMultiFactorAuthenticationBinding()));
serviceModel.BaseAddresses.Add(new Uri("https://server.com/MyServiceImplementation.svc"));
serviceModel.AddExtensions(serviceCredentials);
serviceModel.AddExtensions(metaData);
_container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero)
.Register(Component.For<IMyContract>()
.ImplementedBy<MyServiceImplementation>()
.AsWcfService(serviceModel),
Component.For<IServiceBehavior>().Instance(returnFaults));
MyServicesUsernameValidator наследует UserNamePasswordValidator, а MyServicesCertificateValidator наследует X509CertificateValidator. Оба переопределяют свои соответствующие методы Validate.
Кажется, это решает мою конкретную проблему ... Надеюсь, она решит вашу! :)