Diagnosing Stack Overflow Faults in ASP.NET Production Environments

WinDbg is a tool that is immensely useful, but painfully hard to get any value from if you don’t know how to use it. On a few occasions I’ve found myself in a situation where I knew that WinDbg could give me the answer, but I didn’t have enough knowledge of how to use it.

This week we performed a production deployment on one of the projects I am involved in. Pretty soon after, we started to notice some issues cropping up that were affecting the performance and stability of the whole site.

In this scenario, WinDbg was a life saver for us.

The first sign that something was wrong was this event getting fired across the whole front-end tier:

03 Jul 2009 08:32:43 AM
Computer: WEBTIER14
Monitor Title: "Event Log Monitor" (Type=Event Log Monitor)
Description:
* Event Time: 03 Jul 2009 08:32:42 AM
* Source: .NET Runtime 2.0 Error Reporting
* Event Log: Application
* Type: Error Event
* Event ID: 1000
* Faulting application w3wp.exe, version 6.0.3790.3959, stamp 45d6968e, faulting module kernel32.dll, version 5.2.3790.4062, stamp 4626467c, debug? 0, fault address 0x00022366.

We also noted that CPU usage was spiking erratically and was well above the average we would expect for the number of firewall connections we had open.

This event log entry was telling us that the w3wp worker process was faulting in an unrecoverable way, causing the application pool to be torn down in IIS. The nature of the fault was also causing the Windows Error Reporting dw20.exe process to trigger and try capturing a mini dump. These rapid recycling combined with the CPU and IO intensive task of capturing a mini dump was killing our servers and we needed to act fast.

The first step was to work out what was causing the process to be torn down in the first place, as the impact of Windows Error Reporting’s automated analysis was only a secondary issue. To capture this information, I used the adplus script in the Debugging Tools for Windows package to capture a dump.

In its default configuration, adplus will capture a crash dump on any first chance exception. Being an ASP.NET application there are a number of exceptions that we expect to be thrown as 404s are reported, redirects are performed, etc. As such, running adplus in its default configuration was causing it to grab the crash dump too frequently and not giving us the fault that was causing the complete teardown.

To capture the process teardown fault, I ran adplus with these parameters:

adplus.vbs -pn w3wp.exe -c Unknown.cfg

The first parameter just says that we want to capture dump data for the w3wp.exe process. The second parameter is the interesting one – I am passing in a configuration file that includes some exception filters. I grabbed this config from Tess’ blog.

The content of the configuration file is fairly simple XML that describes the unknown fault scenario:

<adplus>
    <settings>
        <runmode> CRASH </runmode>
    </settings>
    <exceptions>
        <config>
            <code>AllExceptions</code>
            <actions1>Log</actions1>
            <actions2>MiniDump;Log;EventLog</actions2>
        </config>
        <newexception>
            <code> 0xe053534f </code>
            <name> Unknown_Exception </name>
        </newexception>
        <config>
            <code> 0xe053534f </code>
            <actions1>FullDump;Log;EventLog</actions1>
            <actions2>FullDump;Log;EventLog</actions2>
        </config>
    </exceptions>
</adplus>

With the debugger now attached, it was a simple matter of waiting for the problem to occur. In our scenario it was happening every 10 to 15 minutes per server, so it took a while for me to capture the dump. Finally, the console showed “Dump file successfully written”.

To analyse the dump file, I opened windbg.exe, chose Open Crash Dump and selected the .dmp file that had been written out. You’ll usually find it in a CrashDump folder next to where you ran adplus from.

As we are working with a .NET application, but WinDbg is a native debugger, my next step was to load a set of debugger helpers called SOS. This is basically a plugin for WinDbg that lets us access information about the CLR state.

To load it, run:

.loadby sos mscorwks

