Friday, January 8, 2010

Resolving difficulties with using certs

Recently, my company has begun working on integrating the various services and products we offer.  Since most of our customers purchase or use multiple services, it only makes sense that the data they have in one should be available in all (I know, sometimes the obvious takes a while).  To do this, we're using .Net web services to allow this to happen.  In order to protect these services, we're using code signing certificates and access gateways that verify these certs.



A problem we've run into is that most of our internal users in these environments connect via Citrix Xenapp servers.  In order to limit the management of certs in this environment, the code signing certs are added to the local machine, rather than individual, stores.  This allows any user of these machines to connect to the services without our having to make sure the certs are installed in everyone's profiles, etc.  We had a release that was going live today that served as integration between two services, but found they couldn't talk to each other.  It turns out that access to certs was the issue.



When installing these certs, we simply installed them by double-clicking them and following the import wizard. We'd tell the wizard to install them into the local machine store and then used the winhttpcertcfg utility that comes with Windows to grant only specific groups access to these certs.  When put into place, though, on our test systems we found that no users could use the service.  When we checked permissions on these certs, the users definitely had been granted access to them, but it just wasn't working.



When we had users log into machines that had the cert and asked them to run the command:



winhttpcertcfg -l -c LOCAL_MACHINE\TrustedPeople -s certname



They would get the following error:



Error: Access was not successfully obtained for the private key. This

can only be done by the user who installed the certificate.



When an admin, namely myself, ran the same command we'd see that the users had been explicitly granted access.  This made no sense whatsoever, and in googling the error I found I wasn't alone in my confusion.  During this search, I came across a utility that's put out by Microsoft called the FindPrivateKey tool.  After using this spiffy little tool, I found that the cert was actually installed in MY profile.  It was showing as part of the local machine store, but was in C:\Documents & Settings\me\Application Data\Microsoft\Crypto\RSA.  It was actually supposed to reside in C:\Documents & Settings\All Users\Application Data\Microsoft\Crypto\RSA.  Well, that would explain why it worked fine for me, now we just needed to find out why it was going there.

 

When fighting with a problem, I often find it's easiest when I've hit a roadblock to try and rethink the issue.  Importing the cert via the wizard wasn't generating any errors, but it also wasn't putting it into the right place.  All of the tools were giving me error messages or information that lead to dead ends.  On a whim, I decided to change my first step and instead of running the wizard by double-click it, I'd trying importing the cert directly into the Certificates MMC.  Now, a quick word of instruction: you can't just do Start -> Run -> certmgr.msc as this will just take you to your personal store.  You have to launch MMC, add the Certificates snapin and choose Computer Account, Local Machine in order to get to the store you want.

 

When I did the import this way, I got a completely different error:

 

An internal error occurred. This can be either the user profile is not accessible or the private key that you are importing might require a cryptographic service provider that is not installed on your system.

 

Aha!  I love different errors, especially when they lead me to the solution.  So, after reading this article, I checked out the permissions on the RSA folder in All Users and sure enough they were different.  Oddly enough, they seemed less restrictive than what's listed in the article, but I decided to set them as written to test.  And, it worked!  When I installed a cert this time, it went into All Users.  If I ran the winhttpcertcfg command above, it would show that the users listed in the article were the ones who had access to the cert.  In other words, all winhttpcertcfg really appears to do is set NTFS permissions on files.  Ah, obscurity!

 

Okay, solution's now in hand, the day's halfway over, release is tonight and I have 75 servers that all need to have their configuration updated...whatever does one do?  You break out the time honored skill of writing batch files, of course!  I compiled all of the files I would need to make this work right into a folder.  When running scripts across multiple servers, consistency is key.  In this environment, I've striven for as much as I can, but these some servers are cycled on an annual basis as we retire old hardware.  Consistency's an ideal, but not implemented perfectly.  Compiling what I need and pushing it out to each server as part of the process really ensures it'll work. 

 

The first batch file, setupfixes.cmd:

 

Mkdir \\%1\l$\certs
copy *.* \\%1\l$\certs\
psexec \\%1 L:\certs\permfixes.cmd

rd /s /q \\%1\l$\certs

 

I run this by issuing a for loop:

 

for /f %x in (serverlist.txt) do setupfixes.cmd %x

 

serverlist.txt contains simply a list of servers, one per line.  As the loop iterates through, it simply replaces %x with the server name.   This batch creates a new folder on the remote machine, copies all of the necessary files into it, runs a second batch using psexec from Sysinternals and then deletes the folder and all files since it contains our certs that we want to restrict access to.  The second batch, permfixes.cmd:

 

cscript //h:cscript

