Skip to main content

Overview

Code signing is essential for distributing desktop applications. It verifies that your application comes from a trusted source and hasn’t been tampered with. Different platforms have different requirements:
  • macOS: Requires code signing and notarization for Gatekeeper
  • Windows: Requires code signing to avoid SmartScreen warnings
  • Linux: Optional, but recommended for some distributions
Without proper code signing, users will see security warnings when trying to install or run your application.

macOS Code Signing

NativePHP includes built-in support for macOS code signing and notarization using Apple’s notarytool.

Prerequisites

1

Apple Developer Account

You need an active Apple Developer Program membership ($99/year).
2

Developer Certificate

Obtain a “Developer ID Application” certificate from your Apple Developer account.
3

App-Specific Password

Generate an app-specific password for notarization at appleid.apple.com.

Configuration

Set the following environment variables in your .env file:
.env
NATIVEPHP_APPLE_ID=[email protected]
NATIVEPHP_APPLE_ID_PASS=xxxx-xxxx-xxxx-xxxx
NATIVEPHP_APPLE_TEAM_ID=XXXXXXXXXX
NATIVEPHP_APP_ID=com.yourcompany.yourapp
Your Team ID can be found in your Apple Developer account under Membership Details.

Notarization Process

NativePHP automatically handles notarization after the build is signed:
// resources/electron/build/notarize.js:24
await notarize({
    appBundleId: appId,
    appPath: `${appOutDir}/${appName}.app`,
    appleId: process.env.NATIVEPHP_APPLE_ID,
    appleIdPassword: process.env.NATIVEPHP_APPLE_ID_PASS,
    teamId: process.env.NATIVEPHP_APPLE_TEAM_ID,
    tool: 'notarytool',
})
The notarization happens automatically during the build process:
1

Build and Sign

Electron Builder signs your application with your Developer ID certificate.
2

Submit for Notarization

The afterSign hook submits your app to Apple’s notarization service.
3

Wait for Approval

Apple’s servers scan your app (usually takes a few minutes).
4

Staple Ticket

The notarization ticket is stapled to your application bundle.

Entitlements

NativePHP includes macOS entitlements for common permissions:
// resources/electron/electron-builder.mjs:104
mac: {
    entitlementsInherit: 'build/entitlements.mac.plist',
    extendInfo: {
        NSCameraUsageDescription: "Application requests access to the device's camera.",
        NSMicrophoneUsageDescription: "Application requests access to the device's microphone.",
        NSDocumentsFolderUsageDescription: "Application requests access to the user's Documents folder.",
        NSDownloadsFolderUsageDescription: "Application requests access to the user's Downloads folder.",
    },
}
These usage descriptions appear in permission dialogs when your app requests access to protected resources.

Platform Detection

The notarization script only runs when building for macOS:
// resources/electron/build/notarize.js:4
if (process.platform !== 'darwin') return;
if (context.packager.platform.name !== 'mac') return;
This allows you to build for multiple platforms without worrying about platform-specific code signing.

Windows Code Signing

Windows code signing prevents SmartScreen warnings and establishes trust with your users.

Traditional Code Signing

Requirements:
  • A valid code signing certificate from a trusted CA (DigiCert, Sectigo, etc.)
  • The certificate in PFX format with password
Configuration:
.env
WIN_CSC_LINK=/path/to/certificate.pfx
WIN_CSC_KEY_PASSWORD=your-certificate-password
Electron Builder automatically detects these environment variables and signs your Windows executable.

Azure Trusted Signing (Cloud-Based)

NativePHP supports Azure Trusted Signing, a cloud-based code signing solution:
.env
NATIVEPHP_AZURE_ENDPOINT=https://eus.codesigning.azure.net/
NATIVEPHP_AZURE_CERTIFICATE_PROFILE_NAME=YourProfileName
NATIVEPHP_AZURE_CODE_SIGNING_ACCOUNT_NAME=YourAccountName
// resources/electron/electron-builder.mjs:86
win: {
    executableName: fileName,
    ...(azureEndpoint && azureCertificateProfileName && azureCodeSigningAccountName ? {
        azureSignOptions: {
            endpoint: azureEndpoint,
            certificateProfileName: azureCertificateProfileName,
            codeSigningAccountName: azureCodeSigningAccountName
        }
    } : {}),
}
Pros:
  • One-time purchase
  • Works offline
  • Full control over private key
