Code Signing for Delphi and Inno Setup Apps
Delphi developers ship some of the longest-lived Windows software there is: point of sale systems, accounting packages, clinic and workshop tools, utilities that have been in daily use for fifteen years. Most of it is distributed as a single installer built with Inno Setup, sent to customers who are not technical.
That is exactly the audience that gets stopped cold by "Windows protected your PC". A developer knows to click More info and Run anyway. A shop owner installing your invoicing program does not, and an unsigned installer marked "Unknown publisher" looks to them like the thing they were warned about.
Most signing tutorials assume Visual Studio or Electron. This one is for Delphi and Inno Setup.
What needs a signature
Three files, not one:
- Your application exe (and any DLLs you build). This is what runs every day, and what antivirus engines look at after install.
- The installer that Inno Setup produces. This is what SmartScreen judges at download time.
- The uninstaller. Inno Setup generates it during compile. If it is unsigned, Windows shows an unknown publisher prompt when your customer removes or upgrades the program.
The order matters. Sign the exe first, then build the installer so it packs the signed exe, then sign the installer. If you sign the installer and rebuild the exe afterwards, the installer contains an unsigned program.
The traditional route, and why it got harder
The classic setup is a code signing certificate plus signtool.exe in a post-build step. Since 2023 the private key for any publicly trusted certificate has to live on a hardware token or a cloud HSM. For a one or two person Delphi shop that means a purchase of a few hundred dollars a year, a business identity check, and a USB token that must be plugged into whichever machine builds the release. If your build machine is a VM or a CI runner, the token becomes a real obstacle.
It is also worth knowing that an EV certificate no longer buys instant SmartScreen trust. Microsoft's documentation now states that EV and OV behave the same on first download. We covered that in EV certificates no longer skip SmartScreen.
Signing from Inno Setup
Inno Setup has first-class support for an external sign tool. It will call your tool for the installer, for the uninstaller, and for any file you flag in [Files]. With the Bamboo Deploy command line tool the script looks like this:
[Setup]
SignTool=bamboo
SignedUninstaller=yes
[Files]
Source: "MyApp.exe"; DestDir: "{app}"; Flags: signonce
Then register the tool named bamboo once, in the Inno Setup IDE under Tools, Configure Sign Tools:
cmd /c npx -y github:bamboodeploy/cli#v1.1.0 sign $f
Or pass it on the command line if you compile from a script:
ISCC.exe "/Sbamboo=cmd /c npx -y github:bamboodeploy/cli#v1.1.0 sign $f" setup.iss
Two details that trip people up:
- The
cmd /cprefix is required. Inno Setup starts the sign tool directly, without a shell, andnpxis a script on Windows, not an exe. Without the prefix you get a "could not execute" error that says nothing useful. signoncebeatssign. It skips files that already carry a signature, so a rebuild of the installer does not re-sign an exe that has not changed.
The tool uploads each file, waits for it to be signed, and writes the signed file back in place, which is what Inno Setup expects. It needs Node 18 or newer on the build machine and an API key in the BAMBOO_API_KEY environment variable, or in a .bamboorc file next to your project.
Signing straight from the Delphi IDE
If you ship a bare exe, or want the exe signed before Inno Setup ever sees it, add a post-build event under Project, Options, Build Events:
npx -y github:bamboodeploy/cli#v1.1.0 sign "$(OUTPUTPATH)"
Put it on the Release configuration only. There is no point signing every debug build, and each signature is a round trip to the service. Build events run through the command shell, so no cmd /c is needed here.
Delphi-specific false positives
Delphi binaries draw more antivirus heuristics than most, for reasons that have nothing to do with your code:
- The VCL is statically linked, so every Delphi exe shares a large block of identical code. Malware written in Delphi shares it too, and machine-learning engines generalise from that.
- Exe packers make it worse. UPX and similar tools shrink a Delphi exe nicely, and they are also the single strongest packer signal scanners look for. If you pack, stop, and compare the scan results.
- Inno Setup installers carry a compressed payload, which looks like any other high-entropy blob to a scanner that cannot unpack it.
The pattern to look for is a handful of detections with generic names (Heur, Gen:Variant, ML, Suspicious) and no named malware family. That is the false positive shape, and a valid signature is the main remedy for it. You can check a build before release, free:
npx -y github:bamboodeploy/cli#v1.1.0 scan MyAppSetup.exe
It prints the packer analysis, import analysis, rule matches, and signing status, so you see what a scanner sees.
A release checklist
- Fill in the version info in Project Options: company name, product name, file description, version. An exe with empty version info looks anonymous to users and to scanners.
- Build Release. Sign the exe.
- Compile the Inno Setup script with
SignToolandSignedUninstaller=yes. - Right-click the finished installer, Properties, Digital Signatures. Confirm the signature is present and valid. Do the same for the installed exe and
unins000.exe. - Test the download on a clean machine, through a browser, not from a network share. SmartScreen only checks files that carry the downloaded-from-the-web mark.
- Keep the signing identity the same from release to release. Reputation follows the certificate.
Bamboo Deploy signs Delphi and Inno Setup builds without a certificate purchase or a hardware token. Builds are scanned, then signed with our certificate. See the Inno Setup and Delphi docs, or get started here.