September 2004 - Posts

Using CAPICOM to obtain an SSL Certificate

I recently needed to obtain an X509Certificate from the local computer's certificate store, because my program sends data via SSL.  It took way too long for me to find out how to do this, so hopefully, I can save you some trouble.  After banging my head on the desk for a while, I found CAPICOM.

CAPICOM is a DLL that Microsoft provides that allows you to “digitally sign data, sign code, verify digital signatures, envelop data for privacy, hash data, encrypt/decrypt data and more.“  You can download this DLL from Microsoft here.

CAPICOM is not a managed DLL, but .NET will create an InterOp assembly for you.  All you have to do is access the code.  I found an article on the net that showed me the way.  My slightly altered code is shown below.

using System;
using System.Collections;
using System.Security.Cryptography.X509Certificates;
using CAPICOM;

namespace CAPIComWrapper
{
     /// <summary>
     /// Provides methods to interact with Windows Certificate stores.
     /// </summary>
     public class CertificateManager
     {
          /// <summary>
          /// Searches for and returns a particular X509 certificate.
          /// </summary>
          /// <param name="SearchString">A full or partial certificate name</param>
          /// <returns>An instance of the X509Certificate class.</returns>
          public static X509Certificate Get(string SearchString) 
          {
               string storeName = "My"; // "My" indicates the .Default store
               StoreClass oStore;
               Certificates oCerts;
               X509Certificate foundcert = null;

               // get a reference to the LOCAL MACHINE certificate store
               oStore = new StoreClass();
               oStore.Open(
                                   CAPICOM_STORE_LOCATION.CAPICOM_LOCAL_MACHINE_STORE,
                                   storeName,
                                   CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_EXISTING_ONLY | 
                                   CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_READ_ONLY);

               // Get a list of all certificates in the store
               oCerts = (Certificates)oStore.Certificates;

               // get a list of only the matching certificates
               oCerts = (Certificates)oCerts.Find(
                                   CAPICOM_CERTIFICATE_FIND_TYPE.CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME,
                                   SearchString, 
                                   false);

               // do we have any certs?
               if(oCerts.Count > 0)
               {
                    // reference the first certificate
                    Certificate firstcert = (Certificate)oCerts[1] ;

                    // get a certificate context from that cert
                    ICertContext iCertCntxt = (ICertContext) firstcert;

                    // now get a pointer to the context
                    int certcntxt = iCertCntxt.CertContext ;

                    // turn the int pointer into a managed IntPtr
                    IntPtr hCertCntxt = new IntPtr(certcntxt);

                    // was all of this successful?
                    if(hCertCntxt != IntPtr.Zero)
                    { 
                         // create an X509Certificate from the cert context
                         foundcert = new X509Certificate(hCertCntxt);
                    }

                    // free the certificate context
                    iCertCntxt.FreeContext(certcntxt);
               } 
               else
               {
                    foundcert = null;
               }
                return foundcert;
          }
     }
}

Thread.Sleep(0) and File Manipulation

I've been working on a multi-threaded Windows Service for nearly a year now.  I am quite proud of it.  It has a single management thread that assigns tasks to 8 other threads.  (The number of threads is configurable.)  I've gone to great lengths to ensure that there are no race conditions in the code, and that the threads never interfere with one another.  It also communicates with a SQL Server, but only for configuration and event logging purposes.  During all of the system testing, everything worked perfectly. 

Then came the parallel tests.  You can imagine my frustration when I started recieving SQL timeout errors.  After an extended period of head-banging, uh, I mean, methodical analysis, I found the problem.

I create a lot of files.  I also delete a lot of files.  In loops.

This kind of file creation and deletion was pegging the CPUs.  Especially when multiple threads were doing this at the same time.  So, remembering my VB days, I pulled out the equivalent to DoEvents: 

Thread.Sleep(0);

This command tells the thread to yield the rest of its time-slice to other threads.  It did the trick, with no perceptable decrease in performance.

Now, I don't recommend this as a total solution to such problems.  But it certainly helped me out of a jam.  I can now continue the tests, while I examine my disk contention issues more carefully.

Making a Windows Service Shut Itself Down

I've been asked a couple of times now how a Windows Service can shut itself down programmatically.  In the interest of completeness, I should tell you that there is quite a bit of disagreement out there about whether you should do this.  Some say that a Windows Service shouldn't shut itself down, but should only respond to the Service Control Manager.  Being the practical type, I believe that there are a few situations where Services should shut themselves down.  In any event, here is the code that I have used:

/// <summary>
/// Cause the OnStop method to be called by the Service Control Manager.
/// This is done to end the service cleanly.
/// </summary>
private void InvokeOnStop()
{
     ServiceController sc = new ServiceController(this.ServiceName);

     sc.Stop();
     sc.Dispose();
}

Don't forget the <xsl:output encoding=""/> when creating text files!

I've written many XSLTs that create formatted text files.  I don't do this very often, and there are usually many months between each occurrence.  So, I often find that the first three characters in the resulting file are the characters 0xEF, 0xBB, and 0xBF.  (These apparently indicate that UTF8 encoding is being used.)  Because these characters do not show up in normal text editors, I don't always notice that they are there.  When I look at the file with a hex editor, however, BINGO!.  There they are.

As you can imagine, this causes problems for some of my clients that do not expect these characters.

To solve the problem, I place <xsl:output indent="no" method="text" encoding="ISO-8859-1"/> in the XSLT.  This removes the encoding characters from the resulting text file.  Now my clients are happy, and so am I.

Note that this works in my situation, but that you may need a different encoding.  Just don't forget the <xsl:output encoding=""/>!