A Guide to Signing Un-signed Drivers (Part 1)

This is a cleaned-up version of a post I made a while ago on ITNinja (http://www.itninja.com/question/guide-to-signing-unsigned-drivers). I’ve also incorporated a few changes for Windows 7 etc (using a later version of the WDK) and thanks goes to Rowan Heuvel over at ITNinja (Rheuvel) for these additions. I’ve not actually gone through it all from start to finish for a good while, so feel free to offer any feedback. Be careful of the quotes when copying and pasting (specifically for the custom actions) – quotes must be straight quotes!

Part 2 of this driver signing series can be found here, and part 3 can be found here.

Tools you need (most are from the Windows Driver Kit – I placed all of these in the same folder):

Inf2Cat.exe (To generate the unsigned catalog file from our INF)

WindowsProtectedFiles.xml

Microsoft.Whos.Shared.IO.Cabinets.dll

Microsoft.Whos.Shared.IO.Catalogs.dll

Microsoft.Whos.Shared.Xml.InfReader.dll

Microsoft.Whos.Winqual.Submissions.SubmissionBuilder.dll

Microsoft.Whos.Xml.NonXmlDataReader.dll

Makecert.exe (Used to create our certificate)

Cert2spc.exe (Create Software Publisher’s Certificate (SPC) from our certificate)

Signcode.exe (Sign our catalog file with an Authenticode digital signature. Note – this has been replaced with Signtool.exe in later versions of the WDK)

pvk2pfx.exe (Copies public key and private key information contained in .spc, .cer, and .pvk files to a Personal Information Exchange (.pfx) file. Note – this is present in later versions of the WDK and must be used before Signtool.exe.)

Certmgr.exe (Used to add and delete our certificate to the system root)

DifxApp.msm (Difx Merge Module to install the driver)

Introduction

Now we have our toolset in place, let’s package the unsigned Captain Planet printer driver. We’re packaging this driver for a customer called ‘Planeteers Ltd’. Let’s assume we’ve captured the Captain Planet printer driver using a snapshot tool. We can see from the resultant snapshot that there is a file in [WindowsFolder]\inf called ‘captainplanet.inf’. There are also files called ‘captainplanet.sys’ and ‘captainplanet.dll’ in [SystemFolder]spool\drivers\w32x86. All of these files make up the Captain Planet printer driver so let’s create a folder anywhere on your work machine (say, “c:\cpdriver”) and copy the three files to it. We should now have our unsigned driver in a temporary folder called “C:\cpdriver” which contains:

captainplanet.inf

captainplanet.sys

captainplanet.dll

Now let’s generate a customer certificate, so we can sign this (and many more) driver(s).

Step 1: Signing the Driver and Verifying the Signature

Step 1a: Create .cat (catalog) file for driver.

We notice that the Captain Planet driver doesn’t contain a cat file, so we’ll need to generate one. Open the .INF file in a text editor. Ensure that under the [version] section that you have an entry specifying a .cat file. If it’s not there, add the line at the end of the section. For example:

[version] Signature=xxxxxx

Provider=xxxxxx

CatalogFile.NTx86=captainplanet.cat

“captainplanet.cat” is the name of the cat file that we want to generate. Not having a line specifying this will result in an “error 22.9.4 – Missing 32-bit catalog file entry” when we run Inf2Cat.exe. “NTx86” represents the Windows 2000 x86-based platforms.

Command line:

(Windows XP)

Inf2Cat.exe /driver:"C:\cpdriver" /os:XP_X86

(Windows 7)

Inf2Cat.exe /driver:"C:\cpdriver" /os:7_X86

Running this successfully will generate captainplanet.cat in the ‘C:\cpdriver’ folder.

Step 1b: Create authenticode certificate and set private key

Create another folder called ‘c:\PlaneteersLtd_certificate’. It is here where we’ll create our customer-specific certificate and private key. Remember that this certificate can be reused multiple times for the customer (Planeteers Ltd) to sign different drivers, so keep naming conventions generic to your customer.

Command line:

MakeCert.exe -r -pe -n "CN=PlaneteersLtd" -sv "c:\PlaneteersLtd_certificate\PlaneteersLtd.pvk" -len 2048  "c:\PlaneteersLtd_certificate\PlaneteersLtd.cer"

Running this will ask you to set a private key. Make a note of this key! Running this command will generate:

c:\PlaneteersLtd_certificate\PlaneteersLtd.cer and c:\PlaneteersLtd_certificate\PlaneteersLtd.pvk (I think certificates of this kind are actually supposed to be used for development/testing as opposed to a live environment. It’s probably advisable to purchase a certificate from Verisign/Comodo/whoever if you have the budget.)

Step 1c: Create Software Publisher’s Certificate (SPC) from our certificate

Command Line:

cert2spc.exe "c:\PlaneteersLtd_certificate\PlaneteersLtd.cer" "c:\PlaneteersLtd_certificate\PlaneteersLtd.spc"

This will generate c:\PlaneteersLtd_certificate\PlaneteersLtd.spc

Step 1d: Sign the catalog file

Command line:

SignCode.exe -spc "c:\PlaneteersLtd_certificate\PlaneteersLtd.spc" -v "c:\PlaneteersLtd_certificate\PlaneteersLtd.pvk" -t http://timestamp.verisign.com/scripts/timstamp.dll "C:\cpdriver\captainplanet.cat"

This will prompt you for the private key you set earlier. Enter it when prompted.

If you are using a later version of the WDK, you will need to use SignTool.exe instead. Because SignTool.exe requires a .pfx file (Personal Information Exchange), there is an additional step involved.

Command line:

pvk2pfx.exe -pvk "c:\PlaneteersLtd_certificate\PlaneteersLtd.pvk" -spc "c:\PlaneteersLtd_certificate\PlaneteersLtd.spc" -pfx "c:\PlaneteersLtd_certificate\PlaneteersLtd.pfx" -pi "<private key password>"

Command line:

signtool.exe sign /f "c:\PlaneteersLtd_certificate\PlaneteersLtd.pfx" /p "<private key password>" /t http://timestamp.verisign.com/scripts.timstamp.dll "C:\cpdriver\captainplanet.cat"

It’s important to timestamp the signature. Timestamping means that even when the code signing certificate expires, the signature will remain valid indefinitely.

Step 1e: Verifying the signature

You can verify the catalog is signed by adding your certificate to the trusted root and then running the following command:

signtool.exe verify /pa /v /c "C:\cpdriver\captainplanet.cat" "C:\cpdriver\captainplanet.inf"

If the catalog is signed correctly you should see the following at the end of the signtool output:

Successfully verified: C:\cpdriver\captainplanet.inf
Number of files successfully Verified: 1
Number of warnings: 0
Number of errors: 0

Step 2: Adding the Customer Certificate

The catalog for our driver is now signed. Every time we install this driver using DifxApp, we need to ensure the customer certificate is installed on the machine before DifxApp installs the driver. To do this via a windows installer (alternatively we could just deploy the certificates out to all machines using group policy and skip this step), we use CertMgr.exe in two Custom Actions (CA) – one to add the certificate on install, and one to remove it on uninstall. We can do this in two ways:

a) Create the two CAs in every single driver package you do

