[英]WCF application with client certificate fails after a while?
我有一个看起来像这样的解决方案:
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()“System.Reflection.TargetInvocationException:调用目标抛出了异常.---> System.ServiceModel.Security.SecurityNegotiationException:无法为具有权限的SSL / TLS建立安全通道'139.107.245.141 :44310'.---> System.Net.WebException:请求已中止:无法在System.ServiceModel的System.Net.HttpWebRequest.GetResponse()\\ r \\ n中创建SSL / TLS安全通道。\\ r \\ n .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 timeout)\\ r \\ n在System.ServiceModel.Channels。 RequestChannel.Request(消息消息,TimeSpan超时) r \\ n at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(消息消息,TimeSpan超时)\\ r \\ n在System.ServiceModel.Channels.ServiceChannel.Call(String action,Boolean oneway,ProxyOperationRuntime operation,Object [] ins,Object []出,TimeSpan超时)\\ r \\ n在System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall,ProxyOperationRuntime操作)\\ r \\ n在System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\\ r \\ n \\ r \\ n在[0]处重新抛出:\\ r \\ n在System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg,IMessage retMsg)\\ r \\ n在System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke( MessageData&msgData,Int32 type)\\ r \\ n at myapp.ServiceContracts.ImyappClientService.LogData(GeneralFault generalFault)\\ r \\ n ---内部异常堆栈跟踪结束--- \\ r \\ n
at System.ReuntimeMethodHandle.InvokeMethod(Object target,Object [] arguments,Signature sig,Boolean constructor)\\ r \\ n在System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj,Object [] parameters,Object [] arguments)\\ r \\ n在System.Reflection.RuntimeMethodInfo.Invoke(Object obj,BindingFlags invokeAttr,Binder binder,Object []参数,CultureInfo文化)\\ r \\ n在System.Reflection.MethodBase.Invoke(Object obj,Object []参数)\\ r \\ n \\ n在C:\\ myapp \\ Produkter \\ myapp中的myapp.Client.Main.Classes.Service_Management.CustomProxy`1.Invoke(IMessage msg)Utveckling \\ Solution \\ myapp.Client.Main \\ Classes \\ Service Management \\ CustomProxy.cs:第74行“字符串
有时它会首先使WCF服务失败,然后再次工作,然后再次失败(exacly相同的vall),而当IdentityService4连接开始失败时,每次遇到此异常都会失败:
token.Exception.ToString()“System.Net.Http.HttpRequestException:发送请求时发生错误.---> System.Net.WebException:请求已中止:无法创建SSL / TLS安全通道。\\ r \\ n \\ n在System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext&context)\\ r \\ n在System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)\\ r \\ n ---内部异常堆栈跟踪结束 - - 位于System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)\\ r \\ n的System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\\ r \\ n在System.Net.Http.HttpClient上的\\ r \\ n。 d__58.MoveNext()\\ r \\ n ---从抛出异常的上一个位置开始的堆栈跟踪结束---在系统上的System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)\\ r \\ n中的\\ r \\ n .Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\\ r \\ n在IdentityModel.Client.TokenClient.d__26.MoveNext()“st 环
此代码用于在客户端中测试IdentityService连接
它是令牌。将保持上述异常的异常。
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());
}
}
唯一的出路是重新启动客户端(不需要重新启动任何服务)。 重新启动客户端后,它可以像以前一样再次登录并进行呼叫。
如果我将其添加到我的客户端的开头:
ServicePointManager.MaxServicePointIdleTime = 1;
然后我会在登录后几乎立即得到问题(此时变化缓慢)。
有没有办法知道我为什么会得到SSL异常?
注意 ,这篇文章已经更新2017-04-07 01:00,这是为了更多地收集正在发生的事情。 问题实际上包含两个问题,一个仍然是活动的,如上所述,另一个是由于IdentityService4中的一个错误 ,我们找到了解决方法。
如果您的问题出在客户端,我建议您使用ServicePointManager
AppDomain
隔离而不是一些反射黑客(这会伤害我的眼睛:) - 类似于这种.NET https请求,跨线程使用不同的安全协议
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;
}
}
并将沟通包含在以下内容中:
using (var isolated = new Isolated<Client>()) {
string response = isolated.Value.GetResponse(url);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.