L:\certs\xcacls.vbs "T:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA" /G everyone:;E1234589B /G Administrators:F /G SYSTEM:F /T /S  /I Remove /SPEC B

winhttpcertcfg -g -i L:\certs\cert.pfx -c LOCAL_MACHINE\TrustedPeople -a Administrators -p password

winhttpcertcfg -g -c LOCAL_MACHINE\TrustedPeople -s cert -a "domain\client users"

winhttpcertcfg -r -c LOCAL_MACHINE\TrustedPeople -s cert -a everyone

winhttpcertcfg -g -c LOCAL_MACHINE\TrustedPeople -s cert -a Administrator

winhttpcertcfg -r -c LOCAL_MACHINE\TrustedPeople -s cert -a Administrators

 

I'm using xcacls.vbs because the permissions from the article are more granular than what can be set by cacls.  Again, our build process specifies we set the scripting host to cscript, but that doesn't guarantee everyone's done that.  Easy enough to ensure in advance, though.  I set the permissions on the RSA folder to match what MS says is the default.  I'm honestly not all that happy about these are they're fairly open, but I needed to get this out to production.  I push these perm changes down the tree and tell it to remove inheriting permissions from higher levels.  Finally, we import the cert, grant access to the users we want to have it, remove admins and everyone, and add the local admin. 

 

Why remove admins, but add the administrator?  We want to restrict access to this cert to a limited set of users.  Admins like me shouldn't be able to use it as it would give them escalated privliges in the environment.  But, we might need to do something with it at a later date, so we leave a back door.  Since login as local admins is an audited event, we'll be notified of any potential security breach.

 

I've tried to include as much detail in this solution as possible because I had a hard time finding it myself.  My hope is that when someone googles either "Error: Access was not successfully obtained for the private key. This can only be done by the user who installed the certificate" or "An internal error occurred. This can be either the user profile is not accessible or the private key that you are importing might require a cryptographic service provider that is not installed on your system." they'll come here and be able to fix their issues.  I've put the errors in twice to make sure it gets indexed well. :)

 

 

 

 

Friday, January 1, 2010

My first iPhone Webapp

At work, I am a serious scripter.  I try to use VBscripts for almost every repetitive task I do for the simple reason that I make mistakes, computers don't.  If I have to edit the properties for 200+ Citrix-published applications (a task I've had to do numerous times), the chances of my breaking things is much greater than if I just get it scripted and tested once and let the script do the heavy lifting. 


But, VBscripts just haven't been cutting the mustard for some of the more onerous tasks in recent months, and so I took advantage of the fact that Microsoft offers Express editions of Visual Studio 2008 as free downloads.   (Recently, my company got everyone in my group MSDN subscriptions, so I was able to get VS2008 Pro to use!)  I had been wanting to get more familiar with Visual Studio and app development in general as I had a couple of things I wanted to try doing.  Figuring the best way to learn was to find a project I wanted to do and dive it, I thought about it for a while until I came up with...


For years my home has been controlled via a home automation system called HomeSeer.  HomeSeer's a great product, put out by a company that stands firmly behind their products, with a great user community that's always ready to help.  One of the pieces of HomeSeer I've played around with over the years is its scriptability.  It has its own .Net-based webserver built in and will run fairly standard ASPX pages as well as VBscripts.  By just adding some references into your project, you can also control it via other languages as well.


And then, of course, there's my ubiquitous iPhone.  Last year I decided to give iPhone development a try and so got a cheap Mac, downloaded the SDK and gave it a shot...and realized after looking at the docs for about 10 minutes that Objective-C is not an easy transition for a VB guy to make.  So, I put it on the sidelines for a while.  Recently, though, I came across iUI.  This is a framework put together by the founder of Facebook that allows developers to very easily create iPhone-based Webapps.  Since it's agnostic to the underlying development framework, I had found my project: I would create my own iPhone interface to HomeSeer.


I see that others have done this, but I had some specific needs I wanted to accommodate.  And, I wanted to learn.  Creating this from scratch would give me a lot of experience with so many different areas, it was too good to pass up.  And, so, I give you....


 



 


Looks pretty cool, right?  It doesn't actually do anything yet, but it's got a lot of stuff going on behind it.  First, it connects to HomeSeer and enumerates all of the devices and gets their status.  Then, it generates this nifty page with a button that's named the opposite of the device's status.  Eventually, the plan is that you'll be able to tap one of those buttons and turn lights off and on.  But, since HomeSeer is very extensible and can be used to control almost anything, so too will this app be able to do a whole lot more. 


Not too bad for an hour's worth of fiddling.  In coming installments, I'll document what I did to get everything setup and in place to make the magic happen because a lot was either poorly documented or the documentation was spread over multiple locations.