2

我在让 WCF 客户端连接到需要客户端证书和用户名/密码的服务器时遇到问题。

我已经验证了使用 JUST 客户端证书可以使用这个:

        var binding = new WSHttpBinding(SecurityMode.Transport);
        binding.Security.Transport = new HttpTransportSecurity();
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

        var ea = new EndpointAddress(EndpointUrl);

        using (var p = new ServiceReference1.AAAClient(binding, ea))
        {
            try
            {
                p.ClientCredentials.ClientCertificate.SetCertificate(
                        System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser,
                        System.Security.Cryptography.X509Certificates.StoreName.My,
                        System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName,
                        CertificateSubject);

                var x = p.RequiresClientCertificateOnly();
                Console.WriteLine("Status: " + x.status);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            Console.WriteLine();
        }

下一个合乎逻辑的步骤是添加用户名/密码部分,但它返回 403 unauthorized。我知道凭据和证书是有效的,因为我在中间使用了 fiddler 来提供客户端证书和用户名/密码,它工作正常,只是在通过 WCF 使用时它似乎不起作用,或者使用发送客户端证书和用户名/密码。

尝试同时使用两者的代码如下(尝试使用 WSHttpBinding 和 BasicHttpBinding - 两者的结果相同):

        var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
        binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

        var ea = new EndpointAddress(EndpointUrl);

        using (var p = new ServiceReference1.PingPortTypeClient(binding, ea))
        {
            try
            {
                p.ClientCredentials.ClientCertificate.SetCertificate(
                        System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser,
                        System.Security.Cryptography.X509Certificates.StoreName.My,
                        System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName,
                        CertificateSubject);

                p.ClientCredentials.UserName.UserName = Username;
                p.ClientCredentials.UserName.Password = Password;

                var x = p.pingDatabase();
                Console.WriteLine("Status: " + x.status);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            Console.WriteLine();

当我直接在浏览器中访问 URL 时 - 我收到错误消息:

HTTP 错误 403.7 - 禁止您尝试访问的页面要求您的浏览器具有 Web 服务器可识别的安全套接字层 (SSL) 客户端证书。

这表明在上面的第二个示例中没有发送证书,因为它收到了相同的错误 (403)。

有人可以帮我配置 WCF 以提供客户端证书和用户名/密码吗?我在网上搜索过,这里的“类似问题”根本没有帮助。现在变得非常沮丧 - 我错过了什么?

4

2 回答 2

5

如果您需要用户名(消息级别)和证书(传输级别),则必须使用自定义绑定。例如:

        <customBinding>
            <binding name="NewBinding0">
                <textMessageEncoding messageVersion="Default" />
                <security authenticationMode="UserNameOverTransport">
                    <secureConversationBootstrap />
                </security>
                <httpsTransport requireClientCertificate="true" />
            </binding>
        </customBinding>

messageVersion 的值取决于您是要使用 WSHttp 还是 BasicHttp。目前我已将其设置为“默认”,即 WSHttp,对于基本,将其设置为“Soap11”。

于 2013-08-15T10:37:59.413 回答
0

传输级别的用户名和证书

不是这个问题的答案,但问题标题把我带到了这里,所以也许其他人也有:
我需要一个 Soap11 绑定,其中包含证书(用于网关)和传输级别的用户名(用于应用程序)。(这两个部件由客户现场的不同团队和不同供应商维护。)

尽管名称 a customBindingwithUserNameOverTransport在 Soap-header(消息级)中发送用户名和密码,而不是在 http-header(传输级)中,导致这个内部FaultExceptionin SecurityRequestChannel.ProcessReply,源自应用程序:

FaultException: MustUnderstand headers: [{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood.

并使用basicHttpBindingwith<security mode="TransportCredentialOnly">导致:

The provided URI scheme 'https' is invalid; expected 'http'.
Parameter name: via

经过大量的试验和错误,事实证明这可以通过结合配置和代码来完成。

配置传输证书示例

  <basicHttpBinding>
    <binding name="myBasicHttpBindingForTransportCertificate">
      <security mode="Transport">
        <transport clientCredentialType="Certificate" />
      </security>
    </binding>
  </basicHttpBinding>

  <endpointBehaviors>
    <behavior name="myCertificateBehavior">
      <clientCredentials>
        <clientCertificate findValue="my certificate subject" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" />
      </clientCredentials>
    </behavior>

(我使用这个答案动态加载了它。)

通过代码传输的用户名和密码示例

using (new OperationContextScope(client.InnerChannel))
{
    …
    var httpRequestProperty = new HttpRequestMessageProperty();
    httpRequestProperty.Headers[System.Net.HttpRequestHeader.Authorization] =
           "Basic " + Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($"{username}:{password}"));
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;

(感谢“WCF TransportCredentialOnly 不发送用户名和密码”的旧答案。)

于 2020-03-02T11:23:39.380 回答