一段时间后,带有客户端证书的WCF应用程序失败了吗?

时间:2017-03-30 09:15:55

标签: c# .net winforms wcf ssl

我有一个看起来像这样的解决方案:

  • Winform WCF(客户端)
  • 在Windows服务中自托管IdentityService4
  • Windows服务中自托管的WCF HTTPS服务

IdentitySErvice4和WCF服务都使用功能证书。客户端正在使用从Windows应用商店加载的客户端证书。客户端服务通过带有第三方软件的智能卡安装到Windows应用商店。如果卡被移除,证书将从商店中删除。

流程如下:

1 客户端从商店加载证书并将其绑定到takeenhandler,如下所示:

    public override TokenClient GetTokenClient(string host, string port)
            {
                var certificate = SmartCardHandler.GetInstance().Result.CurrentCertificate();
                if (certificate == null)
                    throw new Exception("Certificate is missing");

                var handler = new WebRequestHandler();
                handler.ServerCertificateValidationCallback = PinPublicKey;
                var url = $"https://{host}:{port}/connect/token";

                handler.ClientCertificates.Add(certificate);

                return new TokenClient(url, ClientTypes.Siths, "secret", handler);
            }

token = await tokenClient.RequestCustomGrantAsync(ClientTypes.Siths, "MyApp.wcf offline_access");

2. 客户端将请求发送到IdentityServices,IdentityServices读取客户端证书并使用它进行身份验证并生成返回的客户端令牌。

3. 客户端使用客户端证书为服务创建WCF频道,该令牌也会像这样附加到此频道

private async Task<ChannelFactory<T>> CreateChannelFactory(LoginTypeBase loginType, MyAppToken token)
        {
            var service = await _ConsulService.GetServiceBlocking(loginType.MyAppServicesToUse, forceRefresh: true, token: new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token);

            if (service == null)
                throw new MyAppServiceCommunicationException();

            var cert = loginType.ClientCertificate;
            var uri = loginType.GetMyAppClientServiceURL(service.Address, service.Port);

            var header = AddressHeader.CreateAddressHeader(nameof(MyAppToken), nameof(MyAppToken), token);
            var endpointAddress = new EndpointAddress(uri, header);

            ServiceEndpoint serviceEndpoint = null;
            if (loginType.LoginType == LoginType.SmartCard || loginType.LoginType == LoginType.UsernamePasswordSLL)
            {
                var binding = new NetHttpsBinding("netHttpsBinding");
                binding.Security.Mode = BasicHttpsSecurityMode.Transport;
                if (loginType.LoginType == LoginType.SmartCard)
                    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
                else
                    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

                serviceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(T)), binding, endpointAddress);
            }
            else
            {
                var binding = new NetHttpBinding("netHttpBinding");
                serviceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(T)), binding, endpointAddress);
            }

            serviceEndpoint.EndpointBehaviors.Add(new ProtoEndpointBehavior());
            serviceEndpoint.EndpointBehaviors.Add(new CustomMessageInspectorBehavior());


            var v = new ChannelFactory<T>(serviceEndpoint);
            if (loginType.LoginType == LoginType.SmartCard)
            {
                v.Credentials.ClientCertificate.Certificate = cert;
                //v.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindByThumbprint, cert.Thumbprint);
            }
            return v;
        }
    }

4. 客户端将第一条消息发送给WCF服务。当授予消息时,第三方软件将响应并请求客户端证书的引脚,并将消息转发到WCF服务,其中令牌将根据IdentityService4进行验证。

