Practical Windows Code and Driver Signing

Code and driver signing for Microsoft Windows 8, 7, Vista, and XP

Introduction

If you have ever installed some software or drivers in Windows, you have probably seen a dialog telling you the name of the company or person that published that software. This means that the publisher has cryptographically signed their work. Signing your software is important: by showing a nicer dialog to the end user, it gives end users more confidence that they are not installing malware. In the case of device drivers, signing is even required by certain versions of Windows in certain situations.

If you are a developer figuring out how to sign drivers or software, the aim of this guide is to tell you everything you need to know so that you can do it correctly.

My name is David Grayson and I work at Pololu Robotics & Electronics. In 2012 I went through the process of signing all of our company's USB drivers and most of our installers for Windows. I encountered so many problems along the way that could have been easily avoided if someone had told me about them ahead of time. If you are going through the same process, I sincerely hope that this document can clear up all of your confusion and save you a lot of time. I learned the hard way and now you can learn the easy way.

A lot of this information can be verified in official Microsoft documentation found on MSDN, and I will try to cite the official documentation when needed. The authoritative documents on kernel-mode code signing are kmsigning.doc and KMCS_walkthrough.doc. These are pretty good resources, but they are from 2007 and thus contain no information about Windows 7, Windows 8, or SHA-2. Also, their scope is more limited than the scope of this document because they don't talk about signing executables. Therefore, a lot of the things I say here are actually conclusions that I have drawn from my own experiments. When I am telling you something that I determined experimentally, I will use phrases like "it seems like" or "in my experience". When my experiments contradict the official documentation I will say so.

If you think any of the information I am providing here is wrong, please post a comment and let me know so we can figure it out.

This document only covers Windows XP 32-bit, Windows Vista 32-bit and 64-bit, Windows 7 32-bit and 64-bit, and Windows 8 32-bit and 64-bit.

The most useful part of this document is the signature requirements section.

The RSA cryptosystem

Pretty much every secure thing you do with a computer, including code and driver signing, uses the RSA cryptosystem invented by Rivest, Shamir, and Adleman in the 1970s. I am not going to really explain the mathematics behind it, but I will give you an idea of what RSA lets us do. This will help you understand what a digital signature actually is and why it works.

The first thing RSA gives us is a way to generate a key pair, which consists of a public key and a private key. As the names suggest, the private key must be kept secret, but you can give the public key to anyone.

The second thing that RSA gives us is a pair of functions. The public key provides a function that we will call f. The private key provides a function that we will call g. Do not worry about what the exact inputs or outputs of these functions are. The important properties of these functions are:

  • f and g are inverses of each other: f(g(x)) = x and g(f(x)) = x.
  • It is very very hard to determine g from the public key (or f).

Encrypting and decrypting a message can be done with the functions f and g respectively. Basically, any sender can encrypt a message by passing it through the f function from receiver's public key. Then the receiver is the only one who can read the encrypted message, and he does so by applying g to it.

Signing and verifying a message can be done with the functions g and f respectively. The sender passes his message (or a cryptographic hash of it) through the g function from his private key to make a signature for the message. The sender is the only one who can do this because he is the only one with access to g. Anyone who receives the message and signature can verify the signature by passing it through the f function from the public key and making sure that everything matches up. This is exactly what Windows is doing for you behind the scenes whenever it verifies a signature on a piece of software and tells you who the publisher is.

At a deep level, the RSA cryptosystem works because it is very hard to factor large numbers into primes. The private key mainly consists of two very large primes, and the public key mainly consists of their product. Wikipedia has more details about how RSA works, of course.

Anatomy of a signature

Windows has a series of dialog boxes that allow you to view the details about a signature embedded in a file. It is important that you know your way around these dialogs because they will help you understand the nature of the signature you are applying to your software.

If you right-click on a signed file and go to Properties, you will see a Digital Signatures tab.

In the Digital Signatures tab, you can click on Details to open the Digital Signature Details dialog box. The digital signature is created by the publisher of the software.

You can click on View Certificate to view the certificate that is embedded in the file's signature. The certificate is purchased from a certification authority such as Verisign.

You can click on Certification Path to view most of the certificates in the chain of trust. The point of these certificates is to prove that your certificate was issued by some trustable company. Sometimes the certification path shows the certificates from the cross-certificate, which is a special certificate you can include in your signature when you sign it in order to extend the chain of trust further back. You can double-click on any certificate visible in the certification path to get information about it.

Some day I might expand this section to include details about the different fields you can see in these dialogs, and why those pieces of information are necessary. For now, you should click around yourself and explore some signatures. Feel free to download wixel-signing-experiment.zip and look at the signatures on the included files.

You can also use a hex editor such as WinHex to examine the embedded signatures; you can easily see the names of the signer and the organizations in the certification path. One great feature of WinHex is that it lets you compare two files and highlights the differences in them, so you can see exactly which bytes in the header are modified and which bytes are appended to the end when the signature is added.

