WCF application with client certificate not working after some time?

I have a solution that looks like this:

  • Winform WCF (client)
  • IdentityService4 self-hosted in Windows service
  • The WCF HTTPS service is hosted on a Windows service by itself.

Both IdentitySErvice4 and the WCF service use a function certificate. The client uses the client certificate downloaded from the Windows store. Customer service is installed on the Windows Store using a smart card with third-party software. If the card is removed, the certificate is deleted from the store.

The stream is as follows:

1 The client downloads the certificate from the store and binds it to the accepted manager as follows:

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. The client sends a request to IdentityServices, which reads the client’s certificate and uses it to authenticate and generate the client token that is returned.

3. The client creates a WCF channel for the service using the client certificate, the token is also attached to this channel, like this

 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. The client sends the first message to the WCF service. Third-party software will respond and require a PIN for the client certificate, when it is provided, the message is sent to the WCF service, where the token is checked for IdentityService4.

A real proxy is used so that you can switch to another service if necessary. In this case, it only has the URL of one WCF online service.

  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 Client token will be updated every 30 minutes by the client

 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; } 

All this works fine at the beginning, but after a random number of calls, it will fail, sometimes its WCF service, which it cannot communicate with, and at the moment, IdentityService can still communicate. An exception is thrown in the code shown in paragraph 4 above and looks like this:

e.ToString () "System.Reflection.TargetInvocationException: The exception was thrown at the target of the call. ---> System.ServiceModel.Security.SecurityNegotiationException: Failed to establish a secure channel for SSL / TLS with authority '139.107.245.141:44310. - -> System.Net.WebException: the request was aborted: Could not create a secure SSL / TLS channel. System.Net.HttpWebRequest.GetResponse () \ r \ n on 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 (TimeSpan t timeout) on System.ServiceModel.Channels.RequestChannel.Request (message message, TimeSpan timeout) \ r \ n System.ServiceModel.Dispatcher.RequestChannelBinder.Request (Message message, TimeSpan timeout) \ r \ n on System.ServiceModel.Channels.ServiceChannel.Call (String action, Boolean oneway, Operation ProxyOperationRuntime, Object [] ins, Object [] Outs, TimeSpan timeout) \ r \ n on System.ServiceModel.Channels.ServiceChannelProxy.InvokeService (IMethodCallMer method Call, ProxyOperationRuntime) \ r \ n at System.ServiceModel.Channels.ServiceChannelProxy.Invoke (Message to me) \ r \ n \ r \ nException thrown at [0]: \ r \ n at System.Runtime.Remoting.Proxies. RealProxy.HandleReturnMessage (iMessage reqMsg, IMessage retMsg) \ r \ n in System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke (MessageData & msgData, Int32 type) \ r \ n on myapp.ServiceContracts.ImyappClientService alFault generalFault) \ r \ n --- The end of the internal trace of the exception stack - \ r \ n
in System.RuntimeMethodHandle.InvokeMethod (Object target, Object [] arguments, Signature sig, Boolean constructor) \ r \ n at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal (Object obj, Object [], Object [] arguments) \ r \ n at System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeAttr, binder, parameters Object [], CultureInfo culture) \ r \ n in System.Reflection.MethodBase.Invoke (Object obj, Object []) \ r \ n in myapp.Client.Main.Classes.Service_Management.CustomProxy`1.Invoke (iMessage msg) in C: \ myapp \ Produkter \ myapp Utveckling \ Solution \ myapp.Client.Main \ Classes \ Service Management \ CustomProxy.cs: line 74 " line

Several times, it will first turn off the WCF service, and then it will work again, and then it will work again (exacly the same vall), and when the IdentityService4 connection starts to fail, it will fail every time with this exception:

token.Exception.ToString () "System.Net.Http.HttpRequestException: an error occurred while sending the request. ---> System.Net.WebException: the request was aborted: the Secure SSL / TLS channel could not be created. System.Net.HttpWebRequest .EndGetRequestStream (IAsyncResult asyncResult, TransportContext & context) \ r \ n in System.Net.Http.HttpClientHandler.GetRequestStreamCallback (IAsyncResult ar) \ r \ n --- The end of the internal exception stack trace is \ r \ n at System.Runt. CompilerServices.TaskAwaiter.ThrowForNonSuccess (Task task) \ r \ n in System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task task) \ r \ n in System.Net.Http.HttpClient.d__58.Move - The end of the stack trace from the previous place where the exception was thrown --- \ r \ n in System.Runtime.Compile rServices.TaskAwaiter.ThrowForNonSuccess (Task task) \ r \ n in System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task task) \ r \ n in IdentityModel.Client.TokenClient.d__26.MoveNext

This code is used to verify the IdentityService connection in the client.

This is a token. An exception that will contain the exception described above.

public static async Task LoginSiths () {try {var host = "139.107.245.141"; var port = 44312; var url = $ "https: // {host}: {port} / connect / token";

  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()); } } 

The only way out is to restart the client (none of the services need to be restarted). After restarting the client, it can log back in and make calls.

If I add this to the top of my client:

  ServicePointManager.MaxServicePointIdleTime = 1; 

then I will immediately get the problem right after logging in (this is slowly changing at the moment).

Is there any way to find out why I get an SSL exception?

Note , this post was updated 2017-04-07 01:00, this should give a more collected picture of what is happening. The problem really consisted of two problems, one is still active and explained above, the other was due to an error in IdentityService4 that we found a workaround for.

+7
c # ssl winforms wcf
source share
1 answer

If your problem is on the client side, I suggest you use AppDomain isolation for the ServicePointManager instead of some hackers (it hurts my eyes :) - similar to this is .NET https with various security protocols over the threads

IE:

 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; } } 

And attach the message to something like:

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

All Articles