code signing - nself-org/nchat GitHub Wiki
Complete guide for code signing nchat desktop applications on all platforms.
- Why Code Signing
- macOS Code Signing
- Windows Code Signing
- Linux Code Signing
- Configuration
- Troubleshooting
Code signing is essential for desktop application distribution:
- Required for distribution outside Mac App Store
- Gatekeeper will block unsigned apps
- Users see scary warnings
- Notarization requires valid signing
- Recommended for professional distribution
- SmartScreen flags unsigned apps
- Users see warnings: "Unknown publisher"
- Enterprise deployment requires signing
- Optional but recommended
- Builds trust with users
- Enables repository distribution
-
Apple Developer Account ($99/year)
- Sign up at https://developer.apple.com
- Join Apple Developer Program
-
Xcode (macOS only)
xcode-select --install
- Open Xcode
- Go to Xcode → Preferences → Accounts
- Click + to add Apple ID
- Select your account → Manage Certificates
- Click + → Create:
- Developer ID Application (for apps)
- Developer ID Installer (for pkg installers)
- Go to https://developer.apple.com/account/resources/certificates
- Click + to create certificate
- Choose Developer ID Application
- Follow instructions to create CSR:
# On macOS, open Keychain Access # Certificate Assistant → Request a Certificate from a Certificate Authority # Save to disk
- Upload CSR, download certificate
- Double-click to install in Keychain
# List all code signing identities
security find-identity -v -p codesigning
# Output should show:
# 1) ABC123... "Developer ID Application: Your Name (TEAM123)"
# 2) DEF456... "Developer ID Installer: Your Name (TEAM123)"# From developer.apple.com → Membership
# Or from certificate:
security find-identity -v -p codesigning | grep "Developer ID"
# Team ID is in parentheses: (TEAM123)For notarization:
- Go to https://appleid.apple.com
- Sign in with Apple ID
- Security → App-Specific Passwords
- Click + to generate
- Name: "nchat Desktop Notarization"
- Copy password (format: xxxx-xxxx-xxxx-xxxx)
# In ~/.zshrc or ~/.bashrc
export APPLE_ID="[email protected]"
export APPLE_ID_PASSWORD="xxxx-xxxx-xxxx-xxxx" # App-specific password
export APPLE_TEAM_ID="TEAM123456"
export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAM123456)"
# Reload shell
source ~/.zshrcOr for CI/CD, use GitHub Secrets:
# .github/workflows/build-electron.yml
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}Entitlements are already configured in:
platforms/electron/resources/entitlements.mac.plist-
platforms/tauri/src-tauri/Info.plist(auto-generated)
To customize, edit the plist file to add/remove permissions.
# Build will automatically sign and notarize
./scripts/deploy-desktop-electron.sh --platform mac --env prod
# Or manually:
cd platforms/electron
npm run dist:mac
# Check notarization status
xcrun notarytool history --apple-id "$APPLE_ID" --password "$APPLE_ID_PASSWORD"# Build will automatically sign
./scripts/deploy-desktop-tauri.sh --platform mac --env prod
# Manually notarize (if needed)
xcrun notarytool submit \
target/release/bundle/dmg/nchat.dmg \
--apple-id "$APPLE_ID" \
--password "$APPLE_ID_PASSWORD" \
--team-id "$APPLE_TEAM_ID" \
--wait
# Staple ticket
xcrun stapler staple target/release/bundle/dmg/nchat.dmg# Verify app signature
codesign --verify --deep --strict /Applications/nchat.app
# Display signature info
codesign -dv --verbose=4 /Applications/nchat.app
# Check notarization
spctl -a -vvv -t install /Applications/nchat.app
# Should output: "accepted"-
Code Signing Certificate
- Purchase from certificate authority (CA)
- Recommended: DigiCert, Sectigo, SSL.com
- Choose "Code Signing Certificate"
- ~$100-500/year
-
Certificate Types
- Standard Code Signing: Basic signing
- EV (Extended Validation): Bypasses SmartScreen immediately
- OV (Organization Validation): Business verification required
DigiCert (Premium)
- https://www.digicert.com/signing/code-signing-certificates
- EV available (best for instant SmartScreen bypass)
- ~$474/year (Standard), ~$599/year (EV)
Sectigo (Mid-tier)
- https://sectigo.com/ssl-certificates-tls/code-signing
- Good balance of price and reputation
- ~$179/year (Standard)
SSL.com (Budget)
- https://www.ssl.com/code-signing/
- Affordable option
- ~$99/year (Standard)
- Choose certificate type (Standard or EV)
- Complete organization validation
- Verify domain/business (can take 1-3 days)
- Download certificate + private key
You need a PFX/P12 file with private key:
# Combine into PFX
openssl pkcs12 -export \
-out certificate.pfx \
-inkey private.key \
-in certificate.cer \
-password pass:YourStrongPassword- Open certmgr.msc
- Find your certificate under Personal → Certificates
- Right-click → All Tasks → Export
- Choose Yes, export the private key
- Select Personal Information Exchange (PFX)
- Set strong password
- Save as
certificate.pfx
# Linux/macOS
export WIN_CSC_LINK="/path/to/certificate.pfx"
export WIN_CSC_KEY_PASSWORD="your-certificate-password"
# Windows (PowerShell)
$env:WIN_CSC_LINK="C:\path\to\certificate.pfx"
$env:WIN_CSC_KEY_PASSWORD="your-certificate-password"
# For Tauri
export WINDOWS_CERTIFICATE="/path/to/certificate.pfx"
export WINDOWS_CERTIFICATE_PASSWORD="your-certificate-password"For CI/CD:
# .github/workflows/build-electron.yml
env:
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }} # Base64 encoded cert
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}# Encode certificate to base64
base64 -i certificate.pfx -o certificate.txt
# In GitHub Secrets, paste the base64 content
# The build will decode it automatically# Build will automatically sign
./scripts/deploy-desktop-electron.sh --platform win --env prod
# Or manually
cd platforms/electron
npm run dist:win# Build will automatically sign
./scripts/deploy-desktop-tauri.sh --platform win --env prodOn Windows:
# Using signtool (Windows SDK)
signtool verify /pa /v nchat-setup.exe
# Or right-click .exe → Properties → Digital Signatures
# Should show your certificate detailsEven with valid signature, new apps show SmartScreen warnings.
Solutions:
-
Get EV Certificate ($599/year)
- Bypasses SmartScreen immediately
- Requires hardware token
-
Build Reputation (Free, slow)
- Get downloads from users
- Takes weeks to months
- No guarantee
-
Submit to Microsoft (Free)
- Upload to Microsoft for analysis
- https://www.microsoft.com/en-us/wdsi/filesubmission
- Can speed up reputation
Linux doesn't require code signing, but you can sign packages for trust.
# Generate key
gpg --full-generate-key
# Choose:
# - Key type: RSA and RSA
# - Key size: 4096
# - Expiration: 2 years
# - Name: Your Name
# - Email: [email protected]
# List keys
gpg --list-keys# Export public key
gpg --export --armor [email protected] > public-key.asc
# Upload to key server
gpg --send-keys KEY_ID --keyserver keyserver.ubuntu.com# Sign .deb
dpkg-sig --sign builder nchat_1.0.0_amd64.deb
# Sign .rpm
rpm --addsign nchat-1.0.0-1.x86_64.rpm
# Sign .AppImage (detached signature)
gpg --detach-sign --armor nchat.AppImage
# Creates nchat.AppImage.asc# Verify .deb
dpkg-sig --verify nchat_1.0.0_amd64.deb
# Verify .rpm
rpm --checksig nchat-1.0.0-1.x86_64.rpm
# Verify .AppImage
gpg --verify nchat.AppImage.asc nchat.AppImageFor hosting your own APT/YUM repository:
# Create GPG key for repository
gpg --gen-key
# Sign repository metadata
# APT
gpg --clearsign -o InRelease Release
gpg -abs -o Release.gpg Release
# YUM
gpg --detach-sign --armor repodata/repomd.xml# ============================================
# macOS Code Signing
# ============================================
APPLE_ID="[email protected]"
APPLE_ID_PASSWORD="xxxx-xxxx-xxxx-xxxx" # App-specific password
APPLE_TEAM_ID="TEAM123456"
APPLE_SIGNING_IDENTITY="Developer ID Application: Name (TEAM)"
# For Tauri
APPLE_SIGNING_IDENTITY="Developer ID Application: Name (TEAM)"
APPLE_ID="[email protected]"
APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx"
# ============================================
# Windows Code Signing
# ============================================
WIN_CSC_LINK="/path/to/certificate.pfx"
WIN_CSC_KEY_PASSWORD="certificate-password"
# For Tauri
WINDOWS_CERTIFICATE="/path/to/certificate.pfx"
WINDOWS_CERTIFICATE_PASSWORD="certificate-password"
# ============================================
# Linux GPG Signing
# ============================================
GPG_KEY_ID="your-gpg-key-id"
GPG_PASSPHRASE="gpg-key-passphrase"
# ============================================
# Common
# ============================================
# Skip signing (for testing)
CSC_IDENTITY_AUTO_DISCOVERY=false # Electron
TAURI_SKIP_SIGNING=1 # TauriDO:
- Store certificates in secure location
- Use environment variables for passwords
- Use CI/CD secrets for automation
- Rotate passwords regularly
- Use hardware tokens for EV certificates
DON'T:
- Commit certificates to git
- Share private keys
- Use weak passwords
- Store passwords in plain text
- Reuse passwords across services
name: Build and Sign
on:
push:
tags:
- 'v*'
jobs:
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Import Code Signing Certificate
env:
CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
# Create keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
# Import certificate
echo "$CERTIFICATE_BASE64" | base64 --decode > certificate.p12
security import certificate.p12 -k build.keychain \
-P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" build.keychain
- name: Build and Sign
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
./scripts/deploy-desktop-electron.sh --platform mac
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Build and Sign
env:
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
run: |
.\scripts\deploy-desktop-electron.sh --platform winCause: Gatekeeper quarantine attribute
Solution:
# Remove quarantine
xattr -cr /Applications/nchat.app
# Or users: Right-click → Open (first time only)Cause: App not notarized or signature invalid
Solution:
# Check signature
codesign --verify --deep --strict /Applications/nchat.app
# Check notarization
spctl -a -vvv -t install /Applications/nchat.app
# Re-notarize if needed
xcrun notarytool submit nchat.dmg \
--apple-id "$APPLE_ID" \
--password "$APPLE_ID_PASSWORD" \
--waitCause: New certificate or low download count
Solutions:
- Get EV certificate ($599/year) - instant bypass
- Submit to Microsoft for analysis
- Wait for reputation to build (weeks/months)
Cause: Certificate not from trusted CA
Solution:
- Purchase from major CA (DigiCert, Sectigo, SSL.com)
- Ensure certificate chain is complete
- Check certificate expiration date
Cause: Certificate not in keychain or environment variable not set
Solution:
# macOS: Check keychain
security find-identity -v -p codesigning
# Ensure APPLE_SIGNING_IDENTITY is set
echo $APPLE_SIGNING_IDENTITY
# Or disable signing for testing
export CSC_IDENTITY_AUTO_DISCOVERY=falseCause: Certificate path incorrect or password wrong
Solution:
# Verify certificate exists
ls -la /path/to/certificate.pfx
# Check environment variables
echo $WINDOWS_CERTIFICATE
echo $WINDOWS_CERTIFICATE_PASSWORD
# Try manual signing first
# Windows:
signtool sign /f certificate.pfx /p password /tr http://timestamp.digicert.com app.exe
# macOS:
codesign --force --sign "Developer ID" --timestamp app.appFor code signing issues:
- Check troubleshooting section above
- Review platform-specific documentation
- Contact your certificate provider
- Open issue: https://github.com/nself/nself-chat/issues