b) Create a merge module which can easily be incorporated in every driver package you do In my opinion option b) would provide less hassle for me because after all, we want our certificate to be easily re-usable for multiple driver packages. So for now, we’ll do option b).

Step 2a: Create a merge module and add certificate.

Create a new merge module and add captainplanet.cer to the SystemFolder of the MSM (or wherever you deem appropriate)

Step 2b: Add Custom Actions to add and remove the certificate

Go to the Custom Action (CA) table and create 2 CAs, both are an ‘Exe stored in binary table’ and ‘Deferred in a System Context’. If you use the CA wizard, you should stream ‘certmgr.exe’ into your binary table. Here’s what (roughly) your CustomAction table should look like after you’ve made them:

Name: addCertificate

Type: 3074

Source: certmgr

Target: -add “[SystemFolder]PlaneteersLtd.cer” -s -r localMachine ROOT

 

Name: removeCertificate

Type: 3074

Source: certmgr

Target: -del “[SystemFolder]PlaneteersLtd.cer” -s -r localMachine ROOT

 

And here is the Binary table:

 

Name: certmgr

Data: <binary data>

Of course, using the wizard to do both CAs may result in two entries in your binary table containing the same exe (certmgr.exe). Obviously this is a waste, so remove one of them and set the ‘Source’ column in your CustomAction table appropriately.