Signature requirements

During the development process, you should make sure that your digital signature meets all the necessary requirements documented below. The requirements are summarized in the tables below, and then the terms in the tables are defined and explained after the tables. I strongly suspect that this list is incomplete, so please post a comment if there is anything to add to it.

If you just care about your software working and don't mind if the user sees a scary warning message, these are the requirements your signature needs to meet in my experience:

Signature requirements for it to just work
Running an executable Installing a driver package Loading a kernel module
on Windows XP none none none
on Windows Vista 32-bit No SHA-2 none none
on Windows Vista 64-bit No SHA-2 none MCVR & SHA-1
on Windows 7 none none MCVR & SHA-1
on Windows 8 none TRCA MCVR

If you care about your software working AND you want to show up as the verified publisher in any warning dialog boxes and any Properties dialogs, these are the requirements your signature needs to meet in my experience:

Signature requirements for it to look good
Running an executable Installing a driver package Loading a kernel module
on Windows XP TRCA WHQL ?
on Windows Vista 32-bit TRCA & No SHA-2 TRCA ?
on Windows Vista 64-bit TRCA & No SHA-2 TRCA ?
on Windows 7 TRCA TRCA ?
on Windows 8 TRCA TRCA ?
TRCA
The signature's chain of trust must go back to a certificate in the user's Trusted Root Certification Authorities list. As you can see in certmgr.msc, the TRCA list has certificates from several well-known companies such as a Verisign, Globalsign, Digicert, and Go Daddy. Certain certificates are not in the list initially but are automatically downloaded from Windows Update during signature verification when they are needed. This is called the Microsoft Root Cerificate Program. However, I would not rely on the auto-update. Logically, it shouldn't work if the computer is disconnected from the internet. In my experience, even with an internet connection it does not always work reliably. Therefore, your best bet is to make sure your chain of trust goes back to a certificate that is included in fresh installs of Windows. Unfortunately, I don't have an authoritative list of those certificates. The TRCA requirement is documented in kmsigning.doc. You typically don't need a cross-certificate to meet this requirement. I suspect that the "Trusted Publishers" or "Trusted People" lists would work just as well, if you convince your users to install a certificate there.
MCVR
The signature's chain of trust must go back to the Microsoft Code Verification Root certificate, or some other certificate that is trusted by the kernel. Any WHQL signature should satisfy this requirement. This is documented very clearly in kmsigning.doc, which explains that the kernel does not have access to the Trusted Root Certification Authorities list. A cross-certificate is typically needed to satisfy this requirement. Microsoft publishes a complete list of the Cross-Certificates for Kernel Mode Code Signing.
WHQL
The signature must come from the WHQL program. My understanding is that you have to pay a lot of money to Microsoft or some third party and in exchange they will test your driver. If your driver is OK, they will sign your driver and give you legal permission to use the Windows Logo to sell your product. WHQL is never actually required for your software or drivers to work and is much more expensive and restrictive than just using a standard code signing certificate. I have never gotten a driver WHQL-signed, so my experience with it is limited.
SHA-1
A signature must be present and it must not use SHA-2 in any way, only SHA-1.
No SHA-2
If a signature is present, it must not use SHA-2 in any way.

Internet Explorer requirement: If you are signing an executable that will be available for download on the internet, make sure to use the /tr option instead of /t when you are invoking signtool.

Microsoft is retiring SHA-1: In Microsoft Security Advisory (2880823), published on , Microsoft has announced that it will no longer allow root certificate authorities to issue SHA-1 code signing certificates after . As of 2014-09-12, GoDaddy will not issue you a SHA-1 certificate that extends into 2016, and I think that GlobalSign is behaving the same way (based on a hint a I saw in their user interface during a webinar). I hope that by 2016 Microsoft will fix all of the problems with SHA-2 that are documented here.

The content above is a concise summary of all the code and driver-signing requirements I know about. In the next three sections, I will explain each of the requirements and what you can expect if your software does not meet them.

Running an executable

Digital signatures can be directly embedded inside executables (EXE files). They can also be embedded inside Windows Installer files (MSI files), and everything that I will say about executables also applies to Windows Installer files. Windows verifies the signature inside an executable file in two situations:

  • If the file was downloaded from the internet (including network drives), Windows will show a "Open File - Security Warning" message when the user tries to run the file. The publisher information in the warning comes from the signature embedded in the file. Usually the warning is a simple dialog box, but in Windows 8 the warning is sometimes a SmartScreen dialog that takes over the whole screen. Two examples are shown below:
  • If the executable requests administrator privileges, which is also known as elevating, Windows will display a UAC prompt. The publisher information in the prompt comes from the signature embedded in the file. Two examples are shown below:

Windows has never required signatures on executables. However, it is nice to sign your executable so that whenever the user sees a warning message about it, they will see your name as the publisher instead of being told that the publisher is unknown.

