Search This Blog

Saturday, April 7, 2012

Client Scripts » A script to authenticode-sign your executables (an alternative to signtool.exe)

First note that you'll need an authenticode certificate, issued by a recognized certificate authority (CA). The cheapest I've found is Comodo, if you dig a little you can get a cert *with exportable private key (PK)* from them for $100.

Why is it important that the PK is exportable? Well, without that you won't be able to copy or move the cert to any other machines (without help from the CA) and you won't be able to use signtool's CLI. (Whether or not my script will work without exportable PK I'm uncertain, but my assumption is that it would not.)

Whatever you do, don't go to Verisign for a cert, unless you have more money than you know what to do with! Last I checked they wanted you to pay extra for their "pro" cert if you wanted to export the PK. Verisign just sucks in general -- I'm still pissed off about the bullshit they pulled in 2000... but that's another story...

Back to the topic, the script in downloads will use a cert from your cert store only in the simplest of cases, i.e., you have one authenticode cert in your personal store. I'm sure it's possible to modify it for more complex cases, if you can navigate the Certificates collection. (Comments are inline in the code.) Barring that, you can always export the cert with PK to a .PFX file and use that, as long as you don't mind storing the password in your script in clear text.

If you're wondering why use a script, rather than calling signtool.exe's CLI from a batch... I suspect that if you've found this topic, you already have your own reasons -- please by all means post them under this topic. (I'll post my reasons for writing the script if there's any activity here.)

var oCode = new ActiveXObject("CAPICOM.SignedCode");
var dirname = WScript.ScriptFullName.slice(0, WScript.ScriptName.length * -1);
var filename = dirname + WScript.Arguments.Named("file");
oCode.FileName = filename;
  
var isSigned = false;
try {
 var buf = oCode.Description;
 isSigned = true;
} catch(e) {
 isSigned = false;
}

try {
 if (isSigned == false)
 {
  var oCode = new ActiveXObject("CAPICOM.SignedCode");
  var oSigner = new ActiveXObject("CAPICOM.Signer.1");
  oCode.FileName = filename;
  // The code below assumes a very simple personal cert store, with only
  // one authenticode cert in it, or with the desired cert being first
  // in the list. More complex cases can be handled by navigating the store.
  //
  var oStore = new ActiveXObject("CAPICOM.Store.1");
  oStore.Open();
  oSigner.Certificate = oStore.Certificates.item(1);
  // Alternative to 3 lines above, requires you to export your code signing
  // cert with private key, to a file (and assumes it is in the same dir as
  // this script. Also requires coding the pwd in your script in plain text.
  //
  //oSigner.Load(dirname + "signingcert.pfx", "pwd");
  oCode.Sign(oSigner);
  oCode.TimeStamp("http://timestamp.comodoca.com/authenticode");

  // throw an error on purpose if signing failed
  buf = oCode.Description;
  oStore.Close();
 }
} catch(e) {
 WScript.Echo("An error occurred: " + e.description + "\r\n(Press ENTER to quit)\r\n\r\n");
 WScript.StdIn.Read();
}

No comments:

Post a Comment