Jump to content

Sign your exe with a Digital Signature / Signtool.exe


Tankbuster
 Share

Recommended Posts

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
Link to comment
Share on other sites

  • 2 years later...

@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
Link to comment
Share on other sites

  • 3 months later...

...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

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

  • 4 months later...
  • 6 months later...
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 ):( 

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

...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

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

  • 4 years later...

What's New in Version 0.2021.4.22
Fixed: timestamp URL changed, so updated to a working URL, and moved the value from internally hard coded, to the INI file where it can be changed without having to recompile.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

  • 7 months later...
  • 2 weeks later...

What's New in Version 0.2021.12.20
Rewrote the whole thing. The help/guide is in the generated cert.dat file.

Now everything needed is a macro and the file is a list of commands, that way, you can experiment with different parameters.

This is from the cert.dat file:

Spoiler

There is the certificate creation and the signing of a file with the certificate.
Cert creation on this file starts with ":c:" and signing starts with ":s:".
After the cert creation, those lines can be safely removed. All the lines needed are the "@CertPassword=" line and ":s:" lines.

Each line will execute the given file on the BIN folder replacing the macro words with it's value.
Macro words start with "@" say, @CertPassword=MySecretPassword.
Each line runs in order as if in a batch file in this file.
The @CertPassword macro will be replaced with StringToBinary() for obfuscation, so don't start the password with "0x".
If @CertPassword=@RandomString, then it'll create a 50 to 60 character random string, replacing the Macro word with it's value

The macro words  available are:
@CertPassword: all this uses a password. This is the password value holder.
@FileToSignFullPath: that is the file dropped in or as first parameter.
@RandomString: generates a random A..Z,a..z,0..9 random string 50~60 char. long.
@NoPopup: Set to 1 to default to skip the popup.
@NoLogfile: Set to 1 to default to skip the log file.
@BinFolder: by default is "\BIN" but you can declare another path. ( @BinFolder=c:\my path )
@CertFullPathNoExt: is used internally to have the certs full path minus the extension


The switch words override the defaults and are:
/NoPopup: By default it will show the log to screen. This switch is to not show the popup screen.
/NoLogfile: By default it will write the log to a file. This switch is to not write to file.
/CertFolder: By default it uses the "Default" folder but you can start this with "/CertFolder=AnotherFolder",
if that folder or this file does not exist, this file will be created there as a template in that folder.

I've chosen to code it this way to make you familiar with the concepts and make it as future proof as possible.
https://docs.microsoft.com/en-us/windows/win32/seccrypto/makecert

Do change "CN=Change this before executing a second time" or this will popup for edit again !.
..and actually, is likely to be the only entry requiring change.

... ...

 

Edited by argumentum

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
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
 Share

×
×
  • Create New...