1

我有一个使用 net.tcp 的自托管 WCF 服务,但现在,为了支持旧应用程序,我们必须打开一个 basicHttpBinding。安全性是通过使用证书的传输完成的,它通过 net.tcp 运行良好。服务的配置是通过其 App.config 完成的:

  <system.serviceModel>
    <services>
      <service name="MyServiceHost" behaviorConfiguration="ServiceBehavior">
    <endpoint address="net.tcp://localhost:1234/MyService" binding="netTcpBinding" bindingConfiguration="CertificateCredentialsServiceBinding" name="MyService_Tcp_Certificate" contract="Wcf.MyService" />
    <endpoint address="https://localhost:2234/MyService" binding="basicHttpBinding" bindingConfiguration="HttpCertificateCredentialsServiceBinding" name="MyService_BasicHttp_Certificate" contract="Wcf.MyService" />
    <endpoint address="MyServiceMex" contract="IMetadataExchange" binding="mexTcpBinding" />
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:2235/"/>
        <add baseAddress="net.tcp://localhost:1235/" />
      </baseAddresses>
    </host>
      </service>
    </services>
    <bindings>
      <netTcpBinding>
    <binding name="CertificateCredentialsServiceBinding" receiveTimeout="00:30:00" sendTimeout="00:15:00">
      <readerQuotas maxArrayLength="2147483647" />
      <reliableSession inactivityTimeout="3.00:00:00" enabled="true" />
      <security mode="Transport">
        <transport clientCredentialType="Certificate" />
      </security>
    </binding>
      </netTcpBinding>
      <basicHttpBinding>
    <binding name="HttpCertificateCredentialsServiceBinding" receiveTimeout="00:30:00" sendTimeout="00:15:00">
      <security mode="Transport">
        <transport clientCredentialType="Certificate" />
      </security>
    </binding>
      </basicHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
    <behavior name="ServiceBehavior">
      <serviceTimeouts transactionTimeout="00:15:00" />
      <serviceMetadata httpGetEnabled="true" httpGetUrl="" />
      <serviceCredentials>
        <clientCertificate>
          <authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck" trustedStoreLocation="LocalMachine" />
        </clientCertificate>
        <serviceCertificate storeLocation="LocalMachine" findValue="my-certificate-thumprint-here" x509FindType="FindByThumbprint" />
      </serviceCredentials>
    </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

当客户端使用自己的客户端证书和 NET.TCP 进行连接时,一切正常并且客户端凭据得到正确验证。这是客户端的(简化)代码:

        MyService myService;
        Uri serviceUri = new Uri("net.tcp://localhost:1234/MyService");

        NetTcpBinding netTcpBinding = new NetTcpBinding(SecurityMode.Transport);
        netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
        netTcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;

        Uri netTcpUri = serviceUri;
        EndpointAddress netTcpEndpoint = new EndpointAddress(netTcpUri, EndpointIdentity.CreateDnsIdentity("MyServer"));

        ChannelFactory<MyService> tempServicesFactory = null;
        ChannelFactory<MyService> servicesFactory = null;

        servicesFactory = new ChannelFactory<MyService>(netTcpBinding, netTcpEndpoint);

        servicesFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "my-client-certificate-thumprint-here");
        servicesFactory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;

        myService = servicesFactory.CreateChannel();

直到这里一切正常。尝试连接到 Basic Http 绑定虽然失败了。这是客户端的代码:

        MyService myService;
        Uri serviceUri = new Uri("https://localhost:2234/MyService");

        BasicHttpBinding httpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
        httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

        Uri httpUri = serviceUri;
        EndpointAddress httpEndpoint = new EndpointAddress(httpUri, EndpointIdentity.CreateDnsIdentity("MyServer"));

        ChannelFactory<MyService> tempServicesFactory = null;
        ChannelFactory<MyService> servicesFactory = null;

        servicesFactory = new ChannelFactory<MyService>(httpBinding, httpEndpoint);

        servicesFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "my-client-certificate-thumprint-here");
        servicesFactory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;

        myService = servicesFactory.CreateChannel();

使用此代码,当我尝试调用服务(在本例中为“GetAllProducts”方法)时,出现以下异常:

System.ServiceModel.Security.SecurityNegotiationException was caught
  HResult=-2146233087
  Message=Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost:2234'.
  Source=mscorlib
  StackTrace:
    Server stack trace: 
       at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
       at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
       at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
       at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
       at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
    Exception rethrown at [0]: 
       at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       at Wcf.MyService.GetAllProducts()
       at TesterApp.Initialise() in CODEFILE :line 159
  InnerException: System.Net.WebException
       HResult=-2146233079
       Message=The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
       Source=System
       StackTrace:
        at System.Net.HttpWebRequest.GetResponse()
        at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
       InnerException: System.Security.Authentication.AuthenticationException
        HResult=-2146233087
        Message=The remote certificate is invalid according to the validation procedure.
        Source=System
        StackTrace:
         at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
         at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
         at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
         at System.Net.TlsStream.CallProcessAuthentication(Object state)
         at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
         at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
         at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
         at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
         at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
         at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size)
         at System.Net.ConnectStream.WriteHeaders(Boolean async)
        InnerException: 

尽管证书看起来无效,但我确信它是有效的,因为它适用于 NET.TCP。

哦,当然,在所有这些工作之前,我必须使用 netsh.exe 设置这些:

netsh.exe http add urlacl url=https://+:2234/ user=MYDOMAIN\MyUserName
netsh.exe http add sslcert ipport=0.0.0.0:2234 certhash=my-certificate-thumprint-here appid={my-service-app-guid-here}

关于问题可能是什么以及如何解决的任何想法?

谢谢!

更新:

我现在设法摆脱了第一个错误,这是一个更改所有地址以匹配证书名称(MyServer)而不是使用本地主机的情况。不幸的是,设置 DNS 身份EndpointIdentity.CreateDnsIdentity("MyServer")似乎不适用于 HTTP 绑定,即使它适用于 NET.TCP。

从客户端调用服务时,我现在收到 403(禁止)错误。正如您在上面的代码中看到的那样,正在设置客户端证书,但好像不是。如果我从我的配置 ( <transport clientCredentialType="Certificate" />to <transport clientCredentialType="None" />) 中删除期望客户端凭据类型为证书的配置,那么这一切都适用于传输安全性,但我仍然需要客户端通过客户端证书提供凭据,就像 NET.TCP 解决方案一样。

有任何想法吗?

再次感谢!

4

0 回答 0