The period at the start says it’s a WinDbg core command. The next parameter is the name of the library we want to load into the debugger. The final parameter is basically a relative file base – we’re telling WinDbg to load SOS from the same location that mscorwks had been loaded from. mscorwks is the .NET runtime, so this approach helps ensure we are loading the correct version and architecture of SOS and it saves us from having to write out the full path.

Next, I wanted to see what point of our application we were in when the process faulted. With SOS loaded, the command is quite simple:

!clrstack

Instantly, it was obvious that we had a stack overflow problem. This was made clear by literally hundreds of repeated frames on our call stack. It also explained why our ASP.NET Health Monitoring wasn’t reporting an exception, as a stack overflow causes the whole process to be torn down before any exception handling code can be run.

Here’s a short snippet (with the project details masked out):

...
CompanyName.Project.Web.Logic.Component.Method1(System.String, System.Int32)
CompanyName.Project.Web.Logic.Component.Method2(System.String)
CompanyName.Project.Web.Logic.Component.Method1(System.String, System.Int32)
CompanyName.Project.Web.Logic.Component.Method2(System.String)
CompanyName.Project.Web.Logic.Component.Method1(System.String, System.Int32)
CompanyName.Project.Web.Logic.Component.Method2(System.String)
CompanyName.Project.Web.Logic.Component.Method1(System.String, System.Int32)
CompanyName.Project.Web.Logic.Component.Method2(System.String)
CompanyName.Project.Web.Logic.Component.Method1(System.String, System.Int32)
CompanyName.Project.Web.Logic.Component.Method2(System.String)
CompanyName.Project.Web.Logic.Component.Method1(System.String, System.Int32)
CompanyName.Project.Web.Logic.Component.Method2(System.String)
CompanyName.Project.Web.Logic.Component.Method1(System.String, System.Int32)
CompanyName.Project.Web.Logic.Component.Method2(System.String)
...

The component in question is used across almost all of our pages and the site wasn’t completely down, so I could now derive that it was being caused by a very specific piece of data. This leads us to our next challenge, which is to find out what which record was triggering the problem.

To do so, I ran:

!clrstack -p

The argument of -p asks SOS to list out all of the parameters for each stack frame.

Finding out the URL of the current request is a non-trivial task, and not all parameters are available due to runtime optimisations. Rather than persisting in a search for the URL, it was much easier to grab another stack frame which included the information I needed:

1947ed28 1abb1c7e CompanyName.Project.Web.Logic.Presenters.RetailItemPresenter.View_ItemLoading(System.Object, CompanyName.Project.Web.Logic.Views.ItemLoadingEventArgs)
PARAMETERS:
this = 0x04144bf8
sender =
e = 0x04151b38

This is the event handler on one of our presenters being called as a result of the ItemLoading event being fired on the corresponding view. This approach is specific to our MVP architecture, but it was a known and simple entry point into most of the stack. Try and look for an equivalent call in your own stack.

I could now see that it was a particular retail item triggering the problem, and I knew that the information I needed was in the event arguments. You can see the parameter on the stack frame shown as e = 0x04151b38. That number is the memory location of the object we need.

To get the object, I then ran:

!do 0x04151b38

The !do command is a shortcut for !DumpObject, and gave us this output:

Name: CompanyName.Project.Web.Logic.Views.ItemLoadingEventArgs
MethodTable: 19ef2170
EEClass: 19d49b70
Size: 12(0xc) bytes
(c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\aef04219\e69835b3\assembly\dl3\29e5062003a4007_a4fac901\CompanyName.Project.Web.Logic.DLL)
Fields:
MT Field Offset Type VT Attr Value Name
7932a400 4000183 4c System.EventArgs 0 shared static Empty
>> Domain:Value 001cc630:NotInit 00204700:03043914 <<
793308ec 4000819 4 System.String 0 instance 0412522c <RetailItemId>k__BackingField

We can now see the RetailItemId property and its corresponding backing field. The value column on that line is 0412522c, which is another memory reference.

Once again, we dump the memory reference:

!do 0412522c

This returns us the corresponding System.String object:

