5

I'm not able to bypass the certificate validation in the production server as the ServicePointManager.ServerCertificateValidationCallback is never called:

ServicePointManager.ServerCertificateValidationCallback = ValidateRemoteCertificate;

...

private bool ValidateRemoteCertificate(
    object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors)
{
    LogActionToTable(EntrySeverity.Information, 
        $"ValidateRemoteCertificate() Certificate name: {cert.Subject}");
    return true;
}

That line is never executed when called from a method in the web application, weird enough is that it is properly called from the unit tests (NUnit).

I do have add this call before any other call to the remote API.

What am I doing wrong?

it's not even working if I just do:

ServicePointManager
  .ServerCertificateValidationCallback += 
    (sender, cert, chain, sslPolicyErrors) => true;

This must be really simple, but I'm on this for several days and I can't manage this to work properly.


No matter if I'm using a RestSharp call where I build the XML manually, or called through the generated proxy from wsdl.exe tool, I always end getting this from the call:

The request was aborted: Could not create SSL/TLS secure channel.

and enabling Trace into a log file, I get:

System.Net Information: 0 : [7912] SecureChannel#14469269 - Certificate is of type X509Certificate2 and contains the private key. System.Net Information: 0 : [7912] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent = Outbound, scc = System.Net.SecureCredential) System.Net Information: 0 : [7912] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = (null), targetName = onni.unicom.fi, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation) System.Net Information: 0 : [7912] InitializeSecurityContext(In-Buffer length=0, Out-Buffer length=170, returned code=ContinueNeeded). System.Net Information: 0 : [7912] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 14899210:2119fd0, targetName = onni.unicom.fi, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation) System.Net Information: 0 : [7912] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=CredentialsNeeded). System.Net Information: 0 : [7912] SecureChannel#14469269 - We have user-provided certificates. The server has specified 1 issuer(s). Looking for certificates that match any of the issuers. System.Net Information: 0 : [7912] SecureChannel#14469269 - Left with 0 client certificates to choose from. System.Net Information: 0 : [7912] Using the cached credential handle. System.Net Information: 0 : [7912] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 14899210:2119fd0, targetName = onni.unicom.fi, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation) System.Net Information: 0 : [7912] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=173, returned code=ContinueNeeded). System.Net Information: 0 : [7912] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 14899210:2119fd0, targetName = onni.unicom.fi, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation) System.Net Information: 0 : [7912] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=CertUnknown). System.Net Error: 0 : [7912] Exception in HttpWebRequest#25731266:: - The request was aborted: Could not create SSL/TLS secure channel.. System.Net Error: 0 : [7912] Exception in HttpWebRequest#25731266::EndGetRequestStream - The request was aborted: Could not create SSL/TLS secure channel..

most important:

System.Net Information: 0 : [7912] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=CertUnknown).

Why is not this bypassed by the ServicePointManager.ServerCertificateValidationCallback call?


Code without proxy:

    private CardBalanceInfo ApiGetCardBalance(string cardNumber)
    {
        LogActionToTable(EntrySeverity.Information, $"ApiGetCardBalance('{cardNumber}')");

        var soapEnvelope =
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
                "  <soap:Header/>" +
                "  <soap:Body>" +
                "    <csi:getBalance xmlns:csi=\"urn:si.tm.webservice.uniassociation.unicom.com\">" +
                "      <csi:balanceId>" +
               $"        <entityName>{cardNumber}</entityName>" +
                "      </csi:balanceId>" +
                "    </csi:getBalance>" +
                "  </soap:Body>" +
                "</soap:Envelope>";

        const string soapUrl = "treasurymanagement/si";
        const string soapAction = "http://www.unicom.fi/uAASTreasuryManagementSI/getBalance";
        var response = MakeApiCall(soapUrl, soapAction, soapEnvelope);

        return response.ToBalanceInfo();
    }

code with proxy:

    private CardBalanceInfo ApiGetCardBalanceFromProxy(string cardNumber)
    {
        LogActionToTable(EntrySeverity.Information, $"ApiGetCardBalanceFromProxy('{cardNumber}')");

        using (var service = new uAASTreasuryManagementSIService())
        {
            service.Timeout = 10000; // 10 sec
            service.Url = _settings.BaseUrl.TrimEnd('/') + "/treasurymanagement/si";
            service.ClientCertificates.Add(_clientCertificate);

            var card = new getBalance()
            {
                balanceId = new EntityId
                {
                    entityName = cardNumber
                }
            };

            var balanceResponse = service.getBalance(card); // get API response

            LogActionToTable(EntrySeverity.Information, $"ApiGetCardBalanceFromProxy success.");

            return new CardBalanceInfo()
            {
                AvailableBalance = balanceResponse.balance.availableBalanceValue,
                Balance = balanceResponse.balance.balanceValue,
                BalanceId = balanceResponse.balance.balanceId.ToString()
            };
        }
    }
balexandre
  • 73,608
  • 45
  • 233
  • 342
  • Where in the client side code are you setting this? – Stephen McDowell May 04 '16 at 16:12
  • Does the same code work correctly in the lower environments, such as DEV or QA? It could be related to the security of the AppPool.Another option could be lack of Client Certificate to validate. – Konstantin May 04 '16 at 16:42
  • When you hit the service endpoint in browser what do you see? – Stephen McDowell May 04 '16 at 20:54
  • @StephenMcDowell the service is there, Unit Tests run great, but not when called from a method in the application. Service definition: http://s6.postimg.org/5bilqkx29/screenshot_109.png – balexandre May 04 '16 at 21:58
  • In the client code ensure your line 'ServicePointManager .ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;' is being executed before making the call to the service. I assume you're getting a SecurityNeogtiationException so look in the stacktrace to confirm. – Stephen McDowell May 04 '16 at 22:05
  • @StephenMcDowell I wrote in my question: *I've added this call before any other call to the remote API.* ... :) and it's the first call prior to setup the service :( – balexandre May 04 '16 at 22:42

1 Answers1

0

I do not believe the certificate validation is your issue.

ServerCertificateValidationCallback is used when doing custom validation of the certificate. This is most commonly needed in a dev or qa environment where the host name of the certificate does not match the URL of the service you're calling.

You'll know when you've ran into this problem because you'll receive a SecurityNeogtiationException with the following error details:

Additional information: Could not establish trust relationship for the SSL/TLS secure channel with authority '{HOSTNAME}'

Here is what that looks like when I turn off my ServerCertificateValidationCallback on my local machine.

enter image description here

I'd recommend you shift your focus to

The request was aborted: Could not create SSL/TLS secure channel.

as this appears to be different problem altogether. And one fortunately one I haven't ran into :-) The answer here would be a good place to start: The request was aborted: Could not create SSL/TLS secure channel

HTH

Community
  • 1
  • 1
Stephen McDowell
  • 839
  • 9
  • 21
  • I got to the Certificate from the Stack message `returned code=CertUnknown` and the next one is the SSL error channel ... was not just cause I like it :) - plus, I already have tried with `ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;` . The return code is actualy `200` (I can call the API using Postman from the hosting server correctly). – balexandre May 05 '16 at 20:24
  • BTW, your error is the same, just with different wording as you're using other library (I 'm using HttpWebRequest) – balexandre May 05 '16 at 20:30
  • Sounds like something is "off" with the certificate. Thinking out loud... it makes sense that it works when calling from the same machine because the caller trusts a cert in its own store. – Stephen McDowell May 05 '16 at 20:34