Signed Driver Walkthrough

Pete Batard edited this page Jun 19, 2015 · 15 revisions

This section documents the use of a driver signing digital credential and libwdi to create a driver installer application that allows the installation of a Windows driver without prompts. Note that from libwdi version 1.1.0 and if you are using one of the default WinUSB, libusb0 or libusbK official driver files, none of these operations are necessary, as the driver files are already digitally signed and libwdi will also self-sign driver packages on the fly to prevent additional prompts. The following is only necessary if you are planning to embed your own custom driver (a.k.a. user driver) with libwdi or with a non libwdi based application.

Outside of libwdi usage, this short guide can also be used as a tutorial for driver package signing.

Table of Contents

Prerequisites

  1. A set of driver binaries, along with a static inf file matching the devices you plan to support. These would typically be driver files which you have (re)compiled yourself as, as mentioned above, when using the official WinUSB or libusb0/K drivers in libwdi, the operations described below would either already have been performed, or be automated from the library itself.
  2. A valid driver signing certificate (see Obtaining a driver signing certificate)
  3. The latest libwdi source, and one of the supported libwdi development environments. Since you will also need tools from the WDK for the signing process, it might be a good idea to use it as your development environment
  4. The latest WDK
For the purpose of this exercise, we will be using the libusb-win32_ft2232_driver files from OpenOCD-dev 0.5.0, as well as driver signing credentials obtained from GlobalSign. The credentials typically take the form of a .pfx file, along with its private key password. The development toolchain will be WDK 7.0.0.

Obtaining a driver signing certificate

To be able to digitally sign a Windows driver, you must have a Microsoft-authorized Authenticode code signing credential. A credential consists of a public key, embedded in a public certificate, that contains your trusted third party verified information, as well as the matching private key used to encrypt elements such as binary hashes, that are then only decipherable using the public key.

Most of the Authenticode driver signing credentials seem to originate either from VeriSign or GlobalSign. Other Certification Authorities providing these services also exist, but Verisign and GlobalSign appear to be the two most commonly used.

Verisign

  • 1st year driver signing credentials could be obtained for $99 (dead link)
  • Usually a lot more expensive than GlobalSign in the long run: $499/year
  • Might not provide credentials for non registered companies

Globalsign

  • Usually more competitive on pricing (long term) compared to Verisign
  • Even if non affiliated to a registered company, individual developers can obtain certificates for $129 (US & Canada) / €99 (EU) (promotional offer)
  • More friendly to non-US based customers (Belgium-based)
From the date of registration, delivery of your certificate can take a few days to a few weeks.

Signing the driver binaries

After downloading either one of the openocd[-x64]-0.5.0-dev windows binaries, we find the libusb-win32_ft2232_driver-101028.zip archive in the drivers/ directory. Further extracting reveals that it contains a .inf file, along with the amd64/ and x86/ directories where the actual driver binaries reside. If you look at the binaries' properties, you will see that they have already been signed by the libusb-win32 developers. However, since we are using these files for illustration purpose, we will assume that they are unsigned (our new signature will overwrite the existing one), as they would be if we were to modify and recompile the libusb-win32 drivers from source.
Also, more relevant to a real life scenario, since there is no signed .cat, Windows will not trust the .inf during installation and issue a security prompt, which is another item we will be addressing in this guide.

Timestamping

While using a trusted time source for signature creation or validation is always good practice, the reason you really want to use timestamping from a trusted authority when signing your drivers is that it will ensure they can still be used in Windows, even after the code signing certificate expires. If you don't timestamp, then you will have to provide your users with updated drivers files, and ask them to upgrade drivers every time your original certificate expires. In other words, if you use a certificate with an expiration date of one year and you don't use timestamping during signing, your drivers will cease functioning after a year.

Windows Authenticode Certifications Authorities usually provide a Windows timestamping service (usually a dll) that you can reference when signing. Some of these URLs are:

  • http://timestamp.verisign.com/scripts/timstamp.dll (Verisign)
  • http://timestamp.globalsign.com/scripts/timstamp.dll (GlobalSign)

Additional Certificate