Name: System.String
MethodTable: 793308ec
EEClass: 790ed64c
Size: 28(0x1c) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: 6T046
Fields:
MT Field Offset Type VT Attr Value Name
79332b38 4000096 4 System.Int32 1 instance 6 m_arrayLength
79332b38 4000097 8 System.Int32 1 instance 5 m_stringLength
793315cc 4000098 c System.Char 1 instance 36 m_firstChar
793308ec 4000099 10 System.String 0 shared static Empty
>> Domain:Value 001cc630:02fe01d0 00204700:02fe01d0 <<
7933151c 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 001cc630:02fe0728 00204700:06fe0e14 <<

Hidden away on the sixth line there is the raw string value, 6T046, which was the product id.

At this point, we had:

  1. Captured a complete memory dump of the w3wp.exe process when the fault occurred
  2. Identified a stack overflow to be the cause of the fault
  3. Identified the area of our code that was involved in the stack overflow
  4. Identified the particular piece of data that was exposing the bug

We could now:

  1. Perform a quick fix to the data to circumvent the bug and get the website performant and stable again
  2. Reproduce the bug locally using the same data that caused the issue
  3. Have the lazy Friday we’d been hoping for

Video: Building great standards based websites for the big wide world with ASP.NET 4.0

The video recording from Damian and my session at REMIX Sydney last week is now online. Thanks to some funky aspect ratio issues we both look really buff too.

In this session, two ASP.NET MVPs will share their experiences from building Australia’s largest e-commerce site with ASP.NET Web Forms. They’ll show you how to be a good web citizen by covering standards compliance (properly!), cross browser and accessibility considerations, non-JavaScript support, as well as coding techniques like the Model-View-Presenter pattern to improve maintainability and testing. Along the way, there’ll be plenty of discussion of the differences between Web Forms and ASP.NET MVC in each area, as well as an early peek at some of the new features coming in ASP.NET 4.0 and VS 2010.

Building great standards based websites for the big wide world with ASP.NET 4.0 – watch online

Building great standards based websites for the big wide world with ASP.NET 4.0 – download WMV

The resources from our talk are now all online (including the ones that hadn’t been released yet on the day):

We also talked about the CSS Friendly Control Adapters which are again open source, under MS-PL.

While you’re checking out the videos, take a look at Jordan’s rockin’ Silverlight 3 Super Session. Not all of the videos are up yet (like, most of them aren’t), and I imagine that from the feedback flowing in there might be an updated version of the videos soon, so keep watching the videos page for some other really good content.

Updated @ 1601, 18th June: The video page interface has been updated to support deep linking so I’ve update this post to include links to the in-page players.

Updated @ 1053, 20th June: Added links to resources now that they’re all been published.

Released: XHTML Markup Sanitizer

Last week at Remix I demonstrated a markup sanitizer that I’d been working on and announced that it’d soon be available as open source. After a few days and a bit of intellectual property management, I’ve finally managed to get it up on CodePlex under the MS-PL license.

http://markupsanitizer.codeplex.com

The XHTML Markup Sanitizer takes untrusted (X)HTML and massages it into real, trusted XHTML. While plenty of effort goes into preserving the original intent, markup validity and safety is the first priority. It’s particularly useful with content management systems where users are in control of markup, but you want to target XHTML1.1.

The sanitizer does not process entire pages. It is designed to massage snippets of text that users might enter in things like blog comments or product descriptions.

Check it out and try it in your project today! Now you have no excuse for not targeting full standards compliance.

Released: OpenSearch Validator

image

 

http://opensearchvalidator.com

I’ve been doing a lot of work with both OpenSearch and IE8 Visual Search this week. These are the feeds to let you integrate your website into the browser’s search dropdown. Unfortunately the specs are spread across three different organisations (A9, Mozilla and Microsoft) and all rather poorly documented.

