Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Security / January 2007

Tip: Looking for answers? Try searching our database.

SslStream behavior (slow handshake when used in windows services)

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
letibal@gmail.com - 08 Jan 2007 15:19 GMT
Hello,

I have written a small application (A) that connects to a web site
through SSL, sends a request and receives a response. This application
works fine. In particular, the SSL handshake takes less than 0.20 sec.

If I use the exact same piece of code inside a very simple Windows
service (B), the SSL handshake lasts between 15 and 20 sec (!) - it
does not fail though. However, the web site Im connecting to times out
on reading the request I am supposed to send him

I have enabled tracing for the .NET network stack. There is no
difference between A and B.

Questions :
1) Is there anything I don't know regarding windows services and
SslStream objects ?
2) If I repeat the requests 3 times, in each case the SSL handshake
takes much less time than the very first one. I'd be interested in
knowing what is happening. Is the certificate cached ?

Thanks

Tibo

See below for the code used:
*******************************************************
(A)
using System;
using TestSSL;
using log4net.Config;
using log4net;

namespace ConsoleApplication1
{
   class Program
   {
       private static readonly ILog log =
LogManager.GetLogger(typeof(Program));

       static void Main(string[] args)
       {
           BasicConfigurator.Configure();
           log.Info("Starting test program ...");
           SSLRequest test = new SSLRequest();
           test.TestConnection(null);
      }
   }
}

(B)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Threading;

namespace TestSSL
{
   public partial class Service1 : ServiceBase
   {
       public Service1()
       {
           InitializeComponent();
       }

       protected override void OnStart(string[] args)
       {
           SSLRequest test = new SSLRequest();
           ThreadPool.QueueUserWorkItem(new
WaitCallback(test.TestConnection));
       }

       protected override void OnStop()
       {
       }
   }
}

(Common lib :)

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net.Security;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using log4net;
using System.Configuration;

namespace TestSSL
{
   public class SSLRequest
   {
       private static readonly ILog log =
LogManager.GetLogger(typeof(SSLRequest));

       public SSLRequest()
       {
       }

       public void TestConnection(Object stateInfo)
       {
           NetworkStream stream = null;

           string Server = ConfigurationManager.AppSettings["Server"];
           int Port =
Int32.Parse(ConfigurationManager.AppSettings["Port"]);
           string CertificateSubject =
ConfigurationManager.AppSettings["CertificateSubject"];

           try
           {
               for (int i = 0; i < 3; i++)
               {
                   IPAddress ipAddress = IPAddress.Parse(Server);
                   IPEndPoint connectPoint = new IPEndPoint(ipAddress,
Port);
                   Socket socket = new
Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
                   socket.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress, 1);
                   socket.Connect(connectPoint);
                   stream = new NetworkStream(socket);

                   RemoteCertificateValidationCallback callback = new
RemoteCertificateValidationCallback(OnCertificateValidation);

                   SslStream sslStream = new SslStream(stream, false,
callback);

                   DateTime start = DateTime.Now;
                   sslStream.AuthenticateAsClient(CertificateSubject);
                   DateTime end = DateTime.Now;

                   log.Info("SSL Handshake : "+(end - start));

                   string request = <xml request>

                   byte[] ByteRequest =
Encoding.ASCII.GetBytes(request);

                   log.Info("SslStream IsEncrypted " +
sslStream.IsEncrypted);
                   log.Info("SslStream IsMutuallyAuthenticated " +
sslStream.IsMutuallyAuthenticated);
                   log.Info("SslStream IsSigned " +
sslStream.IsSigned);
                   log.Info("SslStream KeyExchangeAlgorithm " +
sslStream.KeyExchangeAlgorithm);

                   log.Info("Sending request ...");
                   sslStream.Write(ByteRequest, 0,
ByteRequest.Length);

                   byte[] ByteResponse = new byte[2048];

                   log.Info("Receiving response ...");
                   sslStream.Read(ByteResponse, 0,
ByteResponse.Length);

                   log.Info("Response : " +
Encoding.ASCII.GetString(ByteResponse, 0, ByteResponse.Length));
               }
           }
           catch (Exception e)
           {
               log.Error("Exception : ", e);
           }
       }