While the Windows certificate store has both the GlobalSign and Verisign root CA certificates as Trusted Root Certification Authorities, these CA certificates are not the ones used for driver code signing. Instead Microsoft use their own "Microsoft Code Verification Root" certificate, which curiously doesn't appear at the top of the certification chain for signed code certificates, and which they used to sign the root certificate that Certification Authorities use to provide Authenticode credentials to customers. Microsoft calls that a cross certificate.

By default however, Windows platforms only have the Microsoft Code Verification Root certificate installed and not the the ones from subsidiaries, therefore, to be able to validate the trust chain, the CA's Authenticode root certificate must also be provided.

In short, this means that you have to download the root Authenticode certificate from GlobalSign, Verisign or your other third party authority, so that it is embedded in the signature. These certificates can be obtained from:

For the record, these certificates are currently set to expire in 2021, which is hopefully long enough.

Signing process

With your driver signing .pfx or .p12 file, the corresponding private key password, the .crt certificate above and the timestamp URL, you are now good to sign your driver files. For this exercise, I will place the CA certificate (GlobalSign Root CA.crt) along with the pfx (akeo.pfx) in an easily accessible local directory. The timestamping authority will also be the one provided by GlobalSign, and you need to be connected to the internet for time stamping to work. Be mindful that, even when protected by a strong password, leaving .pfx files lying around is not a good idea, so don't forget to secure your credentials when you're done.
Preferably you want to sign all the driver files, including the DLLs, but technically, only the .sys should be required.

