Jump to content
Tankbuster

Sign your exe with a Digital Signature / Signtool.exe

Recommended Posts

Tankbuster

If someone gets offended by this, because he knows already 200 posts about it. Do not reply.

Yes, some wrote already here some words what to do. Yes, you are right I created nothing new.

But I want this summed up for forum searchers, that maybe find this posting useful in spending less time to find an answer in one place.

I don't want to re-animate very old postings. So forgive me if I try to help other idiots like myself.

====================

First I need to give credits to:

For summing it up in a nearly complete way.

====================

There is a nice Gui existing that wrapps it up: Digital Sign Tool

Below you also find some instructions how to add it to the compiler.

====================

I tested this on a Windows 7 (x64) - but all codes used is x86 (32bit) - for 64bit I need to create another package (or make the script smarter).

So here is the sequence in general:

general purpose: I created several EXE files of the years, and I do not want to use the SIGNUI to click each file. So I want a command line version.

  • Create a folder - I named it x:cert4me
  • Download from : http://www.kastaban.de/cert4me/MAKE_MY_CERT_FILE.zip
  • The zip: Contains:
    1999-04-15 17:10:28 PVKIMPRT.EXE
    2003-03-24 23:03:00 makecert.exe
    2005-09-23 07:56:00 cert2spc.exe
    2006-03-03 23:22:44 signtool.exe
    2007-04-11 11:11:20 capicom.dll
    2007-09-27 14:17:44 pvk2pfx.exe
    2013-03-13 15:24:27 MAKE_MY_CERT_FILE.CMD
    2013-03-13 15:31:52 SIGN_FILE.CMD
  • Extract the files in : X:Cert4me
  • now open a cmd in the created folder with the extracted files (the script will also work from other folder but so you are closer to the result...)
    cd /D x:\cert4me

or

  • First start :
    MAKE_MY_CERT_FILE.CMD myNewCertificate secret01

    MAKE_MY_CERT_FILE.CMD needs two arguments

    1=the name of the certificate

    2=your password

  • Sign now the EXE with:
    SIGN_FILE.CMD "d:\1work\mit space\TFTP_HELPER.exe" keys\myNewCertificate_cert.pfx secret01

    SIGN_FILE.CMD needs three arguments

    1=the file path of your executable

    2=the filepath to the pfx folder (created before !)

    3=your password

Done.

in case you want to sign more than one EXE repeat the "Sign now" step with the other EXE.

=================

I packed the files from the original post and added additional files to offer a complete package.

this was tested on Windows 7 (64bit) but all tools and my EXE are 32bit . Keep this in mind.

For 64bit the similar tools of 64bit are maybe needed (not sure).

Maybe someone could start to convert this to a AU3.....because some fields are still left open. Like the End DATE, email field, timestamps URL.....

And "if" someone got additional stuff to add here (for other idio.....mmmh......searchers), do it.

And most important , in case I wrote something wrong or it does not work for you please comment.

========================================================================

//edit: 16.03.2013 - Thx Emiel - as always I face problems with the NOT and Or :-)

Edited by Tankbuster
  • Like 3

Share this post


Link to post
Share on other sites
Emiel Wieldraaijer

@Tankbuster

Thanks for the nice tutorial -> i believe you've missed a word

General purpose: I created several EXE files of the years, and I do NOT want to use the SIGNUI to click each file. So I want a command line version.


Best regards,Emiel Wieldraaijer

Share this post


Link to post
Share on other sites
MrPinkComputerShrink

@Tankbuster

Your tutorial was great and I am successfully signing my .exe but now Windows 8 SmartScreen is flagging the file.

Has anyone successfully applied an EV Code Signing Certificate to their .exe file created with AutoIt?

If so, were there any additional steps required or was it just like the regular code signing Certificate?

Reference: https://www.digicert.com/code-signing/ev-code-signing-compared.htm

Share this post


Link to post
Share on other sites
argumentum

...as to save others from the implementation and/or learning curve, here is the batch files as a script.

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=n
#AutoIt3Wrapper_Res_Description=sign my EXEs
#AutoIt3Wrapper_Res_requestedExecutionLevel=requireAdministrator
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#include <File.au3>

If Not StringInStr($CmdLineRaw, "/ErrorStdOut") And Not @Compiled Then Exit MsgBox(262144, @ScriptName, "...please run from editor.", 3)

Global $iCheckIni = 0
Global $ini = StringTrimRight(@ScriptFullPath, 3) & "ini"
Global $sCERTIFICATE_PASSWORD = "replace with unique password"
Global $sCN = "replace with unique CN"
If BinaryToString(IniRead($ini, "section", "CERTIFICATE_PASSWORD", $sCERTIFICATE_PASSWORD)) = $sCERTIFICATE_PASSWORD Then $iCheckIni = 1
If BinaryToString(IniRead($ini, "section", "CN", $sCN)) = $sCN Then $iCheckIni = 1

