Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Could not establish trust relationship for the SSL/TLS secure channel with authority #2499

Closed
alextochetto opened this issue Jan 15, 2018 · 25 comments
Assignees
Labels
needs more info Need more info in order to reproduce the issue.

Comments

@alextochetto
Copy link

I'm trying to use a digital certificate already installed in my PC.
I'm using ASPNET CORE 2.0, Framework 4.7 and VS 2017 15.5.3
This only occurs because the certificate is not valid: NET::ERR_CERT_AUTHORITY_INVALID

In my winform application, this works using this:
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

@zhenlan zhenlan added this to the S130 milestone Jan 19, 2018
@mconnew
Copy link
Member

mconnew commented Jan 20, 2018

Will this work:

BasicHttpsBinding binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
endpointAddress = new EndpointAddress(new Uri("http://myserver/MyService.svc"));
factory = new ChannelFactory<IService>(binding, endpointAddress);
factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication();
factory.Credentials.ServiceCertificate.SslCertificateAuthentication.CertificateValidationMode = X509CertificateValidationMode.None;

@alextochetto
Copy link
Author

Thanks a lot @mconnew
It worked!!
It was necessary only disable the SslCertificateAuthentication
Regards

@mconnew mconnew closed this as completed Jan 25, 2018
@Etrimus
Copy link

Etrimus commented Jun 7, 2018

Hello, I have same problem, but solution from @mconnew doesn't work for me.

var client = new PartnerApiClient { ClientCredentials = { UserName = { UserName = "testapi", Password = "dfdfTT7" } } };
client.ChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication
            {
                CertificateValidationMode = X509CertificateValidationMode.None,
                RevocationMode = X509RevocationMode.NoCheck
            };
client.ChannelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
await client.GetTariffPlanListAsync() // Could not establish trust relationship for the SSL/TLS secure channel with authority 11.22.33.44:5555

A PartnerApiClient is auto-generated class derived from System.ServiceModel.ClientBase<>

#region Assembly System.ServiceModel.Primitives, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Users\Artem\.nuget\packages\system.servicemodel.primitives\4.4.1\ref\netstandard2.0\System.ServiceModel.Primitives.dll
#endregion

In classic .net 4.7 this code works fine with ServerCertificateValidationCallback

System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;

@gcelet
Copy link

gcelet commented Jun 14, 2018

Hello, it seems that I encounter the same problem calling a SOAP service with self certificate from an asp.net core 2.1 webapi running in .net core 2.1:
System.ServiceModel.Security.SecurityNegotiationException: Could not establish trust relationship for the SSL/TLS secure channel with authority

In my case, the fix given by @mconnew work with version 4.4.2 of System.ServiceModel.Primitives and System.ServiceModel.Http but fail with version 4.5.0 (just changing nuget package version no code modification).

@mconnew
Copy link
Member

mconnew commented Jun 28, 2018

@Etrimus, you are using ChannelFactory.Credentials.ServiceCertificate.Authentication and my example is using ChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication. Change your code to match my example and it should work for you,

@gcelet, we have extensive tests around this area so if something broke in our code, we would see it in our test results. I don't suppose you are running on OSX?

@gcelet
Copy link

gcelet commented Jul 4, 2018

@mconnew No our project is developped on Windows and run in Docker on linux in production.
I see there a new version 4.5.1 of System.ServiceModel.Primitives and System.ServiceModel.Http.
I'll give this version a try to see if the problem still exists.

@gcelet
Copy link

gcelet commented Jul 4, 2018

I still get the problem with version 4.5.1 of System.ServiceModel.Primitives and System.ServiceModel.Http.
I can't share the WCF contract but this test below show the problem i'm facing:

  • it's ok with version 4.4.2
  • throw a System.ServiceModel.Security.SecurityNegotiationException with version 4.5.1