In my experience, in order for your signature to work properly on an executable, it should have a chain of trust that goes back to a certificate in the user's Trusted Root Certification Authorities list, which you can see by running certmgr.msc. Also, in certain cases it is very important to avoid using SHA-2.

Do not use SHA-2 for an executable

If you use SHA-2 in the signature of an executable, expect trouble. On Windows Vista, when the end user double-clicks on a downloaded executable with a signature that uses SHA-2, nothing happens! There is no error message or activity of any kind.

If you don't believe me, feel free to try it yourself by downloading this zip file to a Windows Vista computer: wixel-signing-experiment.zip.

I believe that there is some code in Windows Vista that checks the signature of the executable in order to show the publisher in the warning dialog for downloaded executables. That code apparently cannot handle SHA-2, and will silently exit. One workaround is to run the executable from the Command Prompt, thereby bypassing the warning dialog and the signature checking that goes along with it.

This is a really nasty issue because even if you use the verify feature of signtool AND you test your installer on Windows Vista prior to release, you will not catch the problem! It will only manifest itself when you actually put the executable on your website and Windows Vista users start downloading it.

Also, this violates the common-sense principle that adding a signature should not hurt you; it should be at least as good as having no signature, as long as you haven't used that signature to distribute malware in the past.

It seems like this problem doesn't affect installers created with NSIS, and I think I know why. NSIS installers are always pre-emptively elevated by Windows, so when you run an NSIS installer you will always see the standard UAC warning and never see the special warning that results from running a downloaded executable. In that way, the buggy code in Windows Vista is bypassed.

To make sure that your signature uses SHA-1 instead of SHA-2, be sure to check not only the "Digest algorithm" of the signature, but also the "Signature algorithm" and "Thumbprint algorithm" of every certificate in your certification path.

2014-09-12 update: I have not tested it, but KB2763674 provides an update that seems to describe this problem with SHA-2 executables and claims to fix it. The update is available from Windows Update, but I don't know how many Windows Vista users will have it in practice, so it is best to keep using SHA-1 as long as you can. (Thanks to Yuhong Bao for letting us know about this on the MSDN thread.)

Use /tr to specify the timestamp server