We’ve been implementing the feeds on a major e-commerce website, and thus we wanted to make sure we got them right. (We’ll be showing the site in the REMIX keynote next week.)

The solution? I built a validator. As we found new issues, I added them to the validator.

Today I’m releasing that validator for you to test your own sites with.

Some cool ones to check out are:

  • hanselman.com, who has his SearchFrom element in the wrong namespace at the time of writing
  • wikipedia.org, who are serving their visual search suggestions with the wrong media type at the time of writing
  • au.yahoo.com, who are serving their OpenSearch description with the wrong media type at the time of writing
  • bing.com, who don’t even publish a feed at the time of writing

Update 7th June 2009: The source code is now published on CodePlex.

Event: Wollongong .NET User Group – tonight!

I’ll be at WDNUG tonight spruiking ASP.NET MVC.

ASP.NET MVC Preview 3

What is this whole MVC thing anyway? Tatham Oddie will demonstrate how to use the model-view-controller (MVC) pattern to take advantage of your favourite .NET Framework language for writing business logic in a way that is de-coupled from the views of the data. We’ll discuss the advantages to MVC on the web, the other frameworks that are available, and build an app with it – all in an hour.

If you live in our neighbouring southern city, come along.

See you all there. 🙂

Event: Code Camp SA 2008 – this weekend!

This weekend I’ll be scooting on down to Adelaide to take part in Code Camp SA 2008.

Now that Peter has posted the final schedule, it was high time for me to whip up an ICS feed:

webcal://tatham.oddie.com.au/files/CodeCampSA08.ics

As I’ve said previously, an event doesn’t actually exist for me until it’s lodged in my Outlook. I think I might have a problem in this respect.

See you at TechEd 2007

Assuming nobody at Microsoft Australia rolls over in bed tonight and changes their mind, I’m pleased to announce that I’ll be presenting at TechEd Australia this year.

The official session blurb (missing a zillion ™ and ® symbols) is:

Utilising Windows Live Web Services Today

Windows Live represents a collection of opportunities to integrate with a new generation of online services. This session provides an overview of the Windows Live family – outlining the business and technical advantages, the range of integration options that exist for each service, real world examples and live demos.

