我有一个使用 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 解决方案一样。
有任何想法吗?
再次感谢!