[Test]
public async Task Should_Not_Throw_On_No_Trust_Domain()
{
	BasicHttpBinding binding = new BasicHttpBinding
	{
		CloseTimeout = TimeSpan.Parse("00:10:00"),
		OpenTimeout = TimeSpan.Parse("00:10:00"),
		ReceiveTimeout = TimeSpan.Parse("00:10:00"),
		SendTimeout = TimeSpan.Parse("00:10:00"),
		MaxBufferPoolSize = 90000000,
		MaxBufferSize = 90000000,
		MaxReceivedMessageSize = 90000000,
		ReaderQuotas = new XmlDictionaryReaderQuotas
		{
			MaxDepth = 32,
			MaxArrayLength = 90000000,
			MaxStringContentLength = 90000000
		},
	};
	binding.Security.Mode = BasicHttpSecurityMode.Transport;
	binding.Security.Transport = new HttpTransportSecurity
	{
		ClientCredentialType = HttpClientCredentialType.None,
		ProxyCredentialType = HttpProxyCredentialType.Basic
	};
	EndpointAddress remoteAddress = new EndpointAddress("https://mydomain.with.no.trust.com/Service.scv");
	ServiceWCF client = new ServiceWCF(binding, remoteAddress);
	
	client.ChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication =
		client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication =
			new X509ServiceCertificateAuthentication
			{
				CertificateValidationMode = X509CertificateValidationMode.None,
				RevocationMode = X509RevocationMode.NoCheck,
				TrustedStoreLocation = StoreLocation.LocalMachine
			};
	
	ServiceResultat serviceResultat;
	
	try
	{
		ServiceResultat = 
			await client.callService().ConfigureAwait(false);

		client.Close();
	}
	catch (CommunicationException)
	{
		client.Abort();
		throw;
	}
	catch (TimeoutException)
	{
		client.Abort();
		throw;
	}
	catch (Exception)
	{
		client.Abort();
		throw;
	}
	
	Assert.IsNotNull(serviceResultat);
}
System.ServiceModel.Security.SecurityNegotiationException : Could not establish trust relationship for the SSL/TLS secure channel with authority 'mydomain.with.no.trust.com'.
  ----> System.Net.Http.HttpRequestException : The SSL connection could not be established, see inner exception.
  ----> System.Security.Authentication.AuthenticationException : Authentication failed, see inner exception.
  ----> System.ComponentModel.Win32Exception : The token supplied to the function is invalid.
   at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass1_0.<CreateGenericTask>b__0(IAsyncResult asyncResult)
--- End of stack trace from previous location where exception was thrown ---
...

@flavio1110
Copy link

Same issue here, I had to downgrade to 4.4.2 in order to disable the validation.

@Tonay
Copy link

Tonay commented Apr 24, 2019

Will this work:

BasicHttpsBinding binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
endpointAddress = new EndpointAddress(new Uri("http://myserver/MyService.svc"));
factory = new ChannelFactory<IService>(binding, endpointAddress);
factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication();
factory.Credentials.ServiceCertificate.SslCertificateAuthentication.CertificateValidationMode = X509CertificateValidationMode.None;

Thank you so much man! This is work fine for me :)

@thiagosrios
Copy link

Will this work:

BasicHttpsBinding binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
endpointAddress = new EndpointAddress(new Uri("http://myserver/MyService.svc"));
factory = new ChannelFactory<IService>(binding, endpointAddress);
factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication();
factory.Credentials.ServiceCertificate.SslCertificateAuthentication.CertificateValidationMode = X509CertificateValidationMode.None;

Thanks. This work for me.

@mconnew
Copy link
Member

mconnew commented Jun 19, 2019

The only difference that I can see if using BasicHttpsBinding instead of BasicHttpBinding. Can you verify if using BasicHttpBinding instead is broken? I think this works on .NET Framework but WCF has so many knobs and options I could be wrong.

@gcelet
Copy link

gcelet commented Jun 21, 2019

Hi,

I changed my unit test above from BasicHttpBinding to BasicHttpsBinding:

  • test failed using version 4.5.3 with both BasicHttpBinding and BasicHttpsBinding
  • test passed using version 4.4.2 with both BasicHttpBinding and BasicHttpsBinding

These tests runned on .NET Core using sdk 2.1.700.

@mconnew
Copy link
Member

mconnew commented Jul 9, 2019

Re-opening issue.
@gcelet, can you try with 4.5.3 configuring not to use SocketsHttpHandler. It can be done with the following line at the start of your app:

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

Also, what OS are you using?

@mconnew mconnew reopened this Jul 9, 2019
@StephenBonikowsky StephenBonikowsky added the needs more info Need more info in order to reproduce the issue. label Jul 10, 2019
@gcelet
Copy link

gcelet commented Jul 15, 2019

Hi,

Sorry for my late response: i was busy at work.

I can confirm that my unit test pass with 4.5.3 configured to not use SocketsHttpHandler: without it it still failed.

I run this test on .net core 2.1 with sdk 2.1.701 on windows 10 pro 1809.

Hope this helps