If $iCheckIni Then
    If FileExists(@ScriptDir & '\keys') Then DirMove(@ScriptDir & '\keys', @ScriptDir & '\keys_' & @YEAR & '.' & @MON & '.' & @MDAY & '-' & @HOUR & '' & @MIN & '' & @SEC)
    IniWrite($ini, "section", "CERTIFICATE_PASSWORD", $sCERTIFICATE_PASSWORD)
    IniWrite($ini, "section", "CN", $sCN)
    IniWrite($ini, "section", "UniqueSamplePasswordCreatedOn" & @YEAR & @MON & @MDAY & @HOUR & @MIN & @SEC, UniquePasswordGen())
    ShellExecute($ini)
    MsgBox(262144 + 64, @ScriptName, "edit the ini file" & @CR & 'and run again.', 10)
    Exit
EndIf

$sCERTIFICATE_PASSWORD = IniRead($ini, "section", "CERTIFICATE_PASSWORD", $sCERTIFICATE_PASSWORD)
$sCN = IniRead($ini, "section", "CN", $sCN)

If BinaryToString($sCERTIFICATE_PASSWORD) = $sCERTIFICATE_PASSWORD Then IniWrite($ini, "section", "CERTIFICATE_PASSWORD", StringToBinary($sCERTIFICATE_PASSWORD))
If BinaryToString($sCN) = $sCN Then IniWrite($ini, "section", "CN", StringToBinary($sCN))

$sCERTIFICATE_PASSWORD = BinaryToString($sCERTIFICATE_PASSWORD)
$sCN = BinaryToString($sCN)

$sCERTIFICATE_PASSWORD = BinaryToString(IniRead($ini, "section", "CERTIFICATE_PASSWORD", $sCERTIFICATE_PASSWORD))
$sCN = BinaryToString(IniRead($ini, "section", "CN", $sCN))

Global $WindowsDir = @WindowsDir
FileChangeDir(@ScriptDir)

If Not FileExists("makecert.exe") Then Exit MsgBox(262144, @ScriptName, 'can''t find "makecert.exe" , bye.', 30)
If Not FileExists("cert2spc.exe") Then Exit MsgBox(262144, @ScriptName, 'can''t find "cert2spc.exe" , bye.', 30)
If Not FileExists("pvk2pfx.exe") Then Exit MsgBox(262144, @ScriptName, 'can''t find "pvk2pfx.exe" , bye.', 30)
If Not FileExists("signtool.exe") Then Exit MsgBox(262144, @ScriptName, 'can''t find "signtool.exe" , bye.', 30)

Local $sFileToSign = StringTrimRight(@ScriptName, 3) & 'exe' ; should sign this exe
Local $iNoPopupMsgBox = 0
If StringInStr($CmdLineRaw, "/NoPopup") Then $iNoPopupMsgBox = 1
If @Compiled Then
    If $CmdLine[0] Then
        $sFileToSign = $CmdLine[1]
    Else
        FileChangeDir($WindowsDir)
        MsgBox(262144, @ScriptName, "...please, pass a file." & @CR & '"filename.exe" /NoPopup', 30)
        Exit
    EndIf
EndIf
$sFileToSign = _PathFull($sFileToSign)
If Not FileExists($sFileToSign) Then
    FileChangeDir($WindowsDir)
    Exit MsgBox(262144, @ScriptName, 'file "' & $sFileToSign & '" does not exist !', 60)
EndIf

If StringInStr($sFileToSign, " ") Then $sFileToSign = '"' & $sFileToSign & '"'