       protected static bool OnCertificateValidation(object sender,
X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
       {
           return true;
       }
   }
}

*******************************************************
Joe Kaplan - 08 Jan 2007 16:08 GMT
I'd suggest enabling schannel logging with full debug level as per this KB:

http://support.microsoft.com/?id=260729

That may tell you what's going on.  It is most likely that either the
service has an SSL client certificate available to it that you don't have
when running interactively and thus client cert auth is being attempted, or
the service is checking the server's cert chain's CRL (which can be really
slow).  Hopefully the logging will tell you which it is and give you some
idea how to proceed.

Joe K.

Signature

Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--

> Hello,
>
[quoted text clipped - 181 lines]
>
> *******************************************************
letibal@gmail.com - 08 Jan 2007 17:06 GMT
Hi Joe,

Thanks for your answer.
I updated the registry and re-ran the programs.
I got an answer for my 2nd question in the Trace :

The first request logs :
System.Net Information: 0 : [5772] AcquireCredentialsHandle(package =
Microsoft Unified Security Protocol Provider, intent  = Outbound, scc
 = System.Net.SecureCredential)

The subsequent ones log :
System.Net Information: 0 : [5772] Using the cached credential handle.

However I still have the same problem : the windows service taks more
time to perform the SSL handshake, for no apparent reason. I have
checked whether it was attempting to check the CRL - there is no
mention of CRL checking in the trace. No client cert either ...

System.Net Information: 0 : [5300] SecureChannel#18796293 - Left with 0
client certificates to choose from.

Tibo.

> I'd suggest enabling schannel logging with full debug level as per this KB:
>
[quoted text clipped - 13 lines]
> Co-author of "The .NET Developer's Guide to Directory Services Programming"
> http://www.directoryprogramming.net
Joe Kaplan - 08 Jan 2007 18:13 GMT
Sorry the logging didn't thoroughly answer the question.  You might also
need to set up a network trace with netmon or ethereal/wireshark and try to
figure out what's up.

If it is CRL, you should see some traffic going out to the CRL distribution
point defined in each cert in the server's chain.  This is normally HTTP.

I wish I knew exactly what was up, but hopefully you can find more if you
keep digging.

Joe K.

Signature

Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--

> Hi Joe,
>
[quoted text clipped - 41 lines]
>> Programming"
>> http://www.directoryprogramming.net
letibal@gmail.com - 09 Jan 2007 12:01 GMT
Thanks. I have narrowed down a little bit more my research

My app (A) was running under my account (Administrator)
The Service (B) was running under the Local System account.

If I set the service to run under my account, everything works fine.

In the net trace, I have noticed a difference that hadn't caught my eye
previously between A and B, running respectively under an Administrator
account and the Local System account.

**********************************************************
(A) :
[...]
[Public Key]
 Algorithm: RSA
 Length: 1024
 Key Blob: <key>...
System.Net Information: 0 : [5224] SecureChannel#18796293 - Remote
certificate was verified as valid by the user.

(B) :
[...]
[Public Key]
 Algorithm: RSA
 Length: 1024
 Key Blob: <key>....
System.Net Information: 0 : [2460] SecureChannel#4032828 - Remote
certificate has errors:
System.Net Information: 0 : [2460] SecureChannel#4032828 -     Unknown
error.
System.Net Information: 0 : [2460] SecureChannel#4032828 - Remote
certificate was verified as valid by the user.
**********************************************************

Running under the Local System account either :
- generates an error during the certificate checking
or
- requires some checks that were not required under the Administrator
account, and that fail.

Is there any way I can find out what is going on ?, i.e get more
logging on this 'Unknown Error' ?
Thanks,

Tibo
Joe Kaplan - 09 Jan 2007 16:07 GMT
I don't know how to get more logging on the unknown error (sorry!), but I
think you are definitely on to something.

However, I can't tell from the trace below if the system account is checking
the CRL and that is what it doesn't like or if there is just something wrong
with the chain itself.