My plan is to focus on:

  • Live ID
  • what’s available now
  • an overview of how to integrate it with demo (partly pre-cooked)
  • why to integrate (stats on number of users)
  • Spaces
    • what integration options exist
  • Messenger
    • what integration options exist (activities / bots)
    • why to integrate
  • Contacts
    • control vs. API
    • demo of the Windows Live Data API
    • I’ve been building out a library in this space which makes it easy to call from.NET – will hopefully be releasing this open source soon
  • why to integrate (stats on number of users)
  • Virtual Earth
    • less here – John + Bronwen will cover this in their session
    • overview + show some of the partner sites already out there

    Other topics that could also be covered:

    • Live Toolbar Custom Buttons
    • how to build them
    • I’ve been building out a library in this space which makes them really easy to publish from ASP.NET – will hopefully be releasing this open source soon too
  • Live.com Gadgets
    • been around a while so probably not do much more than mention them

    If you’re interested in know more about the Windows Live family, or don’t really understand what live is about, come along and check it out. If there’s anything you can think of that you’d especially like to see, drop me a note and I’ll try and get it in.

    See you on the Gold Coast!

    ASP.NET AJAX … continued

    Last week I was angry. Today I’m disappointed.

    ASP.NET as a platform rocks, and being the Microsoft zealot I am, we chose to use ASP.NET AJAX behind visitscandinavia.com.au. It’s no longer in beta. It supports IE6, IE7, Firefox 1.5 and Safari 2.0. Good to go – right? Unfortunately, that hasn’t been our experience.

    Luckily, the actually AJAX requirements of this project are very minimal (only two parts of the whole site), but the headaches we’ve experienced along the way are starting to outweigh the benefits. I trusted the framework, and on tight timelines it has come back to bite me in the ass. Even the bits that are supposed to work for everyone don’t seem to work for me (see “Any Browser + UpdatePanel = Nothing” below). This made me angry.

    Here are some of my thoughts at the moment …

     

    Safari 1.3.2 = Neglect

    No support for Safari 1.3.2 at all. This was documented, so this is ok. Considering the number of alternatives on the market that do work with Safari 1.3.2, this disappoints me, but they documented it so it’s ok. For this project, Safari 1.3.2 is a very small sliver of the userbase. I think there should be at least some partial support here for things like the client side library – Sys.UI.DomElement, etc.

     

    Safari 2.0 = Boom

    The first problem was pretty major for us. As I documented in my previous post, the framework failed to do anything useful (like actually load) on Safari 2.0 through 2.0.3. The word I’m hearing from ASPInsiders is that the documentation is actually wrong and should read 2.0.4 not 2.0 – I’m waiting to hear the final response on this. Really, if the documentation gets fixed this could be ok, as most Mac users are up-to-date with minor builds. I found this especially painful though, as one of the first things I did when trying to debug it was start with a blank project. When even that failed, it wasn’t a great confidence boost.

     

    Safari 2.0.4 + <fieldset /> + border:none; + ModalPopupExtender = Big Boom

    This bug requires a pretty specific set of elements to be in place, however I’m still surprised it wasn’t picked up earlier. In most of the examples where I’ve seen a ModalPopupExtender used, it is displaying some nice little gadget like this:

    Currency Converter Gadget

    Considering the gadget, like most others, contains a group of related fields the contents of the gadget should be wrapped in some semantic HTML like so:

    <fieldset>
        <legend>Currency Converter<legend>
        ... form elements ...
    </fieldset>

    Now, the rendered appearance of a fieldset is really rather ugly and also inconsistent between browsers. Lets fix that with some CSS:

     fieldset { border: none; }

    Trigger the updated panel and big boom – Safari itself just crashed after ending up in an infinite loop of updateScrollInfoAfterLayout() calls in the com.apple.AppKit library. This is 100% replicable for me on Safari 2.0.4.

    image

    The workaround is currently to use this rather ugly CSS instead:

    fieldset { border: solid 1px transparent; margin: -1px; } /* Needs to be a transparent border to avoid crashing Safari 2.0.4 — border:none in conjunction with ajaxToolkit:ModalPopupExtender blows up the browser. */
    * html fieldset { border: none; }

    This could be a Safari problem, it could be a bug in the extender that Safari isn’t handling very well. Either way, it’s pretty broken.

     

    Any Browser + UpdatePanel = Nothing

    This one’s the cracker.

    To me, this is pretty basic code:

    <asp:UpdatePanel runat=”server” ID=”UpdatePanel”>
        <ContentTemplate>
            <asp:Label ID=”Proof” runat=”server” />
            <asp:Button ID=”Trigger” runat=”server” OnClick=”Trigger_Click” />
        </ContentTemplate>
        <Triggers>
            <asp:AsyncPostBackTrigger ControlID=”Trigger” EventName=”Click” />
        </Triggers>
    </asp:UpdatePanel>

    Apparently it’s there’s something wrong with my particularly little setup, because it’s rendering out broken JS that the browsers are refusing to even parse:

    <input type="submit" name="ctl00$ContentPlaceHolder$Trigger" value="" onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(&quot;ctl00$ContentPlaceHolder$Trigger&quot;, &quot;&quot;, true, &quot;&quot;, &quot;&quot;, false, false))" id="ctl00_ContentPlaceHolder_Trigger" />

    Notice the escaped “&quot;” within the onclick parameter?

    None of my client-side code can be the culprit as the rendered output is just plain wrong.

    I’ve got working instances of the UpdatePanel in plenty of other places (even some already within this same application), so there’s just something quirky in this particular setup. I’m still trying to debug this one …

     

    ASP.NET AJAX Control Toolkit

    Unfortunately the control toolkit experience on a Mac is pretty pathetic, however it’s pretty clear why. The Control Toolkit is largely developed by people external from Microsoft – people like Paul Glavich. Having vented to Paul quite a bit about all this lately, it became clear that him and most of his co-developers don’t actually have access to a Mac to test any of their bits – they just have to poke blindly in the dark and hope it all works. My experiences with a such an approach over the years haven’t proved very successful – there are even noticeable differences between the same version of Firefox on two different platforms. In the end I spent $80 each for two G4 Mac towers. They hum away on my local network controlled by VNC, one running Mac OS X 10.3.9 with Safari 1.3.2 and the other running Mac OS X 10.4.9 with Safari 2.0.4. When I’m out of the office, I can VNC over the the VPN.

    I personally don’t understand how any professional web developer can justify not having such a setup. In the mean time though, I think the core ASP.NET AJAX team seriously needs to investigate some kind of hosted testing setup like this, with logins issued to the Control Toolkit developers. Obviously it’s a lot easier if you can point it across the local LAN to your development IIS, however a hosted setup like this accessing a CI output would still make a substantial dent in the number of Mac related bugs.

    Right now I’m trying to write a patch for the CalendarExtender that puts it on the right z-index in Safari… Everything else in the extender works perfectly, you just can’t always see it.

     

    The People

    I do have to say, Scott Gutherie continues to amaze me as a person. He seems to be responsible for every useful chunk of developer technology getting released from Microsoft at the moment, yet still manages time to actually connect with the community, listen then actually help people out. This is a rare trait across any workplace, and requires an incredible amount of dedication.

    I still love your work Scott, I just wish it’d love me back from time to time. 🙂

     

    (More to come … Sorry for the delay in getting this follow-up post out; I was busy vomiting between extended periods of sleep.)

    Debugging SSL/TLS problems in .NET

    In my previous post I discussed  some issues I discovered with SSL client certificates.

    For this application, I’m dealing with PayPal. Rather annoylingly, if your client certificate doesn’t check out, they don’t even bother sending an error – they just drop the TCP connection mid-SSL-handshake. Depending on how fast the connection loss is realised, .NET can return a few different errors; none of which are actually much use when it comes to debugging.

    Luckily, System.Net (as with most other areas of the framework) has wonderful tracing capabilities. Particularly with a complex process like an SSL handshake, these capabilities become critical to debugging.

    Better yet – they’re incredibly easy to use. Just add a block like this to the end of your app.config!

    <system.diagnostics>
      <trace autoflush="true"/>
      <sources>
        <source name="System.Net" maxdatasize="1024">
          <listeners>
            <add name="TraceFile"/>
          </listeners>
        </source>
        <source name="System.Net.Sockets" maxdatasize="1024">
          <listeners>
            <add name="TraceFile"/>
          </listeners>
        </source>
      </sources>
      <sharedListeners>
        <add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener"
          initializeData="trace.log"/>
      </sharedListeners>
      <switches>
        <add name="System.Net" value="Verbose" />
        <add name="System.Net.Sockets" value="Verbose" />
      </switches>
    </system.diagnostics>

    Now, run your app again and take a look at the wonderful lines being added to your bin\Debug\trace.log file.

    Getting your PayPal API certificate into a format usable from C#/VB.NET

    (Disclaimer: I’m not a security expert – I’m just a dude who bashes his head against the wall until I can find a way to make it work.)

    The certificate file that PayPal issues (cert_key_pem.txt) is pretty useless if you’re calling their API from .NET. (I don’t know about other platforms).

    Fortunately, it’s a simple one step process to generate a cert_key.der file that you can then use from C# like this:

    X509Certificate certificate = X509Certificate.CreateFromCertFile(certificatePath);
    httpWebRequest.ClientCertificates.Add(certificate);

    What scares me (and thus became the motivation behind the blog post) is that while there is lots of documentation around this process, it’s all painfully over-complicated! Unfortunately this hack method has been distributed so far now that even O’Reilly, a respected  publisher, is touting it in print as the best process. Their method is to use OpenSSL to convert it from PEM to P12 format, then import it into your local certificate store using MMC, and finally export it back to CER format.

    You can actually achieve an equivalent process using a single call to OpenSSL:

    openssl x509 -outform der -in cert_key_pem.txt -out cert_key.der

    (Note: OpenSSL is an awesome, and free, open-soure tool you can download from here.)

    It puzzles me why this harder, more error prone method is getting all the airtime? I came up with two answers:

    1. It’ could be a new feature in OpenSSL and hence wasn’t possible before.
    2. This process is normally being executed by people who’ve never touched X509 certificates before, and thus never realised that DER (what OpenSSL produces) is the same thing as CER (what .NET expects).

    I hope people find this simpler method before they start littering their certificates throughout their development machine registries.

    Update: The rest of the story …

    After playing with this a bit more, I can now see where this idea has come from, however I still don’t like it.

    A .cer file only contains your public key, which isn’t very useful for signing something; .NET needs to have access to the private key too. If you only used the code above, then moved you app to a different machine, you’d be getting an SSL-related exception when you called httpWebRequest.GetResponse(). Taking a look at the tracing output from System.Net would reveal something like this:

    System.Net Information: 0 : [5988] SecureChannel#23264094 – Locating the private key for the certificate: [Subject]
    C=AU, S=New South Wales, L=Neutral Bay, CN=tatham_api1.oddie.com.au

    [Issuer]
    E=re@paypal.com, CN=live_camerchapi, OU=live_certs, O=”PayPal, Inc.”, L=San Jose, S=California, C=US

    [Serial Number]
    00D53D

    [Not Before]
    23/05/2007 11:08:48 AM

    [Not After]
    20/05/2017 11:08:48 AM

    [Thumbprint]
    0461121436D3CE11C042BF3F67884B4220072853
    .
    System.Net Information: 0 : [5988] SecureChannel#23264094 – Cannot find the certificate in either the LocalMachine store or the CurrentUser store.

    Notice that last line saying it couldn’t find the certificate in the store – even though we’d loaded it directly from file? That’s .NET trying to find the full certificate to get the private key.

    This is where the first approach comes in handy – in the process using their local certificate store to import/export, most users wouldn’t have deleted the certificate afterwards. The private key remains in the certificate store, and .NET can resolve it from there. There is in fact a benefit to this approach – your private key can be stored somewhere outside you file system and thus be more secure. Well, that’s the theory anyway. In reality – it still needs to be somewhere that your process can access, be it an ACLed file or the certificate store it probably doesn’t make that much of a difference.

    The problem to this approach is the deployment impact – you need to be able to install the certificate in the servers store. In some environments this just isn’t possible, and in other environments it just sounds painfully fiddly. So – how can we load the entire certificate, including the public and private key tokens – from a file?

    As before, we’re going to use OpenSSL to convert the supplied certificate into something useful. This time though, we’re going to convert it to a P12 file (otherwise known as a “PKCS#12 container file”). These files contain not only certificates, but also private keys in encrypted form.

    openssl pkcs12 -export -in cert_key_pem.txt -out cert_key.p12

    When you execute this command, OpenSSL will ask you to choose a password. It will use this password to encrypt your private key, so make the password complex and unique.

    You can now load and attach the certificate like so:

    string certificatePath = Path.GetFullPath(Path.Combine(Assembly.GetExecutingAssembly().Location, @”..\..\..\..\cert_key.p12″));
    string certificatePassword = “p12passwordhere”;
    X509Certificate clientCertificate = new X509Certificate2(certificatePath, certificatePassword);
    httpWebRequest.ClientCertificates.Add(clientCertificate);

    Voila! It works.

    Now, you just need a way to securely store your API username, API password, P12 file and P12 password. That’s something I’ll cover in another blog post.