Global $sOut = ""
ConsoleWrite(@CRLF)
ConsoleWrite('+ CertSigner - FileToSign >' & $sFileToSign & '<' & @CRLF & @CRLF)
Local $sKEYDIR = @ScriptDir & "\keys"
DirCreate($sKEYDIR)
Local $sTIMESTAMP_URL = "http://timestamp.verisign.com/scripts/timestamp.dll"
If Not FileExists(@ScriptDir & '\keys\' & $sCN & '_cert.pvk') Then createTheCerts($sCERTIFICATE_PASSWORD, $sCN)
If FileExists(@ScriptDir & '\keys\' & $sCN & '_cert.pvk') Then signTheFile($sFileToSign, $sCERTIFICATE_PASSWORD)
Local $sRun = 'signtool verify /pa ' & $sFileToSign
$sOut &= $sRun & @CRLF & @CRLF
$sOut &= runCmd($sRun)
ConsoleWrite('+ CertSigner - done.' & @CRLF)
If $iNoPopupMsgBox Then
    ; nothing
Else
    MsgBox(262144, @ScriptName, "Signing of: " & $sFileToSign & @CR & _
            "=====================================================" & @CR & _
            StringReplace($sOut, @LF, ""))
EndIf
FileChangeDir($WindowsDir)

Func createTheCerts($sCERTIFICATE_PASSWORD, $sCN = "MyPersonalCert")
    Local $sRun = 'makecert -r -n "CN=' & $sCN & '" -b 01/01/1973 -e 01/01/2030 -eku 1.3.6.1.5.5.7.3.3 -sv keys\' & $sCN & '_cert.pvk keys\' & $sCN & '_cert.cer'
    $sOut &= $sRun & @CRLF & @CRLF
    Local $Pid = Run($sRun, @ScriptDir, @SW_HIDE, 8)
    Local $Handle = _ProcessExitCode($Pid)
    While ProcessExists($Pid)
        Sleep(10)
        $sOut &= ShowStdOutErr($Pid, 1, "", "", 1) ; the executable may show an error to console, I'd like to read it.
        If WinExists("Enter Private Key Password", "") Then
            ConsoleWrite(">Enter Private Key Password<" & @CRLF)
            ControlSend("Enter Private Key Password", "", "Edit1", $sCERTIFICATE_PASSWORD)
            Sleep(50)
            ControlClick("Enter Private Key Password", "", "Button1")
            Sleep(500)
        EndIf
        If WinExists("Create Private Key Password", "") Then
            ConsoleWrite(">Create Private Key Password<" & @CRLF)
            Sleep(50)
            ControlSend("Create Private Key Password", "", "Edit1", $sCERTIFICATE_PASSWORD)
            Sleep(50)
            ControlSend("Create Private Key Password", "", "Edit2", $sCERTIFICATE_PASSWORD)
            Sleep(50)
            ControlClick("Create Private Key Password", "", "Button1")
            Sleep(500)
        EndIf
    WEnd
    Local $ExitCode = _ProcessExitCode($Pid, $Handle)
    $sOut &= @CRLF & "ExitCode : " & $ExitCode & @CRLF & "=====================================================" & @CRLF

    $sRun = 'cert2spc keys\' & $sCN & '_cert.cer keys\' & $sCN & '_cert.spc'
    $sOut &= $sRun & @CRLF & @CRLF
    $sOut &= runCmd($sRun)

    $sRun = 'pvk2pfx -pvk keys\' & $sCN & '_cert.pvk -pi ' & $sCERTIFICATE_PASSWORD & ' -spc keys\' & $sCN & '_cert.spc -pfx keys\' & $sCN & '_cert.pfx -po ' & $sCERTIFICATE_PASSWORD
    $sOut &= $sRun & @CRLF & @CRLF
    $sOut &= runCmd($sRun)

EndFunc   ;==>createTheCerts

Func signTheFile($sFileToSign, $sCERTIFICATE_PASSWORD, $sTIMESTAMP_URL = "http://timestamp.verisign.com/scripts/timestamp.dll")

    Local $sRun = 'signtool sign /f keys\' & $sCN & '_cert.pfx /p ' & $sCERTIFICATE_PASSWORD & ' ' & $sFileToSign
    $sOut &= $sRun & @CRLF & @CRLF
    $sOut &= runCmd($sRun)

    $sRun = 'signtool timestamp /t ' & $sTIMESTAMP_URL & ' ' & $sFileToSign
    $sOut &= $sRun & @CRLF & @CRLF
    $sOut &= runCmd($sRun)

EndFunc   ;==>signTheFile

ConsoleWrite(@CRLF)

Func runCmd($sRun)
    Local $Pid = Run($sRun, @ScriptDir, @SW_HIDE, 8) ; 0x8 ($STDERR_MERGED) = Provides the same handle for STDOUT and STDERR. Implies both $STDOUT_CHILD and $STDERR_CHILD.
    Local $Handle = _ProcessExitCode($Pid)
    Local $Return_Text = ShowStdOutErr($Pid)
    Local $ExitCode = _ProcessExitCode($Pid, $Handle)
    _ProcessCloseHandle($Handle)
    Return $Return_Text & @CRLF & "ExitCode : " & $ExitCode & @CRLF & "=====================================================" & @CRLF
EndFunc   ;==>runCmd

; Get STDOUT and ERROUT from commandline tool
Func ShowStdOutErr($l_Handle, $ShowConsole = 1, $Replace = "", $ReplaceWith = "", $dontLoop = 0)
    Local $Line = "x", $Line2 = "x", $tot_out, $err1 = 0, $err2 = 0, $cnt1 = 0, $cnt2 = 0
    Do
        $Line = StdoutRead($l_Handle)
        $err1 = @error
        If $Replace <> "" Then $Line = StringReplace($Line, $Replace, $ReplaceWith)
        $tot_out &= $Line
        If $ShowConsole Then ConsoleWrite($Line)
        $Line2 = StderrRead($l_Handle)
        $err2 = @error
        If $Replace <> "" Then $Line2 = StringReplace($Line2, $Replace, $ReplaceWith)
        $tot_out &= $Line2
        If $ShowConsole Then ConsoleWrite($Line2)
        ; end the loop also when AutoIt3 has ended but a sub process was shelled with Run() that is still active
        ; only do this every 50 cycles to avoid cpu hunger
        If $cnt1 = 50 Then
            $cnt1 = 0
            ; loop another 50 times just to ensure the buffers emptied.
            If Not ProcessExists($l_Handle) Then
                If $cnt2 > 2 Then ExitLoop
                $cnt2 += 1
            EndIf
        EndIf
        $cnt1 += 1
        If $dontLoop Then Return $tot_out
        Sleep(10)
    Until ($err1 And $err2)
    Return $tot_out
EndFunc   ;==>ShowStdOutErr
Func _ProcessCloseHandle($h_Process)
    ; Close the process handle of a PID
    DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $h_Process)
    If Not @error Then Return 1
    Return 0
EndFunc   ;==>_ProcessCloseHandle
;===============================================================================
;
; Function Name:    _ProcessExitCode()
; Description:      Returns a handle/exitcode from use of Run().
; Parameter(s):     $i_Pid        - ProcessID returned from a Run() execution
;                   $h_Process    - Process handle
; Requirement(s):   None
; Return Value(s):  On Success - Returns Process handle while Run() is executing
;                                (use above directly after Run() line with only PID parameter)
;                              - Returns Process Exitcode when Process does not exist
;                                (use above with PID and Process Handle parameter returned from first UDF call)
;                   On Failure - 0
; Author(s):        MHz (Thanks to DaveF for posting these DllCalls in Support Forum)
;
;===============================================================================
;
Func _ProcessExitCode($i_Pid, $h_Process = 0)
    ; 0 = Return Process Handle of PID else use Handle to Return Exitcode of a PID
    Local $v_Placeholder
    If Not IsArray($h_Process) Then
        ; Return the process handle of a PID
        $h_Process = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'int', 0x400, 'int', 0, 'int', $i_Pid)
        If Not @error Then Return $h_Process
    Else
        ; Return Process Exitcode of PID
        $h_Process = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $h_Process[0], 'int*', $v_Placeholder)
        If Not @error Then Return $h_Process[2]
    EndIf
    Return 0
