0
votes

I'm attempting to port from 4.6.1 to ASP.NET Core 2.1.0-preview1. The following code throws an exception "Could not establish trust relationship for the SSL/TLS secure channel with authority '[service url]:447'

Am I applying the client cert incorrectly?

X509Certificate cert = X509Certificate2.CreateFromCertFile("C:\\mycert.cer");
X509Certificate2 cert2 = new X509Certificate2(cert);


var binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var endpoint = new EndpointAddress(new Uri("https://[service url]"));
var channelFactory = new ChannelFactory<APIWebService>(binding, endpoint);
channelFactory.Credentials.ClientCertificate.Certificate = cert2;
var serviceClient = channelFactory.CreateChannel();
var result = serviceClient.getBatchesSinceGUID(new getBatchesSinceGUIDRequest("-1"));
channelFactory.Close();

FACTS

  1. The X509Certificate is known to work with the 4.6.1 code; Allthough, the CORE version is using X509Certificate2
  2. In the 4.6.1 version, the APIWebService proxy was generated with wsdl.exe
  3. In the ASP.NET Core version, the APIWebService proxy was generated with svcutil.exe

EXCEPTION STACKTRACE

System.ServiceModel.Security.SecurityNegotiationException
  HResult=0x80131500
  Message=Could not establish trust relationship for the SSL/TLS secure channel with authority '[server url]:447'.
  Source=System.Private.ServiceModel
  StackTrace:
   at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(HttpRequestException requestException, HttpRequestMessage request, HttpAbortReason abortReason)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.<SendRequestAsync>d__13.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.ServiceModel.Channels.RequestChannel.<RequestAsync>d__33.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.ServiceModel.Channels.RequestChannel.<RequestAsyncInternal>d__32.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   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)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Reflection.DispatchProxyGenerator.Invoke(Object[] args)
   at generatedProxy_1.getDispatchBatchesSinceUID(getDispatchBatchesSinceUIDRequest )
   at NCPA.NADS.Test.UnitTest1.AdsDownload_Test() in UnitTest1.cs:line 26

Inner Exception 1:
HttpRequestException: An error occurred while sending the request.

Inner Exception 2:
WinHttpException: A security error occurred
1
Is the CA certificate that signed the service certificate in the trusted root certificate store?dodexahedron
@dodexahedron, yes it is. I can browse to the service url in IE and verify. Also, the 4.6.1 code does not have any issue with the certificate and service.John McCann
Can you provide the actual exception/stack trace?dodexahedron
Oh wait a sec. I just noticed you're giving it a .cer file. That's going to be just a public key. You can't do SSL with only a public key. You need the private key. You either need to load the certificate from the computer's certificate store or you need to provide a .pfx file to it, so it has the private key.dodexahedron
Would I be correct in guessing that the original code probably has the WCF client configured via the web.config file?dodexahedron

1 Answers

1
votes

Looks like it's probably because it's not being provided with its private key. When doing mutual certificate authentication, both parties need their public/private key pairs to establish a secure and properly authenticated channel.

In general, a .cer file typically contains only the certificate itself, which is the public key plus some metadata and the CA signature. The private key is held either in the certificate store (if it was generated/installed locally) or in a .pfx file (which will generally be either password-protected or permission-restricted).

If you do it with a .pfx file, rather than loading from the local cert store, be VERY sure that you do the following:

  • Restrict permissions on the file to read-only for the account under which the application runs and read/write for ONLY administrators/developers who can be trusted with the private key.
  • If it is a password-protected file, DO NOT hard code the password in your code or check it in to source control. That completely defeats the purpose and you might as well not do HTTPS. Anyone who can check out your code and key file could authenticate as that account.
  • Same comment for the .pfx file itself - don't put it in source control.