To sign the file, open one of the WDK command prompts (eg. Windows XP x86 Free Build Environment), then navigate to the directory containing the driver files you want to sign (eg libusb-win32_ft2232_driver-101028\amd64\libusb0.sys and issue the following:

signtool sign /v /ac "D:\codesign\GlobalSign Root CA.crt" /f D:\codesign\akeo.pfx /p "<YOUR_PASSWORD>" /t http://timestamp.globalsign.com/scripts/timstamp.dll libusb0.sys

Example:

D:\libusb-win32_ft2232_driver-101028>signtool sign /v /ac "D:\codesign\GlobalSign Root CA.crt" /f D:\codesign\akeo.pfx
  /p "**********" /t http://timestamp.globalsign.com/scripts/timstamp.dll x86\libusb0.sys
The following certificate was selected:
    Issued to: Akeo Consulting
    Issued by: GlobalSign ObjectSign CA
    Expires:   Sun Jun 26 11:05:35 2011
    SHA1 hash: 0BF5319EE093F9234D8504527D63CFAFEADEECF8

Cross certificate chain (using machine store):
    Issued to: Microsoft Code Verification Root
    Issued by: Microsoft Code Verification Root
    Expires:   Sat Nov 01 14:54:03 2025
    SHA1 hash: 8FBE4D070EF8AB1BCCAF2A9D5CCAE7282A2C66B3

        Issued to: GlobalSign Root CA
        Issued by: Microsoft Code Verification Root
        Expires:   Thu Apr 15 21:05:08 2021
        SHA1 hash: CC1DEEBF6D55C2C9061BA16F10A0BFA6979A4A32

            Issued to: GlobalSign Primary Object Publishing CA
            Issued by: GlobalSign Root CA
            Expires:   Fri Jan 27 13:00:00 2017
            SHA1 hash: 1AAF4DF10D36215E09E4EEFD70E340C2E4DECF38

                Issued to: GlobalSign ObjectSign CA
                Issued by: GlobalSign Primary Object Publishing CA
                Expires:   Fri Jan 27 12:00:00 2017
                SHA1 hash: B859853EF366AC9335763C340A87BD208113055F

                    Issued to: Akeo Consulting
                    Issued by: GlobalSign ObjectSign CA
                    Expires:   Sun Jun 26 11:05:35 2011
                    SHA1 hash: 0BF5319EE093F9234D8504527D63CFAFEADEECF8

Done Adding Additional Store
Successfully signed and timestamped: libusb0.sys

Number of files successfully Signed: 1
Number of warnings: 0
Number of errors: 0

For details on what each of the option above does, though they should be fairly explicit, you can issue: signtool sign /?.

Checking the signed driver files

Optional, but probably a good idea, as you will get the timestamping info as well:

signtool verify /kp /v libusb0.sys

Example:

D:\libusb-win32_ft2232_driver-101028\amd64>signtool verify /kp /v libusb0.sys

Verifying: libusb0.sys
Hash of file (sha1): B4C09901487067EB10454F6CFFCFA3C64988EE86

Signing Certificate Chain:
    Issued to: GlobalSign Root CA
    Issued by: GlobalSign Root CA
    Expires:   Fri Jan 28 12:00:00 2028
    SHA1 hash: B1BC968BD4F49D622AA89A81F2150152A41D829C

        Issued to: GlobalSign Primary Object Publishing CA
        Issued by: GlobalSign Root CA
        Expires:   Fri Jan 27 12:00:00 2017
        SHA1 hash: 1AAF4DF10D36215E09E4EEFD70E340C2E4DECF38

            Issued to: GlobalSign ObjectSign CA
            Issued by: GlobalSign Primary Object Publishing CA
            Expires:   Fri Jan 27 11:00:00 2017
            SHA1 hash: B859853EF366AC9335763C340A87BD208113055F

                Issued to: Akeo Consulting
                Issued by: GlobalSign ObjectSign CA
                Expires:   Sun Jun 26 10:05:35 2011
                SHA1 hash: 0BF5319EE093F9234D8504527D63CFAFEADEECF8

The signature is timestamped: Tue Feb 08 13:54:32 2011
Timestamp Verified by:
    Issued to: GlobalSign Root CA
    Issued by: GlobalSign Root CA
    Expires:   Fri Jan 28 12:00:00 2028
    SHA1 hash: B1BC968BD4F49D622AA89A81F2150152A41D829C

        Issued to: GlobalSign Timestamping CA
        Issued by: GlobalSign Root CA
        Expires:   Fri Jan 28 12:00:00 2028
        SHA1 hash: 958D23902D5448314F2F811034356A58255CDC9B

            Issued to: GlobalSign Time Stamping Authority
            Issued by: GlobalSign Timestamping CA
            Expires:   Tue Dec 22 09:32:56 2020
            SHA1 hash: AEDF7DF76BBA2410D67DBAF18F5BA15B417E496C

Cross Certificate Chain:
    Issued to: Microsoft Code Verification Root
    Issued by: Microsoft Code Verification Root
    Expires:   Sat Nov 01 13:54:03 2025
    SHA1 hash: 8FBE4D070EF8AB1BCCAF2A9D5CCAE7282A2C66B3

        Issued to: GlobalSign Root CA
        Issued by: Microsoft Code Verification Root
        Expires:   Thu Apr 15 21:05:08 2021
        SHA1 hash: CC1DEEBF6D55C2C9061BA16F10A0BFA6979A4A32

            Issued to: GlobalSign Primary Object Publishing CA
            Issued by: GlobalSign Root CA
            Expires:   Fri Jan 27 12:00:00 2017
            SHA1 hash: 1AAF4DF10D36215E09E4EEFD70E340C2E4DECF38

                Issued to: GlobalSign ObjectSign CA
                Issued by: GlobalSign Primary Object Publishing CA
                Expires:   Fri Jan 27 11:00:00 2017
                SHA1 hash: B859853EF366AC9335763C340A87BD208113055F

                    Issued to: Akeo Consulting
                    Issued by: GlobalSign ObjectSign CA
                    Expires:   Sun Jun 26 10:05:35 2011
                    SHA1 hash: 0BF5319EE093F9234D8504527D63CFAFEADEECF8

Successfully verified: libusb0.sys

Number of files successfully Verified: 1
Number of warnings: 0
Number of errors: 0

Creating a signed cat file

Why would you want a signed catalog file?

What we did above allows the installation of drivers in a Windows environment outside of test mode, which is fine and all, but unless you sign the inf as well, you will get the following prompt on Vista and later versions of Windows during driver installation:

Even worse, unlike Windows 7 and earlier, Windows 8 does not even prompt you with a warning, but flat out refuses to install any unsigned driver packages.

One way to avoid these issues then is to go through WHQL. However, this is an expensive and time-consuming process, and Microsoft are currently abusing their position to prevent GPL licensed drivers (both GPLv2 and GPLv3) from being accepted through the WHQL process.

The other solution then is to use your driver signing credentials to sign the .inf, and create a catalog (.cat) file.

At this stage, it may be important to stress out that the trust validation for signed driver binaries (.sys, .dll) and the trust validation for signed driver packages (.cat) are a completely separate process, in that you don't have to use a kernel mode Authenticode certificate to sign driver packages. A self-signed or any kind of commercial certificate will work equally well, as long as it is installed in the relevant certificate stores.

Thus, you don't really have to re-use your driver signing certificate (as a matter of fact libwdi uses the self-signed certificate approach to avoid prompts when it generates the WinUSB, libusb0 or libusbK driver packages). It may however a good idea to vouch for your driver by reusing the same certificate, as it is provides your user with the confidence that the driver maker and the driver packager are the same entity.

Now, the one limitation of reusing the Authenticode certificate to sign the catalog, is that, as we already mentioned, Authenticode provider authorities are not trusted by default as software publishers. What this means is that, even if you use your Authenticode certificate from Verisign or GlobalSign on a vanilla Windows platform, it will not be trusted by default for cat signing, regardless of the existing Verisign or GlobalSign root CA certificates existing in the Windows certificate store. This is because different root CAs are used to validate different chains of trust, and none of the default root CAs will match the root CA of an authenticode certificate, even if both were issued by the same company (As a matter of fact, the root CA for Authenticode is neither GlobalSign or Verisign, but actually Microsoft, and only appears otherwise because of cross signing).

Therefore users may get a prompt such as the one below, about trusting the company identified by the certificate

This security alert will be displayed to users unless:

  1. they previously checked "Always trust software from company X" when previously prompted with a similar query
  2. you already installed your certificate in the Trusted Publisher system store
In essence, the end result from either using a self signed certificate or an Authenticode certificate is that your certificate must end up as a Trusted Publisher on the user system to avoid yet another security prompt.
As we will see further down, libwdi actually provides an API to take care of option 2.

Signing a catalog file

Since we're using the WDK, we have access to the inf2cat tool, which is the most convenient way to create a .cat out of a .inf file. Before you can use inf2cat, you must ensure that your .inf contains a CatalogFile entry in the [Version] section, that provides the name of the .cat to be created. The libusb-win32_ft2232_driver.inf file we use is missing it, so we add it:

CatalogFile = "libusb-win32_ft2232_driver.cat"
Then, you can simply run:
inf2cat /v /driver:. /os:XP_X86,XP_X64,Vista_X86,Vista_X64,7_X86,7_X64

Example:

D:\libusb-win32_ft2232_driver-101028>inf2cat /v /driver:. /os:XP_X86,XP_X64,Vista_X86,Vista_X64,7_X86,7_X64
Processing directory (D:\libusb-win32_ft2232_driver-101028\) file (info.txt)
Processing directory (D:\libusb-win32_ft2232_driver-101028\) file (libusb-win32_ft2232_driver.inf)
Processing directory (D:\libusb-win32_ft2232_driver-101028\amd64) file (libusb0.dll)
Processing directory (D:\libusb-win32_ft2232_driver-101028\amd64) file (libusb0.sys)
Processing directory (D:\libusb-win32_ft2232_driver-101028\x86) file (libusb0.sys)
Processing directory (D:\libusb-win32_ft2232_driver-101028\x86) file (libusb0_x86.dll)
Parsing INF: D:\libusb-win32_ft2232_driver-101028\libusb-win32_ft2232_driver.inf
Finished parsing INFs
Processing INF: D:\libusb-win32_ft2232_driver-101028\libusb-win32_ft2232_driver.inf
Finished processing INFs
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...
Testing driver package...

Signability test complete.

Errors:
None

Warnings:
None

Catalog generation complete.

After that, you would sign the generated .cat as follows:

signtool sign /v /ac D:\codesign\MSCV-GlobalSign.cer /f D:\codesign\akeo.pfx /p "<YOUR_PASSWORD>" /t http://timestamp.globalsign.com/scripts

Example:

D:\libusb-win32_ft2232_driver-101028>signtool sign /v /ac D:\codesign\MSCV-GlobalSign.cer /f D:\codesign\akeo.pfx
  /p "**********" /t http://timestamp.globalsign.com/scripts/timestamp.dll libusb-win32_ft2232_driver.cat
The following certificate was selected:
    Issued to: Akeo Consulting
    Issued by: GlobalSign ObjectSign CA
    Expires:   Sun Jun 26 10:05:35 2011
    SHA1 hash: 0BF5319EE093F9234D8504527D63CFAFEADEECF8

Cross certificate chain (using machine store):
    Issued to: Microsoft Code Verification Root
    Issued by: Microsoft Code Verification Root
    Expires:   Sat Nov 01 13:54:03 2025
    SHA1 hash: 8FBE4D070EF8AB1BCCAF2A9D5CCAE7282A2C66B3

        Issued to: GlobalSign Root CA
        Issued by: Microsoft Code Verification Root
        Expires:   Mon May 23 17:10:51 2016
        SHA1 hash: 3EEB2750A199F5E7B6A8952430BE5062FE04E9E5

            Issued to: GlobalSign Primary Object Publishing CA
            Issued by: GlobalSign Root CA
            Expires:   Fri Jan 27 12:00:00 2017
            SHA1 hash: 1AAF4DF10D36215E09E4EEFD70E340C2E4DECF38

                Issued to: GlobalSign ObjectSign CA
                Issued by: GlobalSign Primary Object Publishing CA
                Expires:   Fri Jan 27 11:00:00 2017
                SHA1 hash: B859853EF366AC9335763C340A87BD208113055F

                    Issued to: Akeo Consulting
                    Issued by: GlobalSign ObjectSign CA
                    Expires:   Sun Jun 26 10:05:35 2011
                    SHA1 hash: 0BF5319EE093F9234D8504527D63CFAFEADEECF8

Done Adding Additional Store
Successfully signed and timestamped: libusb-win32_ft2232_driver.cat

Number of files successfully Signed: 1
Number of warnings: 0
Number of errors: 0

Embedding the signed files in libwdi

Since we went the trouble of creating a fully signed driver package, that can install without prompts, we might as well deliver the whole thing in an installer that is attractive for end users. For that, we will of course use the custom driver facility of libwdi along with Inno Setup or NSIS. It may be possible to use other driver installation tools, such as Microsoft's own dpinst.exe but one of the many advantages of using libwdi is that it also includes provisions to add your certificate as a Trusted Publisher.

As for the the development environment, we'll keep using the WDK. In the following guide, I'll assume that you also have the latest version of the libwdi sources available in D:\libwdi).