Cons:
  • Certificate management complexity
  • Risk of key compromise
  • Annual renewal required

Building Reputation

Even with a valid certificate, Windows SmartScreen may show warnings for new applications. Building reputation requires:
  1. Consistent Signing: Always sign with the same certificate
  2. User Downloads: More users downloading and running your app
  3. Time: Reputation builds over weeks/months
  4. Extended Validation (EV) Certificate: Provides instant reputation
EV certificates provide immediate SmartScreen reputation but are more expensive and require hardware tokens.

Linux Code Signing

Linux doesn’t have platform-wide code signing requirements, but some distributions support it:

AppImage Signing

AppImages can be signed with GPG:
# Sign the AppImage
gpg --detach-sign YourApp-1.0.0.AppImage

# Verify the signature
gpg --verify YourApp-1.0.0.AppImage.sig YourApp-1.0.0.AppImage

Debian Package Signing

For .deb packages, you can sign with dpkg-sig:
# Sign the package
dpkg-sig --sign builder YourApp-1.0.0.deb

# Verify the signature
dpkg-sig --verify YourApp-1.0.0.deb
Linux signing is optional but recommended for enterprise distributions and repositories.

Security Best Practices

  • Never commit signing credentials to version control
  • Use environment variables or secure secret management
  • Rotate app-specific passwords regularly
  • Use hardware security modules (HSMs) for production
NativePHP automatically removes signing credentials from your bundled .env file:
// config/nativephp.php:61
'cleanup_env_keys' => [
    'NATIVEPHP_APPLE_ID',
    'NATIVEPHP_APPLE_ID_PASS',
    'NATIVEPHP_APPLE_TEAM_ID',
    'NATIVEPHP_AZURE_PUBLISHER_NAME',
    'NATIVEPHP_AZURE_ENDPOINT',
    'NATIVEPHP_AZURE_CERTIFICATE_PROFILE_NAME',
    'NATIVEPHP_AZURE_CODE_SIGNING_ACCOUNT_NAME',
],
  • Use different certificates for development and production
  • Don’t sign development builds
  • Sign only final release builds
After signing, verify your applications:macOS:
codesign --verify --verbose YourApp.app
spctl -a -v YourApp.app
Windows:
Get-AuthenticodeSignature YourApp.exe

CI/CD Integration

For automated builds, store signing credentials in your CI/CD secrets:

GitHub Actions Example

name: Build and Sign

on:
  release:
    types: [created]

jobs:
  build:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build and Sign
        env:
          NATIVEPHP_APPLE_ID: ${{ secrets.APPLE_ID }}
          NATIVEPHP_APPLE_ID_PASS: ${{ secrets.APPLE_ID_PASS }}
          NATIVEPHP_APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        run: |
          npm run build:mac
Never log or print signing credentials in CI/CD output. They should only be accessed through environment variables.

Troubleshooting

macOS Notarization Fails

Issue: Notarization returns errors from Apple. Solutions:
  • Verify your Apple ID and app-specific password are correct
  • Ensure your Team ID matches your Developer account
  • Check that your app bundle ID matches NATIVEPHP_APP_ID
  • Review Apple’s notarization logs for specific issues

Windows SmartScreen Warnings

Issue: Users see “Windows protected your PC” warnings. Solutions:
  • Verify your certificate is properly installed
  • Check that the certificate is from a trusted CA
  • Build reputation over time with consistent signing
  • Consider an EV certificate for instant reputation

Missing Environment Variables

Issue: Notarization is skipped.
// resources/electron/build/notarize.js:12
if (!('NATIVEPHP_APPLE_ID' in process.env && 
      'NATIVEPHP_APPLE_ID_PASS' in process.env && 
      'NATIVEPHP_APPLE_TEAM_ID' in process.env)) {
    console.warn('skipping notarizing, environment variables must be set.');
    return;
}
Solution: Ensure all required environment variables are set in your .env file.

Next Steps

Auto-Updates

Configure automatic updates for your signed applications

Build Configuration

Learn about build configuration options

Build docs developers (and LLMs) love