真实代理用于在需要时切换到其他服务。在这种情况下,它只有一个在线WCF服务的URL。

 public override IMessage Invoke(IMessage msg)
        {
            var methodCall = (IMethodCallMessage)msg;
            var method = (MethodInfo)methodCall.MethodBase;
            var channel = GetChannel(false);
            var retryCount = 3;
            do
            {
                try
                {
                    var result = method.Invoke(channel, methodCall.InArgs);
                    var returnmessage = new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
                    return returnmessage;
                }
                catch (Exception e)
                {
                    if (e is TargetInvocationException && e.InnerException != null)
                    {
                        if (e.InnerException is FaultException)
                            return new ReturnMessage(ErrorHandler.Instance.UnwrapAgentException(e.InnerException), msg as IMethodCallMessage);

                        if (e.InnerException is EndpointNotFoundException || e.InnerException is TimeoutException)
                            channel = GetChannel(true);
                    }
                    retryCount--;
                }
            } while (retryCount > 0);

            throw new Exception("Retrycount reached maximum. Customproxy Invoke");
        }

5 客户端令牌将由客户端每30分钟刷新一次

private async Task RefreshToken(LoginTypeBase loginType)
        {
            if (_MyAppToken == null)
                return;
            var tokenClient = await GetTokenClient(loginType);
            var result = !string.IsNullOrEmpty(_refreshToken) ? await tokenClient.RequestRefreshTokenAsync(_refreshToken, _cancelToken.Token) : await tokenClient.RequestCustomGrantAsync("siths", cancellationToken: _cancelToken.Token);
            if (string.IsNullOrEmpty(result.AccessToken))
                throw new Exception($"Accesstoken har blivit null försökte refresha med {tokenClient.ClientId} {_refreshToken} {DateTime.Now}");
            _MyAppToken.Token = result.AccessToken;
            _refreshToken = result.RefreshToken;
        }

这一切在开始时效果很好但是在随机调用之后它会失败,有时候它无法联系的WCF服务,此时IdentityService仍然可以与之通信。在上面第4点所示的代码中引发了异常,如下所示:

  