EndFunc   ;==>_ProcessExitCode

Func UniquePasswordGen()
    Local $n, $t
    For $n = 48 To 57 ; 0..9
        $t &= Chr($n)
    Next
    For $n = 65 To 90 ; A..Z
        $t &= Chr($n)
    Next
    For $n = 97 To 122 ; a..z
        $t &= Chr($n)
    Next
    Local $a = StringSplit($t, "")
    $t = ""
    For $n = 1 To 50
        $t &= $a[Random(1, $a[0], 1)]
    Next
    Return $t
EndFunc   ;==>UniquePasswordGen

I'm new at this. Any comments are very welcomed.

you can add a line like this to your au3 file:

#AutoIt3Wrapper_Run_After=""%scitedir%\tools\SignThisFile\CertSigner.exe" "%out%" /NoPopup"

where this script is compiled as CertSigner.exe in a folder named "tools\SignThisFile" under the SciTE editor's folder.

Edited by argumentum
revised the code

Share this post


Link to post
Share on other sites
jpm

Nice,

Are they restrictions in characters used in MAKE_MY_CERT_FILE.CMD parameters?

blanks, special chars as :;., ansi chars > 127

Thanks for the help

Share this post


Link to post
Share on other sites
argumentum
On 12/6/2015 at 3:48 AM, jpm said:

blanks, special chars as :;., ansi chars > 127

no idea, could not find an answer to this question but I use a random string as such:

Func UniquePasswordGen()
    Local $n, $t
    For $n = 48 To 57 ; 0..9
        $t &= Chr($n)
    Next
    For $n = 65 To 90 ; A..Z
        $t &= Chr($n)
    Next
    For $n = 97 To 122 ; a..z
        $t &= Chr($n)
    Next
    Local $a = StringSplit($t, "")
    $t = ""
    For $n = 1 To 50
        $t &= $a[Random(1, $a[0], 1)]
    Next
    Return $t
EndFunc   ;==>UniquePasswordGen