@StephenBonikowsky StephenBonikowsky modified the milestones: S130, Investigation Jul 22, 2019
@StephenBonikowsky
Copy link
Member

@gcelet This looks like a bug in CoreFx, in order for us to open an issue in their repo we need a little more data from you if possible.

  • A full call stack
  • The public cert from the service you are trying to connect to

Could you get us that?

@gcelet
Copy link

gcelet commented Jul 26, 2019

Hi,

I worked yesterday on repro but it's seems difficult.

I can't post the certificate publicly on github. How can i send you this information privately ?

The certificate looks like this:

$ openssl x509 -in certificate.crt -text -noout
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 0 (0x0)
        Signature Algorithm: md5WithRSAEncryption
        Issuer: CN = *.subdomain.wcfbug2499.local
        Validity
            Not Before: Jun 22 10:23:42 2009 GMT
            Not After : Jun 20 10:23:42 2019 GMT
        Subject: CN = *.subdomain.wcfbug2499.local
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (1024 bit)
                Modulus:
                    00:b7:f9:b4:6d:9d:13:01:a2:5a:f8:64:c2:9d:49:
                    f0:40:d6:55:a3:28:4c:3b:70:9d:64:fb:87:5a:1e:
                    51:5a:e6:08:eb:dd:91:aa:a2:94:50:bf:e3:6b:be:
                    d9:bf:75:8f:ce:9a:1f:d5:de:6b:20:60:58:74:29:
                    bd:56:76:b2:cf:bf:34:2b:22:ab:5e:46:78:74:0f:
                    22:d3:0c:a8:02:c0:c0:d2:84:bb:1f:68:5a:69:23:
                    f7:3b:73:d0:c3:9b:2c:9b:cd:21:f3:24:2c:d0:7f:
                    05:11:38:48:9f:77:18:0d:06:5d:70:7c:6d:85:b9:
                    3e:8d:81:5d:9b:8d:1a:1d:a5
                Exponent: 65537 (0x10001)
    Signature Algorithm: md5WithRSAEncryption
         4c:12:f8:50:ec:ec:36:f4:fb:45:f3:53:19:7a:ad:49:63:c6:
         e2:09:d0:41:ec:71:74:b0:88:a2:39:51:4c:8a:8a:4d:2f:5f:
         49:d2:4c:ed:23:a8:05:e6:e3:45:6b:a4:da:5e:75:9f:95:74:
         82:65:55:09:fb:30:9b:b6:7b:c0:70:79:53:1b:17:5f:d4:8e:
         db:dc:15:0e:70:52:35:88:3d:9c:e5:bb:80:b9:16:22:5e:9e:
         2f:9a:e0:c6:3c:ea:e5:9b:7e:c7:de:66:fd:a9:fb:25:f3:b1:
         70:d5:fb:b9:14:6b:16:0e:29:7a:7c:b8:bd:ce:fc:dd:d9:fa:
         98:2b

This is not the real certificate: it's was made up and it's likely not working but may be it will help.

This certificate expired in june 2019 but the problem happened before the expiration. And it's working as expected when not using SocketsHttpHandler.

This is the unit test i use to show the problem:

[Test]
public void Should_Not_Throw_On_No_Trust_Domain()
{
    //AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

    BasicHttpBinding binding = new BasicHttpBinding
    {
        CloseTimeout = TimeSpan.Parse("00:10:00"),
        OpenTimeout = TimeSpan.Parse("00:10:00"),
        ReceiveTimeout = TimeSpan.Parse("00:10:00"),
        SendTimeout = TimeSpan.Parse("00:10:00"),
        MaxBufferPoolSize = 90000000,
        MaxBufferSize = 90000000,
        MaxReceivedMessageSize = 90000000,
        ReaderQuotas = new XmlDictionaryReaderQuotas
        {
            MaxDepth = 32,
            MaxArrayLength = 90000000,
            MaxStringContentLength = 90000000
        },
    };
    binding.Security.Mode = BasicHttpSecurityMode.Transport;
    binding.Security.Transport = new HttpTransportSecurity
    {
        ClientCredentialType = HttpClientCredentialType.None,
        ProxyCredentialType = HttpProxyCredentialType.Basic
    };
    EndpointAddress remoteAddress = new EndpointAddress("https://abc.subdomain.wcfbug2499.local/path-to-service");
    WcfServiceClient client = new WcfServiceClient(binding, remoteAddress);

    client.ChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication =
        client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication =
            new X509ServiceCertificateAuthentication
            {
                CertificateValidationMode = X509CertificateValidationMode.None,
                RevocationMode = X509RevocationMode.NoCheck,
                TrustedStoreLocation = StoreLocation.LocalMachine
            };

    try
    {
        string serviceResultat = client.DemoClient();

        ((ICommunicationObject) client).Close();
        
        Assert.IsNotNull(serviceResultat);
    }
    catch (CommunicationException)
    {
        client.Abort();
        throw;
    }
    catch (TimeoutException)
    {
        client.Abort();
        throw;
    }
    catch (Exception)
    {
        client.Abort();
        throw;
    }
}

