WCF服务中的自定义客户端证书和用户名验证

WCF服务中的自定义客户端证书和用户名验证,wcf,certificate,client,Wcf,Certificate,Client,我的问题是这样的: 我们目前正在运行一组服务,要求客户端在调用服务时提供用户名和密码作为身份验证 我们希望在这些服务上实施PKI基础设施,但我们的一些合作伙伴将比其他合作伙伴花费更长的时间来适应这种新的基础设施 作为第一步,我们希望要求一些合作伙伴提供客户端证书。访问我们服务器上的数据需要客户端证书(除了用户名和密码),而其他用户只需要用户名和密码 为了解决这个问题,我正在尝试为WCF中的用户名/密码身份验证(使用UserNamePasswordValidator)和客户端证书(使用X

我的问题是这样的:

  • 我们目前正在运行一组服务,要求客户端在调用服务时提供用户名和密码作为身份验证

  • 我们希望在这些服务上实施PKI基础设施,但我们的一些合作伙伴将比其他合作伙伴花费更长的时间来适应这种新的基础设施

  • 作为第一步,我们希望要求一些合作伙伴提供客户端证书。访问我们服务器上的数据需要客户端证书(除了用户名和密码),而其他用户只需要用户名和密码

为了解决这个问题,我正在尝试为WCF中的用户名/密码身份验证(使用
UserNamePasswordValidator
)和客户端证书(使用
X509CertificateValidator
)实现一个自定义验证器。用户名/密码验证器将向我们的数据库验证这些凭据,而客户端证书验证器将检查请求是否来自我们需要证书的客户端,如果是,则验证是否提供了有效的客户端证书。我无法配置WCF,使其同时使用这两个验证器

服务器上的我的WCF配置当前设置如下:



我想我可能正在尝试实现一些与WCF工作方式不兼容的东西,但是如果有人知道如何解决这个问题,我很高兴能得到您的反馈。

这是在配置中使用开箱即用绑定无法定义的。即使是自定义绑定也不支持足够的基础结构,无法在配置中定义此类绑定

首先,您肯定需要两个端点。一个仅用于具有用户名/密码的客户端。可以使用一些公共绑定配置此端点,这些绑定期望使用用户名客户端凭据的消息安全性或使用消息凭据的传输安全性。第二个端点将用于更复杂的验证。此端点需要在代码中定义新绑定。此绑定必须使用:

  • 不对称安全绑定元素(相互证书认证)
  • X.509安全令牌作为主安全令牌
  • 用户名安全令牌作为支持安全令牌
这是我与类似服务通信时必须使用的绑定示例:

  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基础设施可能只选择一个进行验证,但这样的逻辑也可以被替换。

我想我现在已经找到了问题的解决方案,这要感谢@ladislav mrnka在其回答中提供的宝贵输入。我意识到有必要提供两个端点来配置不同的需求,并且我还了解了在配置服务时支持令牌的可能性

我在MSDN上找到了一个关于的链接,按照这个方法,我在服务器上用以下自定义绑定实现了端点(我通过代码切换到了配置。不确定是否可以在web.config中设置)

此绑定创建一个对称密钥,其中使用对称密钥(使用服务器证书加密)加密消息头中的用户名/密码安全令牌以及消息正文本身

此外,还添加了一个X509安全令牌作为绑定的背书和支持令牌。此令牌配置为始终包含在客户端对服务器的请求中

此自定义绑定随后用于配置一个新的WCF服务,该服务的端点需要此绑定。我正在使用Castle Windsor中的WcfFacility来配置服务

此代码执行以下操作:

  • 设置服务证书
  • 将客户端证书的验证模式设置为链信任,以便传入的客户端证书必须由服务器存储中的受信任根证书颁发机构颁发
  • 为用户名/密码凭据和客户端证书添加自定义验证程序
///注册WCF服务
var returnFaults=new ServiceDebugBehavior{IncludeExceptionDetailInFaults=true};
var metaData=new ServiceMetadataBehavior{HttpsGetEnabled=true};
var servicecdentials=新servicecdentials();
//配置服务服务证书
serviceCredentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
店名,我的,
X509FindType.FindBySubjectName,
“服务器证书”);
//配置客户端证书身份验证模式
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode=X509CertificateValidationMode.ChainTrust;
//添加自定义用户名密码验证程序
serviceCredentials.UserNameAuthentication.UserNamePasswordValidationMode=
UserNamePasswordValidationMode.Custom;
serviceCredentials.UserNameAuthentication.CustomUserNamePasswordValidator=
_container.Resolve();
//添加自定义证书验证程序
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode=
X509CertificateValidationMode.自定义;
serviceCredentials.ClientCertificate.Authentication.CustomCertificateValidator=
_container.Resolve();
var serviceModel=新的DefaultServiceModel();
serviceModel.AddEndpoints(
WcfEndpoint.ForContract().BoundTo(CreateMultiFactorAuthenticationBinding());
serviceModel.BaseAddresses.Add(新Uri(“https://server.com/MyServiceImpl
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);
}