..late answer but, did not have one. ( still don't ):( 

Share this post


Link to post
Share on other sites
jpm

thanks for sharing.

I can't even have the script successful as the Key generation is failing Under Win10

the pvk2pfx.exe is failing with 0x80090005

I use the files of MAKE_MY_CERT_FILE.zip as suggested in the first post.

How can have it working?

Thanks for the help

Share this post


Link to post
Share on other sites
argumentum

This nice pic. shows how the self signed certificate is force fed to the certificate store ( win 10 pic. )

CertStore.png

 

Edited by argumentum
Removed the file. There is a newer version anyway.

Share this post


Link to post
Share on other sites
argumentum

this code should take care of any language other than english in working automatically.
 did not make the code to require Administrator rights for the fact that only at key creation is required. Not so for signing an EXE.

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=CertSigner.ico
#AutoIt3Wrapper_UseUpx=n
#AutoIt3Wrapper_UseAnsi=y
#AutoIt3Wrapper_Res_Comment=sign my EXEs
#AutoIt3Wrapper_Res_Description=sign my EXEs
#AutoIt3Wrapper_Res_Fileversion=0.2016.6.29
#AutoIt3Wrapper_Res_LegalCopyright=you bet
;~ #AutoIt3Wrapper_res_requestedExecutionLevel=requireAdministrator
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
;~ #AutoIt3Wrapper_Run_Au3Stripper=y
;~ #Au3Stripper_Parameters=/MO
#cs ----------------------------------------------------------------------------

    converted the batch files to a, not so nice, script

#ce ----------------------------------------------------------------------------

; Script Start - Add your code below here

#include <File.au3>


Global $iCheckIni = 0
Global $ini = StringTrimRight(@ScriptFullPath, 3) & "ini"
Global $sCERTIFICATE_PASSWORD = "replace with unique password"
Global $sCN = "replace with unique CN"
If BinaryToString(IniRead($ini, "cert data", "CERTIFICATE_PASSWORD", $sCERTIFICATE_PASSWORD)) = $sCERTIFICATE_PASSWORD Then $iCheckIni = 1
If BinaryToString(IniRead($ini, "cert data", "CN", $sCN)) = $sCN Then $iCheckIni = 1

If $iCheckIni Then
    If FileExists(@ScriptDir & '\keys') Then DirMove(@ScriptDir & '\keys', @ScriptDir & '\keys_' & @YEAR & '.' & @MON & '.' & @MDAY & '-' & @HOUR & '' & @MIN & '' & @SEC)
    IniWrite($ini, "cert data", "CERTIFICATE_PASSWORD", $sCERTIFICATE_PASSWORD)
    IniWrite($ini, "cert data", "CN", $sCN)
    IniWrite($ini, "sample data", "sample data", "this sample data generated a unique CERTIFICATE_PASSWORD you can use if you don't have one, the CN, is up to you.")
    IniWrite($ini, "sample data", "CERTIFICATE_PASSWORD", UniquePasswordGen())
    IniWrite($ini, "sample data", "CN", "my unique self sign name")
    IniWrite($ini, "sample data", "HashAlgorithm", "4 ; 0=md5, 1=sha1, 2=sha256, 3=sha384, 4=sha512, default is 1 (sha1)")
    IniWrite($ini, "sample data", "GeneratedKeyLengthBits", "4096 ; minimum is 512, default 2048")
    ShellExecute($ini)
    MsgBox(262144 + 64, @ScriptName, "edit the ini file" & @CR & 'and run again.', 10)
    Exit
EndIf

Global $g_aHashAlgorithm[5] = ["md5", "sha1", "sha256", "sha384", "sha512"]
Global $g_iHashAlgorithm = Int(IniRead($ini, "cert data", "HashAlgorithm", "1"))
If $g_iHashAlgorithm > 4 Or $g_iHashAlgorithm < 0 Then $g_iHashAlgorithm = 1
Global $g_sHashAlgorithm = " -a " & $g_aHashAlgorithm[$g_iHashAlgorithm]

Global $g_sGeneratedKeyLengthBits = Int(IniRead($ini, "cert data", "GeneratedKeyLengthBits", "2048"))
If $g_sGeneratedKeyLengthBits < 512 Then $g_sGeneratedKeyLengthBits = 2048
$g_sGeneratedKeyLengthBits = ' -len ' & $g_sGeneratedKeyLengthBits

$sCERTIFICATE_PASSWORD = IniRead($ini, "cert data", "CERTIFICATE_PASSWORD", $sCERTIFICATE_PASSWORD)
$sCN = IniRead($ini, "cert data", "CN", $sCN)

If BinaryToString($sCERTIFICATE_PASSWORD) = $sCERTIFICATE_PASSWORD Then IniWrite($ini, "cert data", "CERTIFICATE_PASSWORD", StringToBinary($sCERTIFICATE_PASSWORD))
If BinaryToString($sCN) = $sCN Then IniWrite($ini, "cert data", "CN", StringToBinary($sCN))

$sCERTIFICATE_PASSWORD = BinaryToString($sCERTIFICATE_PASSWORD)
$sCN = BinaryToString($sCN)

$sCERTIFICATE_PASSWORD = BinaryToString(IniRead($ini, "cert data", "CERTIFICATE_PASSWORD", $sCERTIFICATE_PASSWORD))
$sCN = BinaryToString(IniRead($ini, "cert data", "CN", $sCN))

If Not StringInStr($CmdLineRaw, "/ErrorStdOut") And Not @Compiled Then Exit MsgBox(262144, @ScriptName, "...please run from editor.", 3)

If StringInStr($CmdLineRaw, "/laconchadelalora") Then
    DirCreate(@ScriptDir & "\CodeDump")
    FileInstall("CertSigner.au3", @ScriptDir & "\CodeDump\CertSigner.au3")
    FileInstall("CertSigner.ico", @ScriptDir & "\CodeDump\CertSigner.ico")
    ShellExecute(@ScriptDir & "\CodeDump")
    Exit
EndIf



Global $WindowsDir = @WindowsDir
FileChangeDir(@ScriptDir)

If Not FileExists("makecert.exe") Then Exit MsgBox(262144, @ScriptName, 'can''t find "makecert.exe" , bye.', 30)
If Not FileExists("cert2spc.exe") Then Exit MsgBox(262144, @ScriptName, 'can''t find "cert2spc.exe" , bye.', 30)
If Not FileExists("pvk2pfx.exe") Then Exit MsgBox(262144, @ScriptName, 'can''t find "pvk2pfx.exe" , bye.', 30)
If Not FileExists("signtool.exe") Then Exit MsgBox(262144, @ScriptName, 'can''t find "signtool.exe" , bye.', 30)

Local $sFileToSign = StringTrimRight(@ScriptName, 3) & 'exe' ; should sign this exe
Local $iNoPopupMsgBox = 0
If StringInStr($CmdLineRaw, "/NoPopup") Then $iNoPopupMsgBox = 1
If @Compiled Then
    If $CmdLine[0] Then
        $sFileToSign = $CmdLine[1]
    Else
        FileChangeDir($WindowsDir)
        MsgBox(262144, @ScriptName, "...please, pass a file." & @CR & '"filename.exe" /NoPopup', 30)
        Exit
    EndIf
EndIf
$sFileToSign = _PathFull($sFileToSign)
If Not FileExists($sFileToSign) Then
    FileChangeDir($WindowsDir)
    Exit MsgBox(262144, @ScriptName, 'file "' & $sFileToSign & '" does not exist !', 60)
EndIf

If StringInStr($sFileToSign, " ") Then $sFileToSign = '"' & $sFileToSign & '"'

Global $sOut = ""
ConsoleWrite(@CRLF)
ConsoleWrite('+ CertSigner - FileToSign >' & $sFileToSign & '<' & @CRLF & @CRLF)
Local $sKEYDIR = @ScriptDir & "\keys"
DirCreate($sKEYDIR)
Local $sTIMESTAMP_URL = "http://timestamp.verisign.com/scripts/timestamp.dll"
If Not FileExists(@ScriptDir & '\keys\' & $sCN & '_cert.pvk') Then createTheCerts($sCERTIFICATE_PASSWORD, $sCN)
If FileExists(@ScriptDir & '\keys\' & $sCN & '_cert.pvk') Then signTheFile($sFileToSign, $sCERTIFICATE_PASSWORD)
Local $sRun = 'signtool verify /pa ' & $sFileToSign
$sOut &= $sRun & @CRLF & @CRLF
$sOut &= runCmd($sRun)
ConsoleWrite('+ CertSigner - done.' & @CRLF)
If $iNoPopupMsgBox Then
    ; nothing
Else
    MsgBox(262144, @ScriptName, "Signing of: " & $sFileToSign & @CR & "=====================================================" & @CR & StringReplace($sOut, @LF, ""))
EndIf
FileChangeDir($WindowsDir)

Func createTheCerts($sCERTIFICATE_PASSWORD, $sCN = "MyPersonalCert")
;~  -a   <algorithm>    The signature algorithm <md5|sha1>.  Default to 'md5'
;~ https://www.jayway.com/2014/09/03/creating-self-signed-certificates-with-makecert-exe-for-development/
;~ Generated Key Length (Bits)

    Local $sRun = 'makecert -r '& $g_sHashAlgorithm &' '& $g_sGeneratedKeyLengthBits &' -n "CN=' & $sCN & '" -b 01/01/1973 -e 01/01/2030 -eku 1.3.6.1.5.5.7.3.3 -sv "keys\' & $sCN & '_cert.pvk" "keys\' & $sCN & '_cert.cer"'
    $sOut &= $sRun & @CRLF & @CRLF
    Local $Pid = Run($sRun, @ScriptDir, @SW_HIDE, 8)
    Local $Handle = _ProcessExitCode($Pid)

    While ProcessExists($Pid)
        Sleep(10)
        $sOut &= ShowStdOutErr($Pid, 1, "", "", 1) ; the executable may show an error to console, I'd like to read it.
        If WinExists("[CLASS:#32770]", "Subject Key") And ControlGetText("[CLASS:#32770]", "Subject Key","Static4") <> "" Then
            ConsoleWrite(">Create Private Key Password<" & @CRLF)
            Sleep(50)
            ControlSend("[CLASS:#32770]", "Subject Key", "Edit1", $sCERTIFICATE_PASSWORD)
            Sleep(50)
            ControlSend("[CLASS:#32770]", "Subject Key", "Edit2", $sCERTIFICATE_PASSWORD)
            Sleep(50)
            ControlClick("[CLASS:#32770]", "Subject Key", "Button1")
            Sleep(500)
        ElseIf WinExists("[CLASS:#32770]", "Subject Key") Then
            ConsoleWrite(">Enter Private Key Password<" & @CRLF)
            ControlSend("[CLASS:#32770]", "Subject Key", "Edit1", $sCERTIFICATE_PASSWORD)
            Sleep(50)
            ControlClick("[CLASS:#32770]", "Subject Key", "Button1")
            Sleep(500)
        EndIf
    WEnd

    Local $ExitCode = _ProcessExitCode($Pid, $Handle)
    $sOut &= @CRLF & "ExitCode : " & $ExitCode & @CRLF & "=====================================================" & @CRLF

    $sRun = 'cert2spc "keys\' & $sCN & '_cert.cer" "keys\' & $sCN & '_cert.spc"'
    $sOut &= $sRun & @CRLF & @CRLF
    $sOut &= runCmd($sRun)

    $sRun = 'pvk2pfx -pvk "keys\' & $sCN & '_cert.pvk" -pi "' & $sCERTIFICATE_PASSWORD & '" -spc "keys\' & $sCN & '_cert.spc" -pfx "keys\' & $sCN & '_cert.pfx" -po "' & $sCERTIFICATE_PASSWORD & '"'
    $sOut &= $sRun & @CRLF & @CRLF
    $sOut &= runCmd($sRun)

EndFunc   ;==>createTheCerts

Func signTheFile($sFileToSign, $sCERTIFICATE_PASSWORD, $sTIMESTAMP_URL = "http://timestamp.verisign.com/scripts/timestamp.dll")

    If StringInStr($sCERTIFICATE_PASSWORD, " ") Then $sCERTIFICATE_PASSWORD = '"' & $sCERTIFICATE_PASSWORD & '"'
    Local $sRun = 'signtool sign /f "keys\' & $sCN & '_cert.pfx" /p "' & $sCERTIFICATE_PASSWORD & '" ' & $sFileToSign
    $sOut &= $sRun & @CRLF & @CRLF
    $sOut &= runCmd($sRun)

    $sRun = 'signtool timestamp /t ' & $sTIMESTAMP_URL & ' ' & $sFileToSign
    $sOut &= $sRun & @CRLF & @CRLF
    $sOut &= runCmd($sRun)

EndFunc   ;==>signTheFile

ConsoleWrite(@CRLF)

Func UniquePasswordGen()
    Local $n, $t, $l = Random(50, 60, 1)
    For $n = 48 To 57 ; 0..9
        $t &= Chr($n)
    Next
    For $n = 65 To 90 ; A..Z
        $t &= Chr($n)
    Next
    For $n = 97 To 122 ; a..z
        $t &= Chr($n)
    Next
    Local $a = StringSplit($t, "")
    $t = ""
    For $n = 1 To $l
        $t &= $a[Random(1, $a[0], 1)]
    Next
    ConsoleWrite(@CRLF & "key length: " & $l & " char. - key: " & $t & @CRLF & @CRLF)
    Return $t
EndFunc   ;==>UniquePasswordGen

Func runCmd($sRun)
    Local $Pid = Run($sRun, @ScriptDir, @SW_HIDE, 8) ; 0x8 ($STDERR_MERGED) = Provides the same handle for STDOUT and STDERR. Implies both $STDOUT_CHILD and $STDERR_CHILD.
    Local $Handle = _ProcessExitCode($Pid)
    Local $Return_Text = ShowStdOutErr($Pid)
    Local $ExitCode = _ProcessExitCode($Pid, $Handle)
    _ProcessCloseHandle($Handle)
    Return $Return_Text & @CRLF & "ExitCode : " & $ExitCode & @CRLF & "=====================================================" & @CRLF
EndFunc   ;==>runCmd

; Get STDOUT and ERROUT from commandline tool
Func ShowStdOutErr($l_Handle, $ShowConsole = 1, $Replace = "", $ReplaceWith = "", $dontLoop = 0)
    Local $Line = "x", $Line2 = "x", $tot_out, $err1 = 0, $err2 = 0, $cnt1 = 0, $cnt2 = 0
    Do
        $Line = StdoutRead($l_Handle)
        $err1 = @error
        If $Replace <> "" Then $Line = StringReplace($Line, $Replace, $ReplaceWith)
        $tot_out &= $Line
        If $ShowConsole Then ConsoleWrite($Line)
        $Line2 = StderrRead($l_Handle)
        $err2 = @error
        If $Replace <> "" Then $Line2 = StringReplace($Line2, $Replace, $ReplaceWith)
        $tot_out &= $Line2
        If $ShowConsole Then ConsoleWrite($Line2)
        ; end the loop also when AutoIt3 has ended but a sub process was shelled with Run() that is still active
        ; only do this every 50 cycles to avoid cpu hunger
        If $cnt1 = 50 Then
            $cnt1 = 0
            ; loop another 50 times just to ensure the buffers emptied.
            If Not ProcessExists($l_Handle) Then
                If $cnt2 > 2 Then ExitLoop
                $cnt2 += 1
            EndIf
        EndIf
        $cnt1 += 1
        If $dontLoop Then Return $tot_out
        Sleep(10)
    Until ($err1 And $err2)
    Return $tot_out
EndFunc   ;==>ShowStdOutErr
Func _ProcessCloseHandle($h_Process)
    ; Close the process handle of a PID
    DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $h_Process)
    If Not @error Then Return 1
    Return 0
EndFunc   ;==>_ProcessCloseHandle
;===============================================================================
;
; Function Name:    _ProcessExitCode()
; Description:      Returns a handle/exitcode from use of Run().
; Parameter(s):     $i_Pid        - ProcessID returned from a Run() execution
;                   $h_Process    - Process handle
; Requirement(s):   None
; Return Value(s):  On Success - Returns Process handle while Run() is executing
;                                (use above directly after Run() line with only PID parameter)
;                              - Returns Process Exitcode when Process does not exist
;                                (use above with PID and Process Handle parameter returned from first UDF call)
;                   On Failure - 0
; Author(s):        MHz (Thanks to DaveF for posting these DllCalls in Support Forum)
;
;===============================================================================
;
Func _ProcessExitCode($i_Pid, $h_Process = 0)
    ; 0 = Return Process Handle of PID else use Handle to Return Exitcode of a PID
    Local $v_Placeholder
    If Not IsArray($h_Process) Then
        ; Return the process handle of a PID
        $h_Process = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'int', 0x400, 'int', 0, 'int', $i_Pid)
        If Not @error Then Return $h_Process
    Else
        ; Return Process Exitcode of PID
        $h_Process = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $h_Process[0], 'int*', $v_Placeholder)
        If Not @error Then Return $h_Process[2]
    EndIf
    Return 0