[ServiceContract]
public interface IWcfService
{
    [OperationContract]
    string DemoClient();
}

public class WcfServiceClient : System.ServiceModel.ClientBase<IWcfService>, IWcfService
{

    public WcfServiceClient()
    {
    }

    public WcfServiceClient(string endpointConfigurationName) :
        base(endpointConfigurationName)
    {
    }

    public WcfServiceClient(string endpointConfigurationName, string remoteAddress) :
        base(endpointConfigurationName, remoteAddress)
    {
    }

    public WcfServiceClient(string endpointConfigurationName,
        System.ServiceModel.EndpointAddress remoteAddress) :
        base(endpointConfigurationName, remoteAddress)
    {
    }

    public WcfServiceClient(System.ServiceModel.Channels.Binding binding,
        System.ServiceModel.EndpointAddress remoteAddress) :
        base(binding, remoteAddress)
    {
    }

    /// <inheritdoc />
    public string DemoClient()
    {
        return base.Channel.DemoClient();
    }
}

It's worked when the switch System.Net.Http.UseSocketsHttpHandler have the value false.

It's produced the following stack strace when the switch System.Net.Http.UseSocketsHttpHandler have the value true:

System.ServiceModel.Security.SecurityNegotiationException : Could not establish trust relationship for the SSL/TLS secure channel with authority 'abc.subdomain.wcfbug2499.local'.
  ----> System.Net.Http.HttpRequestException : The SSL connection could not be established, see inner exception.
  ----> System.Security.Authentication.AuthenticationException : Authentication failed, see inner exception.
  ----> System.ComponentModel.Win32Exception : Le jeton fourni à la fonction n’est pas valide
   at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(HttpRequestException requestException, HttpRequestMessage request, HttpAbortReason abortReason)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.SendRequestAsync(Message message, TimeoutHelper timeoutHelper)
   at System.ServiceModel.Channels.RequestChannel.RequestAsync(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.RequestAsyncInternal(Message message, TimeSpan timeout)
   at System.Runtime.TaskHelpers.WaitForCompletionNoSpin[TResult](Task`1 task)
   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(MethodCall methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(MethodInfo targetMethod, Object[] args)
--- End of stack trace from previous location where exception was thrown ---
   at System.Reflection.DispatchProxyGenerator.Invoke(Object[] args)
   at generatedProxy_1.DemoClient()
   at WcfBug2499.WcfServiceClient.DemoClient() in C:\dev\sandbox\WcfBug2499\WcfBug2499.Tests\WcfBug2499UsingLocalDomainTests.cs:line 121
   at WcfBug2499.WcfBug2499UsingLocalDomainTests.Should_Not_Throw_On_No_Trust_Domain() in C:\dev\sandbox\WcfBug2499\WcfBug2499.Tests\WcfBug2499UsingLocalDomainTests.cs:line 56
--HttpRequestException
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask`1 creationTask)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.SendRequestAsync(Message message, TimeoutHelper timeoutHelper)
--AuthenticationException
   at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo 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.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
   at System.Net.Security.SslState.ThrowIfExceptional()
   at System.Net.Security.SslState.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslState.EndProcessAuthentication(IAsyncResult result)
   at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
   at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__47_1(IAsyncResult iar)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
--Win32Exception

@StephenBonikowsky
Copy link
Member

Thanks @gcelet
@mconnew Can you work with this?

@mconnew
Copy link
Member

mconnew commented Aug 13, 2019

Sorry for the slow response, I was on vacation then out sick. There's one more bit of information which I think would be really useful. There's 4 exceptions nesting each other. On the very inner exception, the Win32Exception, can you tell me what the values are of the NativeErrorCode, ErrorCode and HResult properties. They are likely the same, but it's possible they have different values. You can send the public cert of the server to me at my username at microsoft.com. Please make sure you only send me the public part of the certificate and not the private key.
If that's not enough information to work out what's going on, there's some scripts in the corefx repo for collecting really detailed logs about the connection, but I'll probably pass the issue to the networking team if that's needed.

@gcelet
Copy link

gcelet commented Aug 14, 2019

Hi,

Details of Win32Exception are:

  • NativeErrorCode: -2146893048
  • ErrorCode: -2147467259
  • HResult: -2147467259

Hope this helps but these codes seems really generic ones.

I will send you the public certificate later today (i'm in France).

@mconnew
Copy link
Member

mconnew commented Aug 15, 2019

Error code -2146893048 corresponds to SEC_E_INVALID_TOKEN which has the description "The token supplied to the function is invalid" which I think is what the french in your call stack translates to anyway. I've received your email and I have an answer for you.

The server certificate is using really old standards. The signature hash algorithm is md5 which can't be used with the TLS1.2 protocol. The only reason things are working on .NET Framework is the supported SSL protocols doesn't include TLS1.2. This is probably because your application is targeting an older version of the .NET Framework, or you are running on Windows 7.

In order to connect to that web service, you will need to downgrade the highest TLS version being used to TLS1.1. WCF doesn't expose an api for doing this directly, but we do have the ability for you to provide a delegate to modify the HttpClientHandler instance that we use. This requires adding a behavior to your channel factory. A full example of a behavior which does what you need can be found in my comment on this issue.

@mconnew
Copy link
Member

mconnew commented Aug 15, 2019

Oh, and there's another issue you are likely to run into. There's a bug where we don't apply the ProxyCredentialType value to the underlying HttpsTransportBindingElement. It's been fixed and will be in a future release. Details can be found in issue #3551 along with the clunky workaround that someone provided.

@gcelet
Copy link

gcelet commented Aug 15, 2019

@mconnew Thanks for your quick response.

I'm not sure to understand your answer.

I don't use .NET Framework in this test: it runs on .NET Core 2.1.12 (only LTS version of .NET Core).
There is not proxy use.

I develop on Windows 10 Pro 1809 and run this code in production inside kubernetes on linux.

If i upgrade Nuget packages System.ServiceModel.Primitives and System.ServiceModel.Http to version 4.5.3, this test fail.

This test passed when using version 4.4.2 of Nuget packages System.ServiceModel.Primitives and System.ServiceModel.Http.

I understand that this certificate is using old security standards but i need to be able to call it. It seems that i cannot bypassed the certificate check anymore.

The only way i found is to stay on version 4.4.2 of nuget packages System.ServiceModel.Primitives and System.ServiceModel.Http.

I tried the other workarounds in this thread but none of them work except disabling "System.Net.Http.UseSocketsHttpHandler" using the context flag.

@mconnew
Copy link
Member

mconnew commented Aug 15, 2019

Sorry, I got confused by the history as someone else opened the issue originally and their code ran fine on .NET Framework. You can disregard that.

Up until WCF 4.4.2, we explicitly instantiated an instance of the Curl based HTTP handler as the generic HttpClientHandler didn't expose sufficient properties for everything we needed to set. From 4.5, we instantiate HttpClientHandler instance as they have now added sufficient API's to HttpClientHandler. This means when using 4.4.2, you end up using the Linux platform specific libcurl based HTTP handler but when using 4.5, you use whatever HttpClientHandler is configured to use, which defaults to SocketsHttpHandler. The app setting reverts the behavior of HttpClientHandler to use the platform specific version which for Linux is based on libcurl.

Curl is going to have different behavior around which versions of TLS to support. SocketsHttpHandler supports TLS1.2 by default. Curl must be negotiating a lower version of TLS than SocketsHttpHandler which is why everything is working.

In the comment I linked to, there is a behavior you can add to ChannelFactory which allows configuring the HttpClientHandler that WCF instantiates (which is wrapping SocketsHttpHandler) so you can tell it to only use SslProtocols.Tls11. The old certificate is incompatible with TLS 1.2 which is why the request is failing. The Curl based handler (as is always used by WCF in 4.4.2) must be using a version of TLS <= 1.1. The behavior has been written to specifically configure SslProtocols so you can just set a property on the behavior itself.

@gcelet
Copy link

gcelet commented Sep 9, 2019

Hi,

Sorry for my late response: i just returned from vacation.

Thank you for response: it explained clearly the problem.

I can confirm that using your endpoint behavior SslProtocolCertificateEndpointBehavior to force SslProtocols.Tls11 it worked as expected.

Thanks for your help on this issue.

For posterity sake, here the complete unit test:

    public class WcfBug2499UsingLocalDomainTests
    {
        [Test]
        public void Should_Not_Throw_On_No_Trust_Domain()
        {
            //AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

            BasicHttpBinding binding = new BasicHttpBinding
            {
                CloseTimeout = TimeSpan.Parse("00:10:00"),
                OpenTimeout = TimeSpan.Parse("00:10:00"),
                ReceiveTimeout = TimeSpan.Parse("00:10:00"),
                SendTimeout = TimeSpan.Parse("00:10:00"),
                MaxBufferPoolSize = 90000000,
                MaxBufferSize = 90000000,
                MaxReceivedMessageSize = 90000000,
                ReaderQuotas = new XmlDictionaryReaderQuotas
                {
                    MaxDepth = 32,
                    MaxArrayLength = 90000000,
                    MaxStringContentLength = 90000000
                },
            };
            binding.Security.Mode = BasicHttpSecurityMode.Transport;
            binding.Security.Transport = new HttpTransportSecurity
            {
                ClientCredentialType = HttpClientCredentialType.None,
                ProxyCredentialType = HttpProxyCredentialType.Basic
            };
            EndpointAddress remoteAddress = new EndpointAddress("https://abc.subdomain.wcfbug2499.local");
            WcfServiceClient client = new WcfServiceClient(binding, remoteAddress)
            {
                SslProtocols = SslProtocols.Tls11
            };

            client.ChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication =
                client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication =
                    new X509ServiceCertificateAuthentication
                    {
                        CertificateValidationMode = X509CertificateValidationMode.None,
                        RevocationMode = X509RevocationMode.NoCheck,
                        TrustedStoreLocation = StoreLocation.LocalMachine
                    };

            try
            {
                string serviceResultat = client.DemoClient();

                ((ICommunicationObject) client).Close();

                Assert.IsNotNull(serviceResultat);
            }
            catch (CommunicationException communicationException)
            {
                client.Abort();
                throw;
            }
            catch (TimeoutException timeoutException)
            {
                client.Abort();
                throw;
            }
            catch (Exception exception)
            {
                client.Abort();
                throw;
            }
        }
    }

    [ServiceContract]
    public interface IWcfService
    {
        [OperationContract]
        string DemoClient();
    }

    public class WcfServiceClient : System.ServiceModel.ClientBase<IWcfService>, IWcfService
    {
        public WcfServiceClient()
        {
        }

        public WcfServiceClient(string endpointConfigurationName) :
            base(endpointConfigurationName)
        {
        }

        public WcfServiceClient(string endpointConfigurationName, string remoteAddress) :
            base(endpointConfigurationName, remoteAddress)
        {
        }

        public WcfServiceClient(string endpointConfigurationName,
            System.ServiceModel.EndpointAddress remoteAddress) :
            base(endpointConfigurationName, remoteAddress)
        {
        }

        public WcfServiceClient(System.ServiceModel.Channels.Binding binding,
            System.ServiceModel.EndpointAddress remoteAddress) :
            base(binding, remoteAddress)
        {
        }

        public SslProtocols SslProtocols
        {
            get
            {
                var behavior = Endpoint.EndpointBehaviors.OfType<SslProtocolCertificateEndpointBehavior>().FirstOrDefault();
                if (behavior != null)
                {
                    return behavior.SslProtocols;
                }

                return SslProtocols.Tls12;
            }

            set
            {
                var behavior = Endpoint.EndpointBehaviors.OfType<SslProtocolCertificateEndpointBehavior>().FirstOrDefault();
                if (behavior == null)
                {
                    behavior = new SslProtocolCertificateEndpointBehavior();
                    Endpoint.EndpointBehaviors.Add(behavior);
                }

                behavior.SslProtocols = value;
            }
        }

        /// <inheritdoc />
        public string DemoClient()
        {
            return base.Channel.DemoClient();
        }
    }

    public class SslProtocolCertificateEndpointBehavior : IEndpointBehavior
    {
        public SslProtocols SslProtocols { get; set; } = SslProtocols.Tls12;

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            bindingParameters.Add(new Func<HttpClientHandler, HttpMessageHandler>(x =>
            {
                x.SslProtocols = SslProtocols;
                return x; // You can just return the modified HttpClientHandler
            }));
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }

@StephenBonikowsky
Copy link
Member

@gcelet Glad it worked for you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs more info Need more info in order to reproduce the issue.
Projects
None yet
Development

No branches or pull requests

9 participants