Welcome

OpenAP

802.11

mpeg4

XML

ASPI

Linux Toshiba Satellite A15-S1292

Authenticode

About

Zillabit Projects

Geeky Tidbits

I have an automated build system that produces Windows software on Linux (Debian Sarge) using wonderful tools like MinGW and NSIS. I also need to do authenticode signing of my EXE and my installer on this build system. This page describes how I got it working with the signcode tool in the Mono project. Many thanks to Sebastien Pouliot for creating this tool and sparing me from having to include Windows in my build systems.

Also for completeness are notes on failed attempts to run official Microsoft tools under Wine, in case this information is helpful to anyone.

Update: I now use osslsigncode instead. I recommend that as the best alternative for most people. (You will need to read the README file to get started.) But if you are already using the Mono project it might be easiest to use the Mono signcode tool, in which case these notes might be helpful to you.

Installation of Mono stuff on Debian Sarge

  1. Edit /etc/apt/sources.list to include backports.org source as explained here.
  2. apt-get install mono-mcs

Recently fixed bugs in the Mono Authenticode implementation

In late 2006 i found that the Mono Authenticode implementation created bad signatures for two cases of interest. I filed bug 79741 and Sebastian fixed these problems quickly, in revision 69502 in December 2006. In case you don't have a version of this code recent enough to contain the fixes, you might want to know about workarounds.

EXE files with debugging information after the end of the PE section will trigger this bug. For example, executables built with MinGW will contain such debugging info by default. The easy workaround is to use the strip command to remove that info before signing, for example

i586-mingw32msvc-strip helloworld.exe

Installer files created with NSIS place the bulk of the installer data after the last PE section. Using the strip command will discard the installer data and leave a tiny useless file, so that is not a viable workaround for this case. You'll want the fixed code for this case.

Working around problems with PVK password

Attempting to run
signcode -v mykey.pvk -spc mycert.spc -t http://timestamp.verisign.com/scripts/timstamp.dll helloworld.exe
fails with this output:
Unhandled Exception: System.Security.Cryptography.CryptographicException: Invalid data and/or password
in <0x000a0> Mono.Security.Authenticode.PrivateKey:.ctor (System.Byte[] data, System.String password)
in <0x000c4> Mono.Security.Authenticode.PrivateKey:CreateFromFile (System.String filename, System.String password)
in <0x0000c> Mono.Security.Authenticode.PrivateKey:CreateFromFile (System.String filename)
in <0x0005d> Mono.Tools.SignCode:GetPrivateKey (System.String keyfile, System.Security.Cryptography.CspParameters csp)
in <0x006ac> Mono.Tools.SignCode:Main (System.String[] args)
Examining a recent version of the signcode.cs source code it seems that first it attempts to open the PVK file without a password, then if that fails it is supposed to prompt for a password and try opening the PVK again with that password. However the initial failure throws an unhandled exception before going any farther. Because I do not know if there is any way to prevent the program from terminating on this unhandled exception, I downloaded the source file and edited it so that it would skip the attempt to open the PVK without a password:
--- signcode.cs-orig    2006-10-24 11:58:40.081094750 -0700
+++ signcode.cs 2006-10-24 01:32:10.306788250 -0700
@@ -62,14 +62,14 @@
                                        return null;
                                }

