[英]Custom client certificate and username validation in WCF service
我的特殊問題是這樣的:
我們目前正在運行一組服務,要求客戶端在調用服務時提供用戶名和密碼作為身份驗證。
我們希望在這些服務上實施 PKI 基礎設施,但我們的一些合作伙伴將使用比其他合作伙伴更長的時間來適應這種新的基礎設施。
作為第一步,我們希望從我們的一些合作伙伴那里獲得客戶證書。 需要客戶端證書(除了用戶名和密碼)才能訪問他們在我們服務器上的數據,而其他用戶只需要用戶名和密碼。
為了解決這個問題,我嘗試在 WCF 中為用戶名/密碼驗證(使用UserNamePasswordValidator
)和客戶端證書(使用X509CertificateValidator
)實現自定義驗證器。 用戶名/密碼驗證器將向我們的數據庫驗證這些憑據,而客戶端證書驗證器將檢查請求是否來自我們需要證書的客戶端,如果是,則驗證是否提供了有效的客戶端證書。 我無法配置 WCF 以便它使用這兩個驗證器。
我在服務器上的 WCF 配置目前是這樣設置的:
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpsGetEnabled="true" policyVersion="Policy15" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<authentication customCertificateValidatorType="MyWS.Security.MyServicesCertificateValidator, MyWS"
certificateValidationMode="Custom" revocationMode="NoCheck" />
</clientCertificate>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="MyWS.Security.MyServicesUsernameValidator, MyWS" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<bindings>
<basicHttpBinding>
<binding name="MySoapBinding">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Certificate" />
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="MyWS.Services.TheService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="MySoapBinding" name="TheService" bindingNamespace="https://services.my/TheService" contract="MyWS.Interfaces.Service.ITheService" />
<host>
<baseAddresses>
<add baseAddress="https://localhost:4434/MyWS/TheService"/>
</baseAddresses>
</host>
</service>
</services>
據我了解,此配置無效,因為我無法在傳輸層使用customCertificateValidatorType
(因為 IIS 在此處涉及 WCF 之前檢查證書),但我看不出我如何能夠結合customCertificateValidatorType
和customUserNamePasswordValidatorType
作為消息層的憑證類型。
我已經實現了一個消息檢查器,並且可能能夠以某種方式使用OperationContext
解決問題(如下面的鏈接中所建議的那樣),但我還沒有找到一種方法讓我這樣做。
http://social.msdn.microsoft.com/Forums/en/wcf/thread/b6ab8b58-516b-41d4-bb0e-75b4baf92716
我想我可能正在嘗試實現與 WCF 的工作方式不兼容的東西,但如果有人知道如何解決這個問題,我會很高興收到您的反饋。
我想我現在已經找到了解決我的問題的方法,這要歸功於@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 ,其中使用對稱密鑰(使用服務器證書加密)來加密消息 header 和消息正文中的用戶名/密碼安全令牌。
此外,X509 安全令牌作為背書支持令牌添加到綁定。 此令牌配置為始終包含在客戶端對服務器的請求中。
此自定義綁定隨后用於配置具有需要此綁定的端點的新 WCF 服務。 我正在使用 Castle Windsor 中的 WcfFacility 來配置服務。
此代碼執行以下操作:
//// 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 方法。
這似乎解決了我的特殊問題......希望它能解決你的問題::)
使用開箱即用的綁定無法在配置中定義。 即使自定義綁定也不支持足夠的基礎架構來在配置中定義此類綁定。
首先,您肯定需要兩個端點。 一個將僅用於具有用戶名/密碼的客戶端。 此端點可以配置一些通用綁定,期望使用 UserName 客戶端憑據的消息安全性或使用消息憑據的傳輸安全性。 第二個端點將用於您更復雜的驗證。 該端點需要在代碼中定義新的綁定。 此綁定必須使用:
這是我在與類似服務通信時必須使用的綁定示例:
Custom binding = new CustomBinding();
var userNameToken = new UserNameSecurityTokenParameters();
userNameToken.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
var securityElement = new AsymmetricSecurityBindingElement();
securityElement.IncludeTimestamp = true;
securityElement.RecipientTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.SubjectKeyIdentifier, SecurityTokenInclusionMode.Never);
securityElement.InitiatorTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.SubjectKeyIdentifier, SecurityTokenInclusionMode.AlwaysToRecipient);
securityElement.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256;
securityElement.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
securityElement.SetKeyDerivation(false);
securityElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(userNameToken);
securityElement.MessageProtectionOrder = MessageProtectionOrder.EncryptBeforeSign;
securityElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11;
binding.Elements.Add(securityElement);
var encodingElement = new TextMessageEncodingBindingElement();
encodingElement.MessageVersion = MessageVersion.Soap12WSAddressingAugust2004;
binding.Elements.Add(encodingElement);
var httpElement = new HttpTransportBindingElement();
httpElement.UseDefaultWebProxy = true;
binding.Elements.Add(httpElement);
此示例使用代碼中定義的CustomBinding
。 如果您想在配置中使用它,您必須創建全新的綁定和綁定擴展,並在配置文件中注冊該擴展。
即使這樣,我也不確定是否會使用兩個驗證器 - 我將其用作服務的客戶端。 要點是請求只能有一個主令牌,默認的 WCF 基礎設施可能只會選擇一個來驗證,但也可以替換這種邏輯。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.