e.ToString()&#34; System.Reflection.TargetInvocationException:Exception   已经被调用的目标所抛出。 ---&GT;   System.ServiceModel.Security.SecurityNegotiationException:无法   为权威人士建立SSL / TLS安全通道   &#39; 139.107.245.141:44310&#39 ;. ---&GT; System.Net.WebException:请求是   aborted:无法创建SSL / TLS安全通道。\ r \ n at   System.Net.HttpWebRequest.GetResponse()\ r \ n at   System.ServiceModel.Channels.HttpChannelFactory 1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)\r\n --- End of inner exception stack trace ---\r\n\r\nServer stack trace: \r\n at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)\r\n at System.ServiceModel.Channels.HttpChannelFactory 1.HttpRequestChannel.HttpChannelRequest.WaitForReply(时间跨度   超时)\ r \ n at   System.ServiceModel.Channels.RequestChannel.Request(消息消息,   TimeSpan超时)\ r \ n at   System.ServiceModel.Dispatcher.RequestChannelBinder.Request(消息   消息,TimeSpan超时)\ r \ n at   System.ServiceModel.Channels.ServiceChannel.Call(String action,   Boolean oneway,ProxyOperationRuntime操作,Object [] ins,   对象[]输出,TimeSpan超时)\ r \ n at   System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage   methodCall,ProxyOperationRuntime operation)\ r \ n at   System.ServiceModel.Channels.ServiceChannelProxy.Invoke(即时聊天   消息)\ r \ n \ r \ n在[0]处重新抛出异常:\ r \ n at   System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(即时聊天   reqMsg,IMessage retMsg)\ r \ n at   System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&安培;   msgData,Int32类型)\ r \ n at   myapp.ServiceContracts.ImyappClientService.LogData(GeneralFault   generalFault)\ r \ n ---内部异常堆栈跟踪结束--- \ r \ n
  在System.RuntimeMethodHandle.InvokeMethod(对象目标,对象[]   arguments,Signature sig,Boolean constructor)\ r \ n at   System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj,   Object []参数,Object []参数)\ r \ n at   System.Reflection.RuntimeMethodInfo.Invoke(Object obj,BindingFlags   invokeAttr,Binder binder,Object []参数,CultureInfo   文化)\ r \ n在System.Reflection.MethodBase.Invoke(Object obj,   对象[]参数)\ r \ n at   myapp.Client.Main.Classes.Service_Management.CustomProxy`1.Invoke(即时聊天   msg)在C:\ myapp \ Produkter \ myapp中   Utveckling \解决方案\ myapp.Client.Main \类\服务   Management \ CustomProxy.cs:第74行&#34;串

有时它会首先使WCF服务失败,但之后再次工作,然后再次失败(exacly相同的vall),而当IdentityService4连接开始失败时,每次出现此异常都会失败:

  

token.Exception.ToString()&#34; System.Net.Http.HttpRequestException:An   发送请求时发生错误。 ---&GT;   System.Net.WebException:请求已中止:无法创建   SSL / TLS安全通道。\ r \ n at   System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult的   asyncResult,TransportContext&amp;上下文)\ r \ n at   System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult的   ar)\ r \ n ---内部异常堆栈跟踪结束--- \ r \ n at   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务   任务)\ r \ n at   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务   任务)\ r \ n at   System.Net.Http.HttpClient.d__58.MoveNext(个)\ r \ n ---   从抛出异常的先前位置开始的堆栈跟踪结束   --- \ r \ n在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务   任务)\ r \ n at   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务   任务)\ r \ n at   IdentityModel.Client.TokenClient.d__26.MoveNext()&#34;串

此代码用于在客户端

中测试IdentityService连接

它是令牌。将保留上述例外的例外。

public static async Task LoginSiths()         {             尝试             {                 var host =&#34; 139.107.245.141&#34 ;;                 var port = 44312;                 var url = $&#34; https:// {host}:{port} / connect / token&#34 ;;

            var handler = new WebRequestHandler();

            handler.ServerCertificateValidationCallback = PinPublicKey;
            handler.ClientCertificates.Add(SmartCardHandler.GetInstance().Result.CurrentCertificate());

            var client = new TokenClient(url, ClientTypes.Siths, "secret.ro101.orbit", handler);
            TokenResponse token = await client.RequestCustomGrantAsync(ClientTypes.Siths, "orbit.wcf offline_access");

            if (token.IsError)
                MessageBox.Show("Failed!");
            else
                MessageBox.Show("Success!");
        }
        catch (Exception ex)
        {
            MessageBox.Show("Exception : " + ex.ToString());
        }
    }

唯一的出路是重新启动客户端(不需要重新启动任何服务)。重新启动客户端后,它可以像以前一样登录并进行呼叫。

如果我将其添加到客户的开头:

 ServicePointManager.MaxServicePointIdleTime = 1;

然后我会在登录后几乎立即得到问题(此时变化很慢)。

有没有办法知道我为什么会收到SSL异常?

  

注意,这篇文章已经更新了2017-04-07 01:00,这是为了更详细地了解正在发生的事情。这个问题确实存在   两个问题,一个仍然是活跃的,并在上面解释,另一个   由于bug in IdentityService4我们找到了解决方法   对

1 个答案:

答案 0 :(得分:0)

如果您的问题出在客户端,我建议您使用AppDomain隔离ServicePointManager而不是一些反射黑客(这会伤害我的眼睛:) - 类似于此{{3 }}

I.E。:

public class Client : MarshalByRefObject {
   public string GetResponse(string address) {
     // you can get crazy with ServicePointManager settings here
     // + actually do the request
   }
}

public sealed class Isolated<T> : IDisposable where T : MarshalByRefObject {
   private AppDomain _domain;

   public Isolated() {
     _domain = AppDomain.CreateDomain("Isolated:" + Guid.NewGuid(), null,
       AppDomain.CurrentDomain.SetupInformation);
     var type = typeof(T);
     Value = (T)_domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
   }

   public T Value { get; private set; }

   public void Dispose() {
     if (_domain == null) return;
     AppDomain.Unload(_domain);
    _domain = null;
   }
}    

将通信包含在以下内容中:

using (var isolated = new Isolated<Client>()) {
   string response = isolated.Value.GetResponse(url);
}