Our first point of order then is to configure libwdi to only include the signed driver package. If you were using MinGW or cross-compilation, you would simply pass the --with-userdir option when calling configure but for MS environments, such as WDK or Visual Studio, we edit the msvc/config.h file from libwdi manually. If not done already, you will need to comment the DDK_DIR, LIBUSB0_DIR and LIBUSBK_DIR defines, and then uncomment and set USER_DIR to the path where your custom driver files reside, with something like:

#define USER_DIR "D:/libusb-win32_ft2232_driver-101028"

The second item we need to sort out is the inclusion of the public certificate in libwdi along with the signed driver files. When a user directory is specified, libwdi will recursively include all of its file into the library. One element that we must not forget to add to the disk directory then is the public part of the Authenticode signing credential (.cer/.crt), so that it can be read and installed from the library at runtime. Microsoft does not seem to provide native tools to extract the public certificate part from a pfx, so the usual way to do it is through import/export with the cert manager. If you have openssl installed, you can use the pkcs12 and x509 option to extract the .cer from the command line (see Appendix A). To be safe, you probably want the .cer you use must to include ALL the certificates required to validate the certification chain, not just the root CA one.
You should also be mindful that if you are using Verisign authenticode certificates, the certification chain may break on Windows XP machines unless the .pfx has been exported in a specific manner (see Appendix B). In this example, we will copy the .cer part, which we extracted as Akeo.cer to the base of our USER_DIR directory.