Internet Explorer has a built-in security scan feature that will apparently check the signatures of executables right after they are downloaded. I found that if you use the /t option of signtool to sign your executable and then put it up on the internet for direct download (i.e. you don't put it inside a zip file or installer), then Internet Explorer tells people who download it that "The signature of this program is corrupt or invalid." and makes it hard (but not impossible) for them to run the program. The solution is to use the /tr option to specify the timestamp server when invoking signtool.

I determined this by doing an experiment with Internet Explorer 10.0.9200.16686 on Windows 7 64-bit SP1, and my signatures were made with the signtool from the Windows Software Development Kit (SDK) for Windows 8 (version 8.0). I also got the same results with Internet Explorer 10.0.9200.16688 on Windows 8 64-bit.

This violates the common-sense principle that adding a signature or a timestamp should not hurt you; it should be at least as good as not having that thing. Internet Explorer was perfectly happy with the signatures I made that had no timestamp. Even if an executable is totally unsigned, Internet Explorer just gives a warning to the user and still lets the user easily run the program. I do not see how a timestamp made with /t makes an executable more dangerous to the user.

Installing a driver package

A driver package consists of a single INF file and the files that it references. You can have multiple INF files in the same directory, but in my experience Windows treats each INF file as a separate and independent driver package. A driver package can be signed by first generating a security catalog (CAT) file with cryptographic hashes of all the files, and then embedding a signature in the security catalog. The security catalog contains a list of file names and a hash of the contents of each file; you can simply double-click on it to inspect the information it contains and see its signature.

There are at least three ways to install a driver package. First, the user can right-click on the INF file and select "Install" if the INF file has a DefaultInstall section. Second, a program can call the SetupCopyOEMInf function. Third, a user can right-click on a matching device in the Device Manager, select "Update driver software...", and then tell Windows the directory where the driver package is stored.

When the driver package installation is initiated, Windows will check for a signature and behave differently depending on what it finds; different versions of Windows behave differently.

Driver package installation in Windows XP

Back in Windows XP, it seems that the only kind of driver package signature Microsoft cared about was a WHQL signature. You can have a driver package that displays a nice install prompt in Windows Vista and up that indicates who the publisher is, but installing the same driver package in Windows XP results in the following warning:

The software you are installing has not passed Windows Logo testing to verify its compatibility with Windows XP.  (Tell me why this testing is important.)  Continuing your installation of this software may impair or destabilize the correct operation of your system either immediately or in the future.  Microsoft strongly recommends that you stop this installation now and contact the software vendor for software that has passed Windows Logo testing.

Driver package installation in Windows Vista and 7

Starting in Windows Vista, Microsoft changed their tune. Instead of warning users about whether or not the drivers have passed WHQL testing, Windows Vista and 7 warn the user about whether the publisher is verified or unverified. To show up as the verified publisher, you need to provide a CAT file with a proper signature. The requirements are documented in kmsigning.doc and in the signature requirements section above.

If you sign your driver package properly, users will see a friendly prompt when they install it in Windows Vista, 7, or 8:

The main question in the prompt is "Would you like to install this device software?" and it shows the name of the package along with the publisher.  There are two options: Install and Don't Install.  There is one tiny warning that says: You should only install driver software from publishers you trust.  How can I decide which device software is safe to install?

The name in the prompt comes from from the INF file's DriverPackageDisplayName directive and the publisher comes from the verified signature on the CAT file.

However, if you don't sign your driver package, users will see a big red warning when they install it in Windows Vista or 7:

The main sentence of the warning is "Windows can't verify the publisher of this driver software".  The two options available are "Don't install this driver software" and "Install this driver software anyway".

I think it was a good idea for Microsoft to make that change. WHQL testing is expensive and inflexible. If you change one byte of your driver, you would have to re-submit it to be tested again. Regular code signing is easier and cheaper: you can get a certificate for a couple hundred dollars per year that lets you sign as many driver packages as you want. This probably resulted in more companies making signed drivers, so the malware (and small companies like us) stood out more.

Driver package installation in Windows 8

Starting in Windows 8, all driver packages have to be signed. Unfortunately, I have not seen any official document from Microsoft about this change, even though I asked about it on StackOverflow.

If you try to install an unsigned driver package which previously worked on older versions of Windows, you will get a simple error message:

Install Error: The third-party INF does not contain digital signature information.

This wasn't necessarily a bad decision on Microsoft's part, but it was bad news for a lot of small companies such as ours and individuals making USB devices on a small scale. For years, we had successfully distributed unsigned driver packages that worked fine in Windows XP, Vista, and 7 because they relied on kernel modules (SYS files) that are signed by Microsoft, in particular WinUSB and usbser.sys. Starting with Windows 8, we had to figure out the driver signing process or tell our Windows 8 customers to follow the complicated procedure for disabling driver signature enforcement.

If you are new to the industry and want to start making USB devices, the vendor ID from the USB-IF will cost you $2,000 and the code signing certificate required for Windows 8 support will probably cost a few hundred dollars per year. However, your signatures should keep working after the certificate expires if you make sure to use a timestamp when signing.

The friendly driver installation prompt for signed driver packages in Windows 8 looks pretty much the same as it did in Windows Vista and 7.

Loading a kernel module

Some driver packages contain kernel-mode code (SYS files) that need to get loaded into the kernel at some point, typically when a matching device is plugged into the computer.

Starting with Windows Vista 64-bit, kernel modules must come with a properly-signed security catalog (CAT file) or else they cannot be loaded into the kernel. In July 2007, six months after the release of Windows Vista, Microsoft published two documents about the new signing requirements: kmsigning.doc and KMCS_walkthrough.doc.

If your driver only uses WinUSB or usbser.sys, all you need to worry about is getting your driver package installed, as described in the Installing a driver package section. The kernel modules you are using have already been signed by Microsoft and you will have no trouble getting them loaded into the kernel after the driver package is installed.

The signatures for kernel-mode code are typically kept in the security catalog (CAT file) for the driver package, but in the case of a boot-start driver you are supposed to embed the signature inside the SYS file itself, according to kmsigning.doc.

The signature's chain of trust must go back to the Microsoft Code Verification Root certificate, or some other certificate that is trusted by the kernel. Any WHQL signature should satisfy this requirement. This is documented very clearly in kmsigning.doc, which explains that the kernel does not have access to the Trusted Root Certification Authorities list. A cross-certificate is typically needed to satisfy this requirement. Microsoft publishes a complete list of the Cross-Certificates for Kernel Mode Code Signing.

One important detail is that the signature can come from any security catalog installed on the system; the signature does not actually have to be in the security catalog for the INF file that is matching the device. In other words, if Corporation X makes a kernel module and properly signs it for Windows Vista/7, Corporation Y can make a driver package for their own product that uses the kernel module and Corporation Y does not have to provide any kind of signature for it to work in Windows Vista/7. This is not particularly surprising if you think about it: the dangers of loading code into the kernel depend only the code itself, not the device or INF file it is being used with. In fact, this detail is what allows our CP2102 USB-to-Serial Bridge Driver to work on Windows XP/Vista/7/8. The signature that I put on our catalog file (plluvcp.cat) does not meet all the requirements to get silabser.sys loaded into the kernel, but the signature that Silicon Laboratories put on their catalog file (slabvcp.cat) does, and that's all that matters.

When it is time load a SYS file into the kernel, it seems that Windows will scan all the files in the security catalog store (C:\Windows\System32\catroot) to see if one of them contains a hash for the SYS file and an adequate signature. If it finds what it is looking for, the loading succeeds.

If Windows cannot find a properly-signed security catalog for your kernel module, the Properties dialog for your device will show a Code 52 error:

In the Properties dialog, under Device status, it will say: Windows cannot verify the digital signature for the drivers required for this device.  A recent hardware or software change might have installed a file that is signed incorrectly or damaged, or that might be malicious software from an unknown source.  (Code 52)

The digital signature for a kernel module also affects what users see in the Device Manager. Just double-click on a device using the module, select the Details tab, and click Driver details. Ideally you would want your company's name to show up in this dialog box, but I have not done enough research to really know what the requirements are. That is why I put question marks in the "Loading a kernel module" column in the table above entitled "Signature requirements for it to look good".

For each file listed, Windows displays the name of the Provider and the name of the Digital Signer, which can be different.

Use SHA-1 for kernel modules prior to Windows 8

SHA-2 is a newer family of hashing algorithms that are better than SHA-1. Unfortunately, support for SHA-2 signatures in versions of Windows prior to Windows 8 is spotty.

I failed to get a kernel-mode driver (silabser.sys) loaded into the Windows 7 kernel when its security catalog was signed with a SHA-2 certificate from Go Daddy, but the same driver package worked just fine in Windows 8. Unfortunately, Go Daddy does not offer a SHA-1 certificate whose chain of trust goes back to the Microsoft Code Verification Root, so I was unable to do the obvious experiment of switching to SHA-1 and seeing if my driver package starts working in Windows 7. I inspected some other kernel-mode drivers that I knew to work in Windows 7 and found that their signatures all use SHA-1. I forget if I specifically tested this in Windows Vista.

It's important to note that my SHA-2 signature of a driver package worked just fine in Windows 7 for the purpose of driver package installation, but the same signature was NOT good enough to get code loaded into the kernel. It seems like each version of Windows has several different, independent pieces of code that are responsible for verifying signatures, so the features and requirements of each piece of code can easily be out of sync even within a given version of Windows like Windows 7.

2013-03-11 Update: This section might be wrong! A developer at PiXCL Automation Technologies has contacted me and basically said that the claims I made in this section are false; he was able to get his driver loaded into the kernel in Windows 7 using a SHA-2 Driver Signing Certificate from Go Daddy. He just had to sign it twice, using the /fd sha1 option to support Windows 7 and the /fd sha256 option to support Windows 8. I have not had the resources to verify his claim, but he seems pretty competent and convincing to me. You can read his tutorial on Signing Windows 8 Drivers.

Choosing a certificate

You will need to choose a certificate issuer and purchase a code signing certificate from them.

Code Signing Certificate from GlobalSign

I have not tried it, but the code signing certificate offered by Globalsign looks perfect for all purposes. The marketing "datasheet" specifically claims it has "Vista Kernel Support". The GlobalSign cross-certificate uses SHA-1. The "GlobalSign Root CA" certificate will probably be installed on all your user's computers as a Trusted Root Certification Authority but it is hard to be sure. It is relatively cheap: as of December 2012, it only costs $229 per year for companies and $129 per year for individual developers, and it is cheaper if you buy three years at a time.

I recommend that you try the certificate from GlobalSign. Please post a comment letting us know how it worked out for you.

Code Signing Certificate from Go Daddy

If all you care about is signing executables or driver packages that do not include kernel-mode code (SYS files), then the Go Daddy Code Signing Certificate for $199.99/year is a decent choice. This is the certificate that we use, though we are planning to switch to GlobalSign and SHA-2 in mid-2015. If you are using usbser.sys or WinUSB for your USB driver, your driver package will not include any kernel-mode code so this certificate should work fine for you.

When you create the private key for your Code Signing Certificate, you will be prompted to choose between the SHA-1 and SHA-2 (specifically SHA-256) hashing algorithms; we picked SHA-1. You will also be asked to pick an issuing organization; we picked Go Daddy and the other choice is Starfield. You can change any of these decisions at a later time by re-keying your certificate from Go Daddy's web interface.

If you pick SHA-1 and Go Daddy, the chain of trust for your certificate will trace back to the Go Daddy Class 2 Certification Authority. That is nice because the Go Daddy Class 2 Certification Authority will probably be in the Trusted Root Certification Authorities list on all of your users' computers; you can run certmgr.msc to verify this.

Do not get a Driver Signing Certificate from Go Daddy; this product is the same price and appears to be exactly the same as a Code Signing Certificate except that they force you to pick the SHA-2 hashing algorithm.

GoDaddy will not issue you a SHA-1 certificate whose validity period extends into 2016, so if you want a SHA-1 certificate from them, you probably have to do it before 2015-01-01. (I expect most other certificate authorities are behaving the same way.)

Finding a good certificate product

To find a good all-purpose certificate, here is what I would do:

  1. Download all the cross certificates available on Microsoft's Cross-Certificates for Kernel Mode Code Signing web page. The companies listed on that page are the only ones that can provide a certificate with a chain of trust that goes back to the Microsoft Code Verification Root, so they are the only ones that can help you sign kernel modules.
  2. Open each downloaded cross-certificate and check the "Signature algorithm" and "Thumbprint algorithm" to make sure they are SHA-1. As I described above, SHA-2 should not be used to sign executables (or MSI files) for Windows Vista and cannot be used to sign kernel modules for Windows Vista/7. Therefore you should delete any certificate that uses SHA-2. As of December 2012, the only cross-certificate that uses SHA-2 is Go Daddy's.
  3. You can further narrow down your list by looking at a few different Windows computers and seeing which certificates are already installed in the Trusted Root Certification Authorities list, which is visible from certmgr.msc. For example, on my Windows 8 computer I see "GlobalSign Root CA" in my Trusted Root Certification Authorities, and that matches matches the "Subject" of one of the cross-certificates from the Microsoft. The "Subject Key Identifiers" of the two certificates also match. This tells me that Globalsign's certificate probably comes pre-installed on Windows 8. However, I am not sure about that: it is possible that Globalsign's certificate was automatically downloaded from Windows Update instead of coming with Windows 8. Your goal is to buy a certificate whose chain of trust is rooted in a certificate that will already be a Trusted Root Certification Authority on all of your users' computers, so you don't have to rely on Windows Update.
  4. Each of the certificates you have left represents a company that could possibly sell you a good certificate. Look at their websites and compare the prices of the different certificate products they offer. Try to make sure you are buying a product that specifically says it can be used for driver signing, because some companies have more than one root certificate and it's hard to be sure which one will be in your certificate's chain of trust. If possible, try to get a sample file that was signed with the certificate they are selling you, so you can inspect its signature and make sure it meets all the signature requirements you care about. Make sure they have a decent return policy.

How to sign

This section will explain what to do after you have purchased the code signing certificate in order to actually use it. This information can mostly be found from official sources, but some of those sources (e.g. kmsigning.doc) are out of date.

First of all, you should follow the instructions from your certificate provider. These will include some sort of procedure to generate a private key and certificate and install them on your computer. After you have followed those instructions, you should open up certmgr.msc and look at your certificate to make sure everything looks good. The certificate should be in the "Personal" category, and the properties dialog for it should say "You have a private key that corresponds to this certificate."

Cross-certificate

You might need to download an appropriate cross-certificate in order to extend your chain of trust and meet all the desired signature requirements. All of the standard cross-certificates that go back to the Microsoft Code Verification Root are available for download from Microsoft. Your certificate provider might have some other useful cross-certificates available for download on their website. To use a cross-certificate, you will have to include an argument of the form /ac "path-to-your-cross-cert.ct" when you invoke signtool.

Timestamp server

Make sure to timestamp your signatures so they will continue to work after your certificate expires. Your certificate provider should provide the URL of a timestamp server in their documentation, but you can probably use the timestamp server from any provider for free. I found that the Verisign timestamp server worked fine for me even though I was not a Verisign customer. To timestamp your signature, you will have to include an argument of the form /tr http://timestampserver.com when you invoke signtool.

Signtool and inf2cat

To sign anything, you will need the Signtool.exe (Sign Tool) utility from Microsoft. To obtain signtool.exe, I installed the latest version of the Windows SDK.

To sign driver packages, you first need to use another tool called Inf2Cat (Inf2Cat.exe) to create the security catalog (CAT) file, which you can then sign with signtool. To obtain inf2cat.exe, I installed the latest version of the Windows Driver Kit. You should get the latest version to ensure that it will support the latest versions of Windows.

You can probably figure out how to use inf2cat and signtool from the documentation, but here are some examples of how to use them.

This is an example batch (.bat) script. You can simply drag an executable or MSI file onto it, and it will sign the file for you:

"C:\Program Files (x86)\Windows Kits\8.0\bin\x86\signtool" sign /v /ac "your-cross-cert.crt" /n "Your company name" /tr http://tsa.starfieldtech.com %1
pause

This is another example batch script. You can put it in the same directory as your driver package and then double-click on it to create the security catalog and sign it.

"C:\Program Files (x86)\Windows Kits\8.0\bin\x86\inf2cat" /v /driver:%~dp0 /os:XP_X86,Vista_X86,Vista_X64,7_X86,7_X64,8_X86,8_X64
"C:\Program Files (x86)\Windows Kits\8.0\bin\x86\signtool" sign /v /ac "your-cross-cert.crt" /n "Your company name" /tr http://tsa.starfieldtech.com *.cat
pause

For both of these batch files, I recommend just putting the cross-certificate in the same directory as the batch file to make the /ac parameter simpler.

Verifying

You should use the verify option of signtool.exe to check your signatures while you are still learning the process. The documentation of the options for signtool verify is pretty confusing, so I will tell you what you need to know:

  • To test a signature for the purpose of running an executable or installing a driver package, the correct option is /pa. I infer this from KMCS_Walkthrough.doc.
  • To test a signature for the purpose of loading kernel-mode code, the correct option is /kp.

Here is an example batch script that verifies the signature of a file you drop onto it, using /pa:

"C:\Program Files (x86)\Windows Kits\8.0\bin\x86\signtool" verify /v /pa %1
pause

Unfortunately, signtool verify has limited usefulness. It will make sure that your chain of trust extends back to the right place, but it will not tell you about most of the other signature requirements that I have documented above. For example, it doesn't warn you about using SHA-2. To actually be confident in your signatures, you need to properly test them, so keep reading.

Testing

It's pretty obvious that it would be ideal to test your signed drivers/executables on every different version of Windows you are targeting.

What isn't obvious is that when you are testing executables or MSI files, you should run them right after downloading them from the internet. As I explained in the Installing a driver package section, there is a bug in Windows Vista that only manifests itself if the file was downloaded from the internet, and there could easily be more bugs like that. Just throw your executables into a zip file at a secret URL and download them onto the test computer. Generally, you will know that you are testing executables correctly if Windows displays an extra warning when you try to run the executable.

You should test your downloadable file (e.g. your ZIP file or installer) by downloading it in Internet Explorer to make sure there are no problems when Internet Explorer checks your signature. In my experience, Internet Explorer checks the signatures on EXE downloads (and probably MSI too), but in future versions it might reach inside ZIP files and check the signatures on the executable files inside.

Backup

If your certificate provider is like Go Daddy, they do not actually store a backup of that private key for you. The private key only exists on your machine, and if you lose it you will have to "re-key" your certificate. Re-keying probably isn't a big deal; with Go Daddy you can just do it for free through their web interface.

However, I recommend backing up your certificate and private key. To do this, run certmgr.msc, select your certificate, and then select Action > All Tasks > Export.... This brings up the Certificate Export Wizard. Be sure to select "Yes, export the private key", select PFX format, select "Include all certificates in the certification path if possible", do NOT select "Delete the private key if the export is successful", and select "Export all extended properties". You will then be asked to choose a password and choose the name of the output file. When you are finished, you will have a password-protected Personal Information Exchange (PFX) file that contains your certificate and your private key. You can install the contents of the file on other computers simply by double-clicking on it and entering the password.

Signing myths

It's annoying when you ask for help and the good people trying to help you end up telling you things that are untrue or half-truths. Here are some of the myths I have encountered:

Myth: Kernel-mode drivers require WHQL testing

Let's say that the booby girls at GoDaddy don't give the user much confidence in the a certificate issued by them, promising that the driver always works. Drivers for the 64-bit version of Windows have to be qualified by Microsoft, not them girls. Google "whql labs certifications". Hans Passant, who has 300,000+ reputation on StackOverflow, in response to my question
A customized installation [generated by our software] does not contain certified drivers for Windows XP/2003/Vista/7. Certification must be performed by Microsoft for the new driver installation. An uncertified installation will not cause any other problems other than the warning message displayed by Windows XP/2003/Vista when installing uncertified drivers. Uncertified drivers cannot be installed in Windows 7 unless they are installed with a testing certificate or the Ignore Serial Signing option is enabled by pressing F8 on start up and selecting the corresponding option. Silicon Labs, makers of the CP2102 serial bridge, in AN220

This is a myth. Microsoft isn't trying to assert total control over what gets loaded into the kernel. They just want to make it easier to figure out what went wrong when the user experiences a blue screen of death or some other problem.

So starting with Windows Vista 64-bit, Windows requires signatures for loading kernel-mode code. Starting with Windows 8, they also require driver packages to be signed before they can be installed. The code could still contain infinite loops and viruses, but at least it can be tracked to its source when problems arise! See the signing requirements section for a complete explanation.

The Silicon Labs quote above is especially sad. If they were aware of the details I described in the Loading a kernel module section, they could have written instructions for how Silicon Labs customers can create totally unsigned driver packages matching their own CP2102-based devices and successfully running on Windows Vista/7. The official signed driver package from Silicon Labs, which provides the correct digital signature for silabser.sys, would just be a dependency of the unsigned driver packages.

Myth: DefaultInstall doesn't work with signed drivers

The INF file of a driver package must not contain an INF DefaultInstall section if the driver package is to be digitally signed. Microsoft, in the INF Default Install Section documentation

The documentation is incorrect. I have distributed signed drivers with DefaultInstall sections to our customers since November 2012 and there have been no problems. I don't see any reason why there should be a problem. While I was figuring out the signing process, I used the DefaultInstall section almost exclusively as my method for testing driver package installation.

The DefaultInstall section allows a user to install your INF file simply by right-clicking on it and selecting "Install". Really you should use DPInst or make your own installer that calls SetupCopyOEMInf, but the DefaultInstall section is easy to add and it could be useful to some customers, so you should have it.

For example, if your driver is named foo_driver.inf, you should add the following lines:

[DefaultInstall]
CopyINF=foo_driver.inf

You can even reference multiple INF files in the CopyINF directive if you want.

Myth: The INF version number indicates OS support

Create an INF file in your driver package directory and edit it for Windows Vista. Specifically, change the build date to 4/1/2006 or greater and the version to 6. For example: DriverVer=04/01/2006, 6.0.1.0 Microsoft, in kmsigning.doc

Generally, kmsigning.doc is pretty good, but that line is wrong.

I don't know if their claim about the date is correct, because I have never tried dating one of my drivers before 2006, but they are definitely wrong about the version number. I have successfully distributed drivers to thousands of customers who run Windows XP/Vista/7, and our driver versions are typically in the 1.0.0.0 to 3.0.0.0 range.

The INF DriverVer Directive is documented here on MSDN. If the DriverVer version number were important in some way, that should be documented on that page, not buried on page 11 of kmsigning.doc. In fact, the DriverVer version is optional according to that page.

I think the best practice for the version number is to start it at 1.0.0.0, and whenever you edit the file for any reason you should increment the version number and update the driver date.

Half-truth: Windows 7 doesn't support SHA-2

In some cases, you might want to sign a driver package with two different signatures. For example, suppose you want your driver to run on Windows 7 and Windows 8. Windows 8 supports signatures created with the SHA256 hashing algorithm, but Windows 7 does not. For Windows 7, you need a signature created with the SHA1 hashing algorithm. Microsoft, from Signing a Driver for Public Release on MSDN

This is a half truth. In my experience, SHA-2 signatures on driver packages (i.e. CAT files) work just fine in Windows Vista and Windows 7 for the purpose of driver package installation. However, they don't work for the purpose of loading kernel modules (SYS files) into the kernel. If you use SHA-2 to sign a driver-package that has kernel-mode code, you will get a Code 52 error when you plug in your device and actually try to use the driver. The part of Windows Vista/7 that checks to see if a file can be loaded into the kernel apparently does not recognize SHA-2 signatures. If your driver package doesn't contain any new kernel modules (e.g. you use WinUSB or usbser.sys), a SHA-2 signature will work fine. For more details about this, see the signature requirements section above.

Sometimes telling your customer a half-truth can be worse than just telling a myth. The first time I read the paragraph from the MSDN documentation quoted above, I just assumed it was totally wrong because in my experience the SHA-2 signature was working fine for my WinUSB and usbser.sys-based driver packages. There is a kernel of truth to that paragraph, but unfortunately I could not receive that truth because it was veiled in inaccuracy. Keep that in mind the next time you try to write documentation or explain something to someone: if you say something that goes against their experience of the world, they will discount what you are saying.

On the other hand, someone once told me:

Signing is perhaps the least suitable area to show off creativity and independent thinking. Just the opposite. Try to follow the instructions precisely. ... No matter what they scribble at Stack Overflow – the WDK documentations says the ultimate truth (when updated, of course). Pavel A., in response to my question on MSDN

Well, Pavel was right in this case. If I had turned off all of my creativity and independent thinking, I would have accepted that paragraph as the truth (even though it contradicts all available evidence) and it would have saved me some pain later.

I can't just turn off my brain, but I think it is important that we compromise with Pavel. We should take the documentation seriously, and when it says something that contradicts our experience, we should consider the possibility that the documentation could be correct in some other domain that we haven't tested yet. Therefore, two of the myths I listed above might actually be half-truths.

References

  1. KB2763674. Microsoft.
  2. Digital Signatures for Kernel Modules on Windows (kmsigning.doc). Microsoft. .
  3. Kernel-Mode Code Signing Walkthrough (KMCS_walkthrough.doc). Microsoft. .
  4. Windows root certificate program members. Microsoft.
  5. Cross-Certificates for Kernel Mode Code Signing. Microsoft.
  6. Signtool.exe (Sign Tool). Microsoft.
  7. Inf2Cat. Microsoft.
  8. Automatic root certificate update problems when verifying my signed INF driver package. David Grayson. .
  9. Signing Windows 8 Drivers. PiXCL Automation Technologies. 2013-03-09.
  10. Microsoft Security Advisory (2880823). Microsoft. .

Comments

I would like to hear from you! I do not host comments here, but if you have anything to say, please post it to the MSDN thread I have started. If you have and found this document to be useful, please up-vote my original post in that thread and write a reply saying thanks.

Revision History

  1. : Added a mention of KB2763674, which should make SHA-2 executables usable on Windows Vista.
  2. : Added a mentions of Microsoft Security Advisory (2880823).
  3. : Added section Use /tr to specify the timestamp server and changed all examples to use /tr.
  4. : Added new info from PiXCL about how to double-sign with SHA1 and SHA2 to the Use SHA-1 for kernel modules prior to Windows 8 section.
  5. : Initial release.