EndFunc   ;==>_ProcessExitCode

 The AutoIt3Wrapper directives are for an older AutoIt. ( but that you know. )
Hope this version works. Else, we keep looking :) 

Edit1: I was revising the code and see the part of the INI where HashAlgorithm=4 will not work with these older exe's. HashAlgorithm=1 would work fine. If everything work out in this go, I'll repack everything with newer EXEs.

Edited by argumentum

Share this post


Link to post
Share on other sites
argumentum

...as to try if language would be a problem, one of the strangest languages for me is Korean, so I made me a Win 10 KN Korean x64 to test
and a French one, and a Spanish one, and it works just fine. =) Win10_French_SelfCert_Show.pngWin10_korean_SelfCert_Show.png

Edited by argumentum
change the pics

Share this post


Link to post
Share on other sites
argumentum
2016.07.01b
fixed the installer. At times the template on vista and newer goes to c:\users\All Users\.. ..\Templates

2016.07.01 ; https://www.autoitscript.com/forum/topic/149137-sign-your-exe-with-a-digital-signature-signtoolexe/?do=findComment&comment=1316935
after extensive testing, this is the best I can come up with, to simplify self signing the executable compiled
with AutoIt3 from SciTE ( the editor ). Could have given the code more options but I felt at the time of coding
that it was practical enough and if you ( the coder ) feel different, the code is in the file to tweak or just plain rewrite.
In this ZIP are included the updated utility files from the original distribution of this package plus an installer of sorts
called "copy all these to SciTE path.exe", to place the files in the path that the "#AutoIt3Wrapper_Run_After" command can find
even if running as a portable setup. The path is %scitedir%\tools\SignThisFile\CertSigner.exe and it will also patch the Template.au3
( if one is found ), to add the directive, with /NoPopup /NoLogfile ( therefore CertSigner.exe would otherwise write a log file
and show a GUI with said log ), but the output is displayed at SciTE's console anyway, so what's the need for more bells and whistles. No need.
Unless you run it in self standing mode, by dropping a file to it. Then you do want to have a feedback. In such case, a log is written and displayed.
You may even add a shortcutr to "SendTo", to sign files from explorer.

I tested the original code from Windows 2000 to Windows 10 and this compilation in english, spanish, french and korean, to assure 
uniform looks and functionality under every circumstance that I can think of at this moment.

An ini file will be created as you first run CertSigner.exe, for you to edit and create your own self signed certificate.
Do add the certificate to the store as trusted root ( otherwise what's the point ), and by doing so, the verification at the 
end of the signing will pass.

Enjoy

 

Edited by argumentum
file moved to downloads
  • Like 2

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Similar Content

    • R0G
      By R0G
      Digital Sign Tool V0.5

      Features:
      Digitally sign: (.exe) (.cab) (.cat) (.ocx) (.dll) (.stl) Metro style GUI thanks @BBs19 Error logging Multi file support Instructions:
      You must have your (.pfx) certificate imported to the "Current User\Personal\Certificates" store Your certificate must be exportable Select your digital certificate from the drop down menu Click browse to add your files to sign Click sign For more information please visit: 
      https://www.autoitscript.com/forum/topic/149137-sign-your-exe-with-a-digital-signature-signtoolexe/#comment-1061738
      https://msdn.microsoft.com/en-us/library/bfsktky3(vs.100).aspx?f=255&MSPPError=-2147217396
      Download:
      Digital Sign Tool.zip
×