As to the driver installer executable that, can be referenced from the Inno Setup/NSIS installer, we will make use of wdi-simple from the libwdi samples since it contains everything we require and can handle automated certificate installation. When running the libwdi WDK compilation script, the sample will be built automatically so there is not additional configuration required here. If required, you can of course customize the wdi-simple.c source or create your own libwdi based driver installer executable.

For the build process itself, in order to build libwdi along with a wdi-simple sample that is compatible with all versions of Windows from XP, including 64 bit versions, we recommend that you use the Windows XP x86 command prompt (yes, it will build x64 binaries as well as x86 ones). From there, after having navigated to the libwdi directory, you can simply invoke:

ddk_build.cmd

If compilation was successful, you should now have the various sample executables in the examples\ directory. The next step then is to reference that executable from your application intaller.

Installing the certificate as a Trusted Publisher with Inno Setup or NSIS

If you look at the wdi-simple source, you'll see that it's just a commandline application that controls the driver installation process through various options. Before setting everything up in Inno Setup or NSIS (Nullsoft Scriptable Install System) it may be a good idea to test the various parameters you want to feed to wdi-simple. To see the various options at your disposal, you can run:

-n, --name <name>          set the device name
-f, --inf <name>           set the inf name
-m, --manufacturer <name>  set the manufacturer name
-v, --vid <id>             set the vendor ID (VID, use 0x prefix for hex)
-p, --pid <id>             set the product ID (PID, use 0x prefix for hex)
-i, --iid <id>             set the interface ID (MI)
-t, --type <driver_type>   set the driver to install
                           (0=WinUSB, 1=libusb0, 2=libusbK, 3=custom)
