.NET Forum / .NET Framework / Security / January 2007
SslStream behavior (slow handshake when used in windows services)
|
|
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 MagazinesGet 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 ...
|
|
|