JBrowser Source Docs
You are reading the documentation for JBrowser 1.5.2. The newest release is 1.5.4. Go to the latest documentation

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.

Signed builds on GitHub Actions (SignPath Foundation)#

New in 1.5.2 SignPath Foundation signs open-source releases for free, but only builds it can trace to the public repository: made by a workflow on GitHub-hosted runners, and approved by hand for each release. JBrowser is set up for it; the project owner applies and connects the accounts (docs/SIGNING.md lists the steps).

master.ps1 -Publish ──► release.ps1 ──► draft release vX.Y.Z (notes only)
                                   └──► release-build.yml on windows-latest (GitHub-hosted)
                                          build JBrowser.exe ──► SignPath "app" ──► signed JBrowser.exe
                                          build the installer ─► SignPath "installer" ─► signed Setup
                                          check signatures, write .sha256, package the zip (tools/package.ps1)
                                          attach the files, publish the release, rebuild the website
Piece Role
release-build.yml The build. Inputs: ref (a tag) and publish. With the SIGNPATH_ORGANIZATION_ID variable and SIGNPATH_API_TOKEN secret it submits signing requests (signpath/github-action-submit-signing-request@v3, waiting up to a day for approval); without them it builds unsigned, which is how the workflow is tested.
.signpath/artifact-configurations app.xml and installer.xml, pasted into SignPath. They restrict signing to files whose product name is JBrowser and whose version is the release's.
Test-SignPath in tools/common.ps1 Asks GitHub whether the variable exists; release.ps1 then creates a draft and starts the workflow instead of uploading a local build.
tools/package.ps1 Makes the zip, the same way on the PC and in the workflow.
Updater.signature_problem() in services/updater.py Once the running JBrowser is signed, an update must be validly signed by the same publisher (win.authenticode()), on top of the SHA-256 check.

The installer's version resource carries the same product name and version as JBrowser.exe (VersionInfoProductName, VersionInfoProductTextVersion in the .iss), so both pass SignPath's metadata checks. SignPath signs the installer after Inno Setup has built it, so the uninstaller inside stays unsigned; Windows never checks an uninstaller's reputation.

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.