-d, --dest <dir>           set the extraction directory
-x, --extract              extract files only (don't install)
-c, --cert <certname>      install certificate <certname> from the
                           embedded user files as a trusted publisher
    --stealth-cert         installs certificate above without prompting
-s, --silent               silent mode
-b, --progressbar=[HWND]   display a progress bar during install
                           an optional HWND can be specified
-l, --log                  set log level (0 = debug, 4 = none)
-h, --help                 display usage

The options are fairly explicit. The one we are interested in here, that deals with the installation of a custom certificate in Trusted Publishers, is the --cert option. --cert is basically just a wrapper for the wdi_install_trusted_certificate() API call, which takes as the name of an embedded certificate as parameter. In our case, since we have the Akeo.cer embedded in our files, this is the certificate we will reference.

Let's say our application is targeted at one of the Olimex USB-TINY JTAG adapters referenced in the inf file we got from OpenOCD. To install the driver using wdi-simple, then one could issue the following from an elevated prompt:

wdi-simple --type 3 --name "Olimex ARM-USB-TINY (Channel A)" --vid 0x15BA --pid 0x0004 --iid 0 --inf "libusb-win32_ft2232_driver.inf" --cert "Akeo.cer" --progressbar

Note: If you try the following from a non-elevated prompt, you will see the following security notice:

You may wonder why this still occurs, despite the fact that the --cert option was specified with the name of the embedded cert, but the error report indicates that indeed the wdi_install_trusted_certificate() API call requires elevated privileges to run. This won't be a problem from Inno Setup/NSIS, as we can ensure the application runs elevated there.

Now, even from an elevated prompt, if you use only the options above, you will still get the following security warning:

This security notice is actually issued by libwdi, and not by the OS. This is because libwdi considers that it is bad practice to install a certificate as a Trusted Publisher without first notifying the user. It can however be disabled if you use the --stealth-cert option as follows:

wdi-simple --type 3 --name "Olimex ARM-USB-TINY (Channel A)" --vid 0x15BA --pid 0x0004 --iid 0 --inf "libusb-win32_ft2232_driver.inf" --cert "Akeo.cer" --stealth-cert --progressbar

If you do that, you should now see the driver installation succeed, without any prompts:

D:\libwdi\examples>wdi-simple --type 3 --name "Olimex ARM-USB-TINY (Channel A)" --vid 0x15BA --pid 0x0004 --iid 0 
 --inf "libusb-win32_ft2232_driver.inf" --cert "Akeo.cer" --stealth-cert --progressbar
Extracting driver files...
  Success
Installing certificate 'Akeo.cer' as a Trusted Publisher...
  Success
Installing driver(s)...
  Success

Armed with our choice of options, we can now create a .iss Inno Setup or .nis NSIS script. The only addition we make to the parameters above is the addon of the installer HWND handle ({wizardhwnd} for Inno Setup, $HWNDPARENT for NSIS) to the --progressbar option, as it allows libwdi to center the progressbar dialog onto the installer Window. The scripts below are based on the wdi-simple.iss/.nis scripts that are also provided in the examples directory.

Inno Setup Script

[Setup]
AppName = MyApp
AppVerName = MyApp 1.0.0.1
AppPublisher = Akeo Consulting
AppPublisherURL = http://akeo.ie
AppVersion = 1.0.0.1
DefaultDirName = {pf}\MyApp
DefaultGroupName = MyApp
Compression = lzma
SolidCompression = yes
MinVersion = 5,5
PrivilegesRequired = admin

[Files]
Source: "wdi-simple.exe"; DestDir: "{app}"; Flags: replacesameversion promptifolder;

[Icons]
Name: "{group}\Uninstall MyApp"; Filename: "{uninstallexe}"

[Run]
Filename: "{app}\wdi-simple.exe"; Flags: "runhidden"; Parameters: " --type 3 --name ""Olimex ARM-USB-TINY (Channel A)"" --vid 0x15BA --pid 0x0004 --iid 0 --inf ""libusb-win32_ft2232_driver.inf"" --cert ""Akeo.cer"" --stealth-cert --progressbar={wizardhwnd}"; StatusMsg: "Installing the Olimex ARM-USB-TINY driver (this may take a few seconds) ...";

Nullsoft Scriptable Install System Script

; Use modern interface
  !include MUI2.nsh
  !define MUI_FINISHPAGE_NOAUTOCLOSE

; General
  Name                  "libwdi-example"
  OutFile               "libwdi-example-setup.exe"
  InstallDir            $PROGRAMFILES\libwdi-example
  InstallDirRegKey      HKLM "Software\libwdi-example" "Install_Dir"
  ShowInstDetails       show
  RequestExecutionLevel admin

; Pages
  !insertmacro MUI_PAGE_DIRECTORY
  !insertmacro MUI_PAGE_INSTFILES
  !insertmacro MUI_UNPAGE_INSTFILES 
  !insertmacro MUI_UNPAGE_FINISH

; Installer
Section "libwdi-example" SecDummy
  SetOutPath $INSTDIR
  File "wdi-simple.exe"
  WriteRegStr HKLM SOFTWARE\libwdi-example "Install_Dir" "$INSTDIR"
  ; Write the uninstall keys for Windows
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwdi-example" "DisplayName" "libwdi example"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwdi-example" "UninstallString" '"$INSTDIR\uninstall.exe"'
  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwdi-example" "NoModify" 1
  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwdi-example" "NoRepair" 1
  WriteUninstaller "uninstall.exe"
SectionEnd

; Call wdi-simple
Section "wdi-simple"
  DetailPrint "Running $INSTDIR\wdi-simple.exe"
  nsExec::ExecToLog '"$INSTDIR\wdi-simple.exe" --type 3 --name "Olimex ARM-USB-TINY (Channel A)" --vid 0x15BA --pid 0x0004 --iid 0 --inf "libusb-win32_ft2232_driver.inf" --cert "Akeo.cer" --progressbar=$HWNDPARENT'
SectionEnd

; Uninstaller
Section "Uninstall"
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwdi-example"
  DeleteRegKey HKLM SOFTWARE\libwdi-example
  Delete $INSTDIR\wdi-simple.exe
  Delete $INSTDIR\uninstall.exe
  RMDir "$SMPROGRAMS\libwdi-example"
  RMDir "$INSTDIR"
SectionEnd

If you compile the scripts above with Inno/NSIS you should find that everything works and that you can now easily create installers that can install your own signed driver packages.

Links

Appendix A: Creating a Windows .cer from a .pfx, using openssl

This section details how to create a Windows compatible .cer certificate (X509 DER) from an authenticode .pfx (PKCS#12), which can then be used for .cat signing. Once again, we're going to use the Akeo.pfx authenticode certificate which we got from GlobalSign. Much of this content, as well as clarification with regards to using authenticode certificates for .cat signing, is courtesy of Bob Paddock.

# openssl pkcs12 -in akeo.pfx -out globalsign_cacerts.pem -cacerts -nokeys 
Enter Import Password: *******************
MAC verified OK

The -cacerts indicates that we only want the certificates from the Certification Authority trust chain, and exclude our end user part (the Akeo certificate itself) -nokeys option ensures that a private key will not be contained in the generated .pem file. The option is likely redundant, as we don't export anything from the end user part, but you obvioulsy don't want to run the risk of your private key being revealed.

If you look at the content of the generated file, you should see something like this:

# vi globalsign_cacerts.pem
Bag Attributes
    friendlyName: GlobalSign ObjectSign CA - GlobalSign nv-sa
subject=/C=BE/O=GlobalSign nv-sa/OU=ObjectSign CA/CN=GlobalSign ObjectSign CA
issuer=/C=BE/O=GlobalSign nv-sa/OU=Primary Object Publishing CA/CN=GlobalSign Primary Object Publishing CA
-----BEGIN CERTIFICATE-----
MIIEvjCCA6agAwIBAgILBAAAAAABHkSl7L4wDQYJKoZIhvcNAQEFBQAwgYExCzAJ
................................................................
Bf9+9VGAoBY42LaAoFFPrPaY
-----END CERTIFICATE-----
Bag Attributes
    friendlyName: GlobalSign Primary Object Publishing CA - GlobalSign nv-sa
subject=/C=BE/O=GlobalSign nv-sa/OU=Primary Object Publishing CA/CN=GlobalSign Primary Object Publishing CA
issuer=/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
-----BEGIN CERTIFICATE-----
MIID+DCCAuCgAwIBAgILBAAAAAABHkSl4k4wDQYJKoZIhvcNAQEFBQAwVzELMAkG
................................................................
QGYX32KNKzSnQmcl
-----END CERTIFICATE-----
Bag Attributes
    friendlyName: GlobalSign Root CA
subject=/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
issuer=/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
................................................................
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----
This tells us that our trust chain has 3 certificates above our Akeo credentials. From highest (root CA) to lowest:
GlobalSign Root CA → GlobalSign Primary Object Publishing CA → GlobalSign ObjectSign CA

Now, we only have the certificate chain as a .pem file, which we cannot use with Windows and libwdi. We need an X509 DER file. However, the default from openssl is to only process the first relevant section from a PEM file (Bag Attributes), and if we use the .pem as is, the .cer generated will only contain the "GlobalSign ObjectSign CA" certificate, and none of the others. Since we want to have the full certification chain in our .cer, with all the intermediate certificates, we must edit the .pem to remove all the Bag Attributes sections, except the first one:

# vi globalsign_cacerts.pem
Bag Attributes
    friendlyName: GlobalSign ObjectSign CA - GlobalSign nv-sa
subject=/C=BE/O=GlobalSign nv-sa/OU=ObjectSign CA/CN=GlobalSign ObjectSign CA
issuer=/C=BE/O=GlobalSign nv-sa/OU=Primary Object Publishing CA/CN=GlobalSign Primary Object Publishing CA
-----BEGIN CERTIFICATE-----
MIIEvjCCA6agAwIBAgILBAAAAAABHkSl7L4wDQYJKoZIhvcNAQEFBQAwgYExCzAJ
................................................................
Bf9+9VGAoBY42LaAoFFPrPaY
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID+DCCAuCgAwIBAgILBAAAAAABHkSl4k4wDQYJKoZIhvcNAQEFBQAwVzELMAkG
................................................................
QGYX32KNKzSnQmcl
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
................................................................
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----
Then we can issue:
# openssl x509 -in globalsign_cacerts.pem -inform PEM -out globalsign.cer -outform DER
If you open the certificate in Windows, you should then be able to see the certificate with the full chain, and you will also be able to use this certificate with libwdi.

Appendix B: Using a Verisign authenticode certificate to sign a .cat on XP

Once again courtesy of Bob Paddock:

In CertMgr if the Certification Path for your certificate only shows three items in the tree, it means there will be problems using the certificate on XP machines that are not up to date. The problem stems back from the original private key that was exported out of Windows to make the .pfx file. If the file is around 6.6K it indicates that part of the chain is missing, that it seems outdated XP machines need, and nothing else does. The .pfx file needs to be around the size of 7.4K if it has the correct chain of certificates to make XP happy. The exact file size will vary somewhat due to variations of length in the certificate name (has nothing to do with the name of the file), and the particular CA that was used. When you export the Private Key make sure 'Include all certificates in the certificate path if possible' and 'Export all extended properties' are checked, to make old XP machines happy.