-                               PrivateKey pvk = PrivateKey.CreateFromFile (keyfile);
-                               if (pvk.Encrypted) {
+                               //PrivateKey pvk = PrivateKey.CreateFromFile (keyfile);
+                               //if (pvk.Encrypted) {
                                        Console.WriteLine ("Enter password for {0}: ", keyfile);
                                        string password = Console.ReadLine ();
-                                       pvk = PrivateKey.CreateFromFile (keyfile, password);
+                                       PrivateKey pvk = PrivateKey.CreateFromFile (keyfile, password);
                                        if (pvk.RSA == null)
                                                Console.WriteLine ("Invalid password!");
-                               }
+                               //}
                                rsa = pvk.RSA;
                        }
                        else {
After compiling this with
mcs -r:Mono.Security.dll signcode.cs
it is possible to run the local signcode tool and it will prompt for the password then successfully write out the signed EXE.
./signcode.exe -v mykey.pvk -spc mycert.spc -t http://timestamp.verisign.com/scripts/timstamp.dll helloworld.exe
An alternative workaround is to use the signcode tool as-is but to create a version of the PVK file without a password with a program like this:
using System;
using System.IO;
using System.Text;
using Mono.Security.Authenticode;
public class PrivateKeyTest {
        private const string testfilein = "testin.pvk";
        private const string testfileout = "testout.pvk";
        public static void Main ()
        {
                PrivateKey pvk = PrivateKey.CreateFromFile (testfilein, "my_actual_password");
                pvk.Save (testfileout);
        }
}
where you replace "my_actual_password" with the real password. Of course the usual security caveats apply if you're going to leave a password-less key sitting around.

Working around problems with Thawte certificate in Mono

Initially Windows told me that my signature was not OK. I found this thread on the Mono list which sounded exactly like my problem because my certificate was issued by Thawte. Apparently I was running into bug 68903. Sebastian explains a workaround in the bug description, but unfortunately I did not fully understand the explanation and was unable to make it work at first. Specifically, in my first attempt I failed to understand that all the certificates in the original SPC file need to be exported individually as DER files, then all glued back together into a single SPC file with the cert2spc tool. Here is a more detailed explanation that would have helped me get it right the first time.
  1. On Windows (I used Windows XP), double-click your mycert.spc file to open it in the Certificates program.
  2. In the left pane, expand the folder icon and select the "Certificates" folder icon inside it. The right pane should now show the list of certificates in the file. In my case I see 3:
    • My Company, Inc.
    • Thawte Code Signing CA
    • Thawte Premium Server CA
  3. Select the top one of the 3, and select "Action / All Tasks / Export..." from the menu. This starts the Certificate Export Wizard.
  4. The first screen is informational only, so click Next
  5. On the next screen, "Exported File Format", select "DER Encoded binary X.509 (.CER)" which might be the default
  6. On the next screen, "File to Export", select a location and name for your exported file, which will be given a ".cer" extention.
  7. On the next screen click "Finish" to complete the export
  8. Repeat the previous 5 steps to export each of the other certificates as ".cer" files.
At this point, if you named the .cer files like I did, you'll have:
  1. my-company.cer
  2. thawte-code-signing.cer
  3. thawte-premium-server.cer
Copy these .cer files over to the system running mono and use the cert2spc command like this:
cert2spc my-company.cer thawte-code-signing.cer thawte-premium-server.cer myfixedcert.spc
which creates the new file myfixedcert.spc. Now try signing again with the new fixed SPC file:
./signcode.exe -v mykey.pvk -spc myfixedcert.spc helloworld.exe
Now Windows should tell you that the signature is OK!

Failure to check the signature in Mono with the chktrust tool

Even though Windows confirmed the signature was good, I was unable to get the chktrust tool in Mono to successfully confirm the signature. The steps i attempted were to add the Thawte root certificate to the "Trust" store with
sudo certmgr -add -c -m Trust thawte-premium-server.cer
then confirm it is there with
certmgr -list -c -m Trust
then attempt to check the signature with
chktrust helloworld.exe
but the output is
WARNING! helloworld.exe is not timestamped!
ERROR! helloworld.exe couldn't find the certificate that signed the file!

Failed attempts to run official Microsoft tools under Wine

I also attempted to use both the older signcode.exe tool and the new signtool.exe running under Wine. Both failed, even after attempting to copy some missing DLLs from a Windows XP system into the local directory.

The signcode.exe tool actually does start and even show the dialog box for entering the PVK file's password ("Enter Private Key Password"), but after password entry Wine prints the following and no output EXE file is written:

fixme:crypt:CRYPT_VerifyImage (rsaenh.dll, 0x7fd6bc60): not verifying image
fixme:crypt:CRYPT_VerifyImage (rsaenh.dll, 0x7fd6fd18): not verifying image
Error: Unable to open a CSP provider with the correct private key
Error: Signing Failed.  Result = 80092006, (-2146885626)
Wine failed with return code 255
The signtool.exe tool is unable to start. Wine crashes into winedbg with the following output:
fixme:actctx:QueryActCtxW stub!
fixme:commctrl:InitCommonControlsEx Unknown class! dwICC=0x4000
fixme:seh:EXC_RtlRaiseException call (from 0x100a259) to unimplemented function msvcrt.dll._wsetlocale
fixme:msvcrt:_XcptFilter (-2147483392,0x7fb8e8b4)semi-stub
wine: Unhandled exception (thread 0009), starting debugger...
WineDbg starting on pid 0x8
Unhandled exception: unimplemented function msvcrt.dll._wsetlocale called in 32-bit code (0x7fa4d051).
In 32 bit mode.
Register dump:
 CS:0073 SS:007b DS:007b ES:007b FS:003b GS:0033
 EIP:7fa4d051 ESP:7fb8ed18 EBP:7fb8ed7c EFLAGS:00200212(   - 00      - -IA1)
 EAX:7fb8ed24 EBX:7fa7ac80 ECX:00000000 EDX:00000005
 ESI:7fc66490 EDI:00000000
Stack dump:
0x7fb8ed18:  01000000 0104c3a0 7fb8ed50 80000100
0x7fb8ed28:  00000001 00000000 0100a259 00000002
0x7fb8ed38:  7fa6f7e0 7fa6fb07 0104c908 7f81cb50
0x7fb8ed48:  00000005 0104c908 7fb8ed84 7f7d3342
0x7fb8ed58:  7fb8eea0 0104c90a 0000000a 00000000
0x7fb8ed68:  00000000 00000000 00000000 0000029d
Backtrace:
=>1 0x7fa4d051 in msvcrt (+0xd051) (0x7fb8ed7c)
  2 0x7fa4d956 __wine_stub_msvcrt_dll_536 in msvcrt (0x7fb8ed8c)
  3 0x0100a259 in signtool (+0xa259) (0x7fb8fea4)
  4 0x01010ab2 EntryPoint in signtool (0x7fb8ff20)
  5 0x7fc73de2 in kernel32 (+0x53de2) (0x7fb8fff4)
  6 0xb7f35181 wine_switch_to_stack in libwine.so.1 (0x00000000)
0x7fa4d051: subl        $4,%esp
Wine-dbg>
This page doesn't give much hope that this can be made to work.