UPDATE 1: In windows 7 environments we may also use certutil which is part of the operating system:

Too add: certutil -addStore “Root” “[SystemFolder]PlaneteersLtd.cer”

Too remove: certutil -delStore “Root” xxyyzz

‘xxyyzz’ is the Serial Number of the certificate, which can be obtained (after importing the certificate) by: certutil -store “Root”

UPDATE 2: We should also add/remove the certificate to/from the TrustedPublisher store. See the end of part 2 of this driver series for more information.

Step 2c: Add Custom Actions to the installation sequence

Go to the ModuleInstallExecuteSequence table and add the following:

Action: addCertificate

Sequence: <null>

BaseAction: InstallFiles

After: 1

Condition: Not Installed

 

Action: removeCertificate

Sequence: <null>

BaseAction: InstallInitialize

After: 1

Condition: REMOVE~=”ALL”

 

Action: InstallFiles

Sequence: 4000

BaseAction: <null>

After: <null>

Condition: <null>

 

Action: InstallInitialize

Sequence: 1500

BaseAction: <null>

After: <null>

Condition: <null>

The sequencing above is important as the addCertificate CA needs to run after the certificate is put down on the machine (after InstallFiles) and the removeCertificate CA needs to run before the certificate is removed from the machine. It also needs to run before DifxApp installs the driver.

STEP 3: Create the Driver Package

Great. At this point we should now have a signed driver, and a merge module which installs our customer certificate. Now we can create the MSI package which installs our signed driver:

Step 3a: Create a new ISM/WSI project and make a folder somewhere for your driver (one folder per driver remember).

Let’s say: “[ProgramFilesFolder]CaptainPlanetDriver” for this example. This folder will contain all the files in ‘c:\cpdriver’ on our work machine: captainplanet.inf captainplanet.sys captainplanet.dll captainplanet.cat (the one we’ve just generated!)Remember that all these files should be in the SAME COMPONENT as well as the same folder (one .inf per folder), and the keypath of the component needs to be the .inf file! Let’s call the component: “CaptainPlanet_DRIVER”

Step 3b. Install the driver using DifxApp.

When we add the latest version of the DifxApp.msm merge module, it will create a table in the project called “MsiDriverPackages”. Installshield and Wise have slightly different wizards to install a driver, so I’ll just show you roughly how the MsiDriverPackages table should be populated:

Component: CaptainPlanet_DRIVER

Flags: 7

Sequence: 1

ReferenceComponents: <null>

Once this is done, compile your .MSI. Ensure in your .MSI that your ‘addCertificate’ CA runs after ‘installFiles’ and before ‘MsiProcessDrivers’ in the installExecuteSequence. At last, we’re done!

Step 4. Final Thoughts

Now I’d highly recommend testing your MSI install with verbose logging enabled, as the DifX merge module does write some half-decent logging which can assist you. For example, just recently DifxApp verbose logging warned me that some DLLs (part of the driver) were not being copied to the driver store. This was the reason why my driver wasn’t working, and so I had to edit the .INF file [SourceDisksFiles] section (Add the relevant files which weren’t being copied to the driver store), re-generate the cat file, re-sign it and then re-add these updated files to my MSI (Note there was obviously no need to go fiddling with my MSM as the certificate remains the same). It’s also always best to test your driver with the appropriate hardware. Anyway. I hope this guide can be of some help to others. If you break your machine, or yourself, during implementing this guide then don’t blame myself. It’s only meant as a guide.