Summary
The desktop installers built by .github/workflows/desktop-installers.yml (via build-desktop.sh) are not code-signed. As a result:
- macOS: Gatekeeper blocks first launch. The Release notes currently tell users to right-click the app and choose Open, then confirm.
- Windows: SmartScreen warns on the
.msi. Users must choose More info then Run anyway.
For tooling aimed at memory institutions on locked-down workstations, those warnings are a real adoption barrier and often violate local software policy. This issue records what it takes to sign and notarize the four installers so the warnings go away, so we can budget for it and implement it.
Signing needs certificates and CI secrets that must be provisioned first, so this is scoped as a planning issue rather than a ready-to-merge PR.
Current build (for reference)
Four targets, each on its own runner, produced by build-desktop.sh:
| Target |
Runner |
Output |
Signed today |
| linux-x64 |
ubuntu-latest |
.deb |
n/a |
| mac-x64 |
macos-latest + Rosetta 2 |
.dmg |
no |
| mac-arm64 |
macos-latest |
.dmg |
no |
| windows-x64 |
windows-latest |
.msi |
no |
Linux packages are not code-signed in the Authenticode/Gatekeeper sense, so this issue covers macOS and Windows only.
macOS: Developer ID signing + notarization
Two separate steps are both required for Gatekeeper to pass silently:
- Sign the
.app (and the bundled JRE and JavaFX .dylibs inside it) with a Developer ID Application certificate, using the hardened runtime and a secure timestamp.
- Notarize the resulting
.dmg with Apple's notary service, then staple the ticket so it verifies offline (important for air-gapped machines).
Prerequisites (org / one-off)
- Apple Developer Program membership (99 USD/year), under an OPF-owned Apple Account, ideally an Organization account so the certificate is not tied to one person. Given the project's history, a shared org account matters.
- A Developer ID Application certificate (not "Mac App Store"), exported as a password-protected
.p12.
- An App Store Connect API key (
.p8 + key id + issuer id) for notarization. Preferred over an Apple ID + app-specific password because it is not tied to a person and does not break on 2FA.
jpackage support
jpackage can sign directly during packaging, so most of this lives in build-desktop.sh behind a "signing material present" check:
--mac-sign \
--mac-signing-key-user-name "Developer ID Application: Open Preservation Foundation (TEAMID)" \
--mac-signing-keychain "$KEYCHAIN_PATH" \
--mac-entitlements branding/mac/entitlements.plist
The JVM needs hardened-runtime exceptions, so entitlements.plist must include:
com.apple.security.cs.allow-jit
com.apple.security.cs.allow-unsigned-executable-memory
com.apple.security.cs.disable-library-validation
disable-library-validation is needed because JavaFX loads native libraries that jpackage does not sign with our leaf identity.
Notarize + staple (after the .dmg is built)
xcrun notarytool submit OpenFixity-<ver>-mac-<arch>.dmg \
--key ASC_KEY.p8 --key-id "$ASC_KEY_ID" --issuer "$ASC_ISSUER_ID" --wait
xcrun stapler staple OpenFixity-<ver>-mac-<arch>.dmg
Both the mac-x64 and mac-arm64 jobs need this. The Rosetta-built x64 .dmg signs and notarizes the same way as the native arm64 one.
CI additions
- Decode the base64
.p12 secret, create and unlock a temporary keychain, import the cert, set it default, and delete the keychain at the end of the job.
- Run the signed build, then notarize and staple.
Secrets
MACOS_CERT_P12_BASE64, MACOS_CERT_PASSWORD, KEYCHAIN_PASSWORD, MACOS_TEAM_ID, ASC_API_KEY_P8_BASE64, ASC_API_KEY_ID, ASC_API_ISSUER_ID.
Windows: Authenticode signing
Prerequisites (org / one-off)
Since June 2023, standard OV and EV code-signing private keys must live on hardware (HSM/USB token) or a cloud signing service. There is no downloadable .pfx for newly issued OV certificates, so the old "PFX in a secret" approach is not available for a new cert. Options:
- Azure Trusted Signing (recommended). Roughly 10 USD/month, has an official GitHub Action (
azure/trusted-signing-action), and needs no hardware. Identity validation for an organization takes a few days.
- DigiCert KeyLocker or SSL.com eSigner: cloud HSM signing, also CI-friendly, higher cost.
- Physical EV token: not CI-friendly (manual token on a machine), avoid.
EV certificates get instant SmartScreen reputation. OV / Trusted Signing builds reputation as downloads accumulate, so early users may still see a warning for a while.
jpackage support
jpackage does not sign on Windows, so signing is a separate signtool step in two places:
- The app-image launcher
OpenFixity.exe, signed before the .msi is built.
- The final
.msi.
signtool sign /fd SHA256 /tr http://timestamp.acs.microsoft.com /td SHA256 <file>
The RFC3161 timestamp (/tr + /td) is essential so signatures stay valid after the certificate expires.
Implementation note: build-desktop.sh runs jpackage app-image then installer in one pass. To sign the launcher between those, split the Windows path so it signs the app-image .exe before invoking the --type msi step, then signs the .msi. Keep it conditional on signing secrets being present.
Secrets (Azure Trusted Signing)
AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, TRUSTED_SIGNING_ENDPOINT, TRUSTED_SIGNING_ACCOUNT, TRUSTED_SIGNING_CERT_PROFILE.
Wiring pattern
Mirror the existing optional-token pattern used for the OPF upload: signing runs only when its secrets are present, so local builds and forks still produce working unsigned installers. Signing material is never required to build.
Cleanup once signing lands
- Remove the "not code-signed / right-click Open / More info then Run anyway" text from the Release body in
desktop-installers.yml and from INSTALL.md.
- Add a short note that installers are signed and notarized.
Checklist
Org / procurement:
Implementation:
References
- jpackage macOS signing options (
--mac-sign, --mac-signing-key-user-name, --mac-entitlements)
notarytool and stapler (Xcode command line tools)
azure/trusted-signing-action GitHub Action
- Microsoft's June 2023 change requiring hardware/cloud key storage for code-signing certificates
Summary
The desktop installers built by
.github/workflows/desktop-installers.yml(viabuild-desktop.sh) are not code-signed. As a result:.msi. Users must choose More info then Run anyway.For tooling aimed at memory institutions on locked-down workstations, those warnings are a real adoption barrier and often violate local software policy. This issue records what it takes to sign and notarize the four installers so the warnings go away, so we can budget for it and implement it.
Signing needs certificates and CI secrets that must be provisioned first, so this is scoped as a planning issue rather than a ready-to-merge PR.
Current build (for reference)
Four targets, each on its own runner, produced by
build-desktop.sh:.deb.dmg.dmg.msiLinux packages are not code-signed in the Authenticode/Gatekeeper sense, so this issue covers macOS and Windows only.
macOS: Developer ID signing + notarization
Two separate steps are both required for Gatekeeper to pass silently:
.app(and the bundled JRE and JavaFX.dylibs inside it) with a Developer ID Application certificate, using the hardened runtime and a secure timestamp..dmgwith Apple's notary service, then staple the ticket so it verifies offline (important for air-gapped machines).Prerequisites (org / one-off)
.p12..p8+ key id + issuer id) for notarization. Preferred over an Apple ID + app-specific password because it is not tied to a person and does not break on 2FA.jpackage support
jpackage can sign directly during packaging, so most of this lives in
build-desktop.shbehind a "signing material present" check:The JVM needs hardened-runtime exceptions, so
entitlements.plistmust include:disable-library-validationis needed because JavaFX loads native libraries that jpackage does not sign with our leaf identity.Notarize + staple (after the .dmg is built)
Both the
mac-x64andmac-arm64jobs need this. The Rosetta-built x64.dmgsigns and notarizes the same way as the native arm64 one.CI additions
.p12secret, create and unlock a temporary keychain, import the cert, set it default, and delete the keychain at the end of the job.Secrets
MACOS_CERT_P12_BASE64,MACOS_CERT_PASSWORD,KEYCHAIN_PASSWORD,MACOS_TEAM_ID,ASC_API_KEY_P8_BASE64,ASC_API_KEY_ID,ASC_API_ISSUER_ID.Windows: Authenticode signing
Prerequisites (org / one-off)
Since June 2023, standard OV and EV code-signing private keys must live on hardware (HSM/USB token) or a cloud signing service. There is no downloadable
.pfxfor newly issued OV certificates, so the old "PFX in a secret" approach is not available for a new cert. Options:azure/trusted-signing-action), and needs no hardware. Identity validation for an organization takes a few days.EV certificates get instant SmartScreen reputation. OV / Trusted Signing builds reputation as downloads accumulate, so early users may still see a warning for a while.
jpackage support
jpackage does not sign on Windows, so signing is a separate
signtoolstep in two places:OpenFixity.exe, signed before the.msiis built..msi.The RFC3161 timestamp (
/tr+/td) is essential so signatures stay valid after the certificate expires.Implementation note:
build-desktop.shruns jpackage app-image then installer in one pass. To sign the launcher between those, split the Windows path so it signs the app-image.exebefore invoking the--type msistep, then signs the.msi. Keep it conditional on signing secrets being present.Secrets (Azure Trusted Signing)
AZURE_TENANT_ID,AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,TRUSTED_SIGNING_ENDPOINT,TRUSTED_SIGNING_ACCOUNT,TRUSTED_SIGNING_CERT_PROFILE.Wiring pattern
Mirror the existing optional-token pattern used for the OPF upload: signing runs only when its secrets are present, so local builds and forks still produce working unsigned installers. Signing material is never required to build.
Cleanup once signing lands
desktop-installers.ymland fromINSTALL.md.Checklist
Org / procurement:
.p12Implementation:
branding/mac/entitlements.plistwith the JVM exceptionsbuild-desktop.sh/ workflow (both mac jobs)signtoolsigning of the launcher and the.msispctl -a -vv OpenFixity.apppasses on macOS;signtool verify /pa OpenFixity-<ver>-windows-x64.msipasses on WindowsReferences
--mac-sign,--mac-signing-key-user-name,--mac-entitlements)notarytoolandstapler(Xcode command line tools)azure/trusted-signing-actionGitHub Action