Code signing and SmartScreen
New in 1.5.1 When someone downloads JBrowser-Setup-<version>.exe with a browser and runs it, Windows SmartScreen
can stop it with "Windows protected your PC". The build can sign JBrowser's programs with a code-signing
certificate; this page explains what that does and doesn't change.
Why the warning appears#
SmartScreen checks programs that carry the Mark-of-the-Web, the tag browsers add to downloaded files (extracting a downloaded zip passes it on). A program runs without a warning when it has a reputation: enough people have run it, or it is signed with a certificate that has.
| Situation | Warning? |
|---|---|
| Unsigned installer, downloaded with a browser | Yes, until that exact file has been run by enough people. Every release is a new file and starts again. |
| Signed with a certificate from a trusted certificate authority | At first, sometimes. The reputation belongs to the certificate, so it builds up over releases and new versions inherit it. |
| Signed with a self-signed certificate | Yes: Windows doesn't trust the certificate. |
Installed with the one-command install (install.ps1) |
No: PowerShell's download has no Mark-of-the-Web, and the script removes it after checking the SHA-256. |
| Updated by JBrowser's updater | No: the installer is checked against its SHA-256 and run without the Mark-of-the-Web. |
EV certificates no longer skip the reputation stage (since 2024). With Smart App Control on (a Windows 11 option, off on most PCs), unsigned programs are blocked outright.
Getting a certificate#
Every option checks the publisher's identity first, so it has to be requested by the project's owner. docs/SIGNING.md compares them: SignPath Foundation (free for open-source projects, for builds made by CI), Certum Open Source Code Signing (from about €69, on a smart card), Azure Artifact Signing (US$9.99 a month; individuals in the USA and Canada only) and commercial OV certificates. A Microsoft Store listing (MSIX, signed by the Store) avoids SmartScreen entirely.
How the build signs#
tools/sign.ps1 does the signing. It is called for:
| File | Called by |
|---|---|
dist\JBrowser\JBrowser.exe |
tools\build_app.ps1, right after PyInstaller |
| the uninstaller inside Setup | Inno Setup, while compiling (SignedUninstaller=yes) |
JBrowser-Setup-<version>.exe |
Inno Setup, at the end; the .sha256 is written after it |
tools\build_installer.ps1 asks sign.ps1 -Status whether a certificate is configured. If one is, it passes
/DSignSetup (which turns on SignTool=jbsign in installer/JBrowser.iss) and
defines the jbsign tool as powershell.exe -File tools\sign.ps1 $f. With nothing configured, sign.ps1 does
nothing and builds are unsigned.
The certificate comes from environment variables:
| Variable | Meaning |
|---|---|
JBROWSER_SIGN_THUMBPRINT |
SHA-1 thumbprint of a certificate in Cert:\CurrentUser\My or Cert:\LocalMachine\My, including hardware tokens and cloud HSMs |
JBROWSER_SIGN_PFX, JBROWSER_SIGN_PFX_PASSWORD |
a .pfx file and its password |
JBROWSER_SIGN_AZURE, JBROWSER_SIGN_AZURE_DLIB |
Azure Artifact Signing: metadata.json and Azure.CodeSigning.Dlib.dll |
JBROWSER_SIGN_TIMESTAMP |
a timestamp server (default: DigiCert, then Sectigo, then GlobalSign) |
JBROWSER_SIGN_TEST=1 |
accept an untrusted (self-signed) certificate, for testing; master.ps1 -Publish refuses to run with it |
$env:JBROWSER_SIGN_THUMBPRINT = "0123456789ABCDEF0123456789ABCDEF01234567"
.\tools\sign.ps1 -Status # the certificate and tool that will be used
.\master.ps1 -Version 1.5.2 -Publish
sign.ps1 uses signtool.exe from the Windows SDK when it is installed (required for Azure), and otherwise
Set-AuthenticodeSignature. Signatures are SHA-256 and timestamped, so they stay valid after the certificate
expires. Timestamp servers are retried, and each file is checked afterwards: the signature must be Valid (or, in
test mode, present and made by the configured certificate).
master.ps1 shows before building whether signing is configured, and afterwards whether Setup is signed. A signed
build's release notes and INSTALL.txt name the publisher instead of explaining the SmartScreen warning.
PyInstaller and signing
Signing appends a certificate table to JBrowser.exe. PyInstaller's bootloader finds its archive from the end of
the file and copes with that; a signed build starts normally. Check with a throw-away profile after changing how
the exe is built.
Testing without a real certificate#
Create a self-signed code-signing certificate in memory and export it as a .pfx (CertificateRequest in .NET), so
nothing is added to Windows' certificate stores. Then set JBROWSER_SIGN_PFX, JBROWSER_SIGN_PFX_PASSWORD and
JBROWSER_SIGN_TEST=1, build or sign a copy, check Get-AuthenticodeSignature (it reports UnknownError: the
certificate isn't trusted), and delete the .pfx. Never publish a build signed this way.