You might consider writing some diagnostics code using
System.Security.Cryptography.X509Certificates and using the new X509Chain
class to check the status while playing around with various policies.  I'm
not sure exactly how to stitch all that together right off the top of my
head, but it gives you granular control over how the chain is verified, so
you might be able to figure it out that way.

I wish I had a magic bullet for you.

Joe K.

Signature

Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--

> Thanks. I have narrowed down a little bit more my research
>
[quoted text clipped - 42 lines]
>
> Tibo
Dominick Baier - 09 Jan 2007 16:25 GMT
you can turn tracing on at System.Net level - maybe this is helpful

http://www.leastprivilege.com/TracingSystemNet.aspx

-----
Dominick Baier (http://www.leastprivilege.com)

> I don't know how to get more logging on the unknown error (sorry!),
> but I think you are definitely on to something.
[quoted text clipped - 13 lines]
>
> Joe K.
letibal@gmail.com - 19 Jan 2007 11:35 GMT
Hi Joe/Dominik,

I have found a workaround to my problem.
I still don't know what is really happening there :

****************
System.Net Information: 0 : [2460] SecureChannel#4032828 - Remote
certificate has errors:
System.Net Information: 0 : [2460] SecureChannel#4032828 -      Unknown
error.
System.Net Information: 0 : [2460] SecureChannel#4032828 - Remote
certificate was verified as valid by the user.
*****************

However I know it has something to do with the server-side certificate
validation.
For some reason, it seems that it fails to validate the cert chain (?)
or takes ages to retrieve it and does not succeed ... but still does
not log any error/warning apart from this unknown error.

So If I store the server side cert into the certificate store of the
user account of the user that runs the Windows Service, the problem
dissapears !

One last problem though ! I have tried the exact same piece of code,
but instead of putting it on a win app or on a windows service, I have
put it on an ASP.NET web service. (a client triggers the web service
which in turns attempt to remotely connect using SSL to a web server)
Since I encountered the exact same problem, I tried to store the server
side cert into the correct certificate store.

Using the Microsoft Management console (snap in 'Certificate'), I
stored this cert in the 'Personal' directory of the snap in for :
- a Computer account -- did not make any change
- a Service Account -- (I tried W3WP and ASP.NET) -- did not make any
change

Questions :
- Does this approach make sense ?
- Which windows service do you think I should consider ? (i.e. which
one carries out the SSL handshake when this handshake is initiated by
an ASP.NET web service)

Thanks for your help,

Tibo

> you can turn tracing on at System.Net level - maybe this is helpful
>
[quoted text clipped - 20 lines]
> >
> > Joe K.
Joe Kaplan - 19 Jan 2007 14:36 GMT
I think the personal store is the wrong place.  You should put them in other
people most likely.  If you are putting the whole chain in, I'd put the
intermediate CA certs in the intermediate CA certificate store.  I assume
the root is already in trusted roots.

The machine store should be fine and apply to all processes on the box,
although I don't know if there are any weird exceptions to this.  The
profile for the service account should also work.

Joe K.

Signature

Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--

> Hi Joe/Dominik,
>
[quoted text clipped - 67 lines]
>> >
>> > Joe K.
letibal@gmail.com - 29 Jan 2007 14:17 GMT
Hi Joe,

I actually realized that the server cert used by the web site was a
root CA cert (?!)
I have put it in the Third Party Root CA folder and it worked.
Could this be the cause of all the problems ?
Anyway, thank you very much for your help.

Tibo

On Jan 19, 2:36 pm, "Joe Kaplan"
<joseph.e.kap...@removethis.accenture.com> wrote:
> I think the personal store is the wrong place.  You should put them in other
> people most likely.  If you are putting the whole chain in, I'd put the
[quoted text clipped - 6 lines]
>
> Joe K.
Joe Kaplan - 29 Jan 2007 16:27 GMT
Possibly.  It sounds like there was something strange happening with
verifying the cert chain.  However, I don't really know what that was.

This might also be a good question to ask on ms.public.platformsdk.security.
If you summarize your results, someone might be able to provide some more
insight.

In any case, I'm glad you got it working.

Joe K.

Signature

Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--

> Hi Joe,
>
[quoted text clipped - 19 lines]
>>
>> Joe K.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.