DevOps

How to Sign Windows Applications with DigiCert Using GitHub Actions

Secure your Windows application releases by automating code signing with DigiCert and GitHub Actions. Learn how we integrated signing into our CI/CD pipeline for EXE and MSI builds.

PK
Punit Kumar
Senior DevOps Engineer
10 min read
#digicert#github-actions#code-signing#tauri#windows

How to Sign Windows Applications with DigiCert Using GitHub Actions

Shipping a desktop application without code signing can cause Windows SmartScreen warnings and erode user trust. In this blog post, I’ll walk you through how we securely sign Windows EXE and MSI installers using DigiCert’s Secure Software Manager (SSM)—fully automated via GitHub Actions.

This setup is battle-tested in our production CI pipeline at Sparrow, where we build and sign cross-platform Tauri apps.

🧠 Why Code Signing Matters

  • ✅ Verifies publisher identity
  • 🔐 Ensures file integrity
  • 🚫 Avoids SmartScreen "unrecognized app" warnings
  • 📦 Enables distribution in corporate environments

🔧 Tools & Services Used

  • DigiCert Secure Software Manager (SSM)
  • GitHub Actions
  • Tauri (Rust + WebView for Desktop Apps)
  • AWS S3 for artifact storage
  • Windows SDK + signtool.exe

📦 Workflow Breakdown

We sign the following Windows bundles:

  • .msi (MSI Installer via Tauri)
  • .exe (NSIS-based installer)

Here's the end-to-end strategy:

1. Build the App (Tauri + Node.js)

- name: Build Tauri App
  run: |
    yarn install
    yarn desktop-build

This step outputs both MSI and EXE builds in their respective bundle directories.

2. Configure DigiCert Secure Software Manager

We store our .p12 certificate as a base64 secret in GitHub (SM_CLIENT_CERT_FILE_B64) and decode it in the pipeline:

- name: Decode DigiCert Certificate
  run: |
    $bytes = [Convert]::FromBase64String("${{ secrets.SM_CLIENT_CERT_FILE_B64 }}")
    [IO.File]::WriteAllBytes("D:\\Certificate_pkcs12.p12", $bytes)

Then we export DigiCert environment variables:

- name: Set DigiCert Variables
  run: |
    echo "SM_HOST=${{ secrets.SM_HOST }}" >> $GITHUB_ENV
    echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> $GITHUB_ENV
    echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> $GITHUB_ENV
    echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> $GITHUB_ENV

3. Install and Use DigiCert Signing Tools

- name: Install DigiCert CLI
  uses: digicert/ssm-code-signing@v1.0.0

Extract the first available KeyPair alias:

$alias = (smctl keypair ls | Select-Object -Skip 2 | Select-Object -First 1) -split '\s{2,}' | Select-Object -Index 2

Sync the certificate into the Windows store:

smctl windows certsync --keypair-alias="$alias" --store=system

4. Sign MSI and EXE Files with Signtool

& "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe" sign `
  /sm /s My `
  /tr http://timestamp.digicert.com `
  /td sha256 `
  /fd sha256 `
  /sha1 $env:SM_CODE_SIGNING_CERT_SHA1_HASH `
  "path\to\your\installer.msi"

Repeat for .exe files.

5. Verify Signatures

$sig = Get-AuthenticodeSignature "installer.msi"
if ($sig.Status -ne 'Valid') {
  throw "Signature is invalid!"
}

6. Upload to AWS S3

We use this step to host signed builds:

- name: Upload to S3
  run: |
    aws s3 cp path\to\installer.msi s3://your-bucket/

7. Notify Teams (Optional)

You can even notify your team of new builds via MS Teams webhook:

Invoke-RestMethod -Uri ${{ secrets.TEAMS_INCOMING_WEBHOOK_URL }} -Method POST -Body $jsonBody -ContentType 'application/json'

Conclusion

By integrating DigiCert signing into our GitHub CI pipeline, we now ship secure, signed Windows apps with every release. This has helped us avoid trust warnings, meet enterprise security requirements, and streamline our release process.

Want help setting this up for your app? Schedule a call and I’d be happy to guide you through it.

Found this article helpful? Share it with your team or